WindowManagerService添加窗口流程简析---应用窗口的创建

Android 11----Context.getWindowManager().getDefaultDisplay().getSize()详解 (上) 基本上做过Android的同学都知道,context无处不在,Activity,Service,Application都是Context,view也有context,我们可以通过context 阅读详情

    Android系统中的窗体是屏幕上的一块用于绘制各种UI元素并能够响应应用户输入的一个矩形区域。从原理上来讲,窗体的概念是独自占有一个Surface实例的显示区域。比如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗体。 


 一、Android窗口类型

  在WindowManager类的内部类LayoutParams中定义了三种窗口类型(Application windows,Sub-windows和System windows),所以Android系统中窗口分为三大类型:

  • 应用窗口:该窗口对应一个Activity,因此,要创建应用窗口就必须在Activity中完成了。
  • 子窗口:必须依附在某个父窗口之上。
  • 系统窗口:由系统进程创建,不依赖于任何应用或者不依附在任何父窗口之上。

    

二、Activity应用窗口的创建

    

    1、Activity创建Window并与WindowManager进行绑定

    在Activity启动过程中会调用ActivityThread的performLaunchActivity方法,performLaunchActivity方法中又会调用Activity的attach方法。下面从Activity的attach方法开始入手,如下所示:

final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident,
           Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, 
            IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) {

        .......
       //创建PhoneWindow,Window的实现类 ,一个Activity对应着一个Window也就是应用窗口。
        mWindow = new PhoneWindow(this, window, activityConfigCallback); 
        ......
       //调用PhoneWindow的setWindowManager方法,
       mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
              mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow()); //设置父容器
        }
        mWindowManager = mWindow.getWindowManager(); //WindowManagerImpl对象
        ......
   }

    调用了PhoneWindow的setWindowManager方法,这个方法的具体的实现在PhoneWindow的父类Window中。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {

            //mContext.getSystemService(Context.WINDOW_SERVICE);返回的是WindowManagerImpl对象
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);

         }

        //创建WindowManagerImpl并将window和WindowManagerImpl关联
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

    经过上面的setWindowManager方法,Window就和WindowManagerImpl进行了关联,这样WindowManagerImpl就持有了Window的引用,就可以对Window进行操作。

    2、Activity给Window添加View

    每个Activity都会调用setContextView方法来加载布局视图,而这其实就是视图View添加到Activity窗口上的一个过程。加载布局视图代码如下Activity#setContentView:

public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID); //getWindow()返回Activity的成员变量mWindow即PhoneWindow
        initWindowDecorActionBar(); //初始化ActionBarView
    }

2.1 查看PhoneWindow的SetContentView方法

public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();  //1----创建顶层视图DecorView,整个应用窗口的根View
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);  //加载xml布局视图内容到视图容器mContentParent中
        }
    }

2.1.1 PhoneWindow的installDecor()的具体内容:

 private void installDecor() {
        if (mDecor == null) {  
            mDecor = generateDecor(-1); //1---生成DecorView对象,DecorView继承自FrameLayout
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }

        //2---mContentParent是id为android.R.id.content的FrameLayout,setContentView就是将布局设置在这里
        if (mContentParent == null) {  
            mContentParent = generateLayout(mDecor);
            .......
         } else {  //设置title
               mTitleView = findViewById(R.id.title);
                if (mTitleView != null) {
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        final View titleContainer = findViewById(R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        mContentParent.setForeground(null);
                   } else {
                        mTitleView.setText(mTitle);
                    }
                }
           }
}

    注释1中generateDecor方法通过new DecorView创建对象,在DecorView的构造方法中,传入了PhoneWindow对象,即将PhoneWindow和DecorView进行了关联。下面看下注释2中generateLayout主要内容:

2.1.2 PhoneWindow的generateLayout(mDecor)

protected ViewGroup generateLayout(DecorView decor) {
  /*应用当前主题数据,加载的是 
    *TypedArray  mWindowStyle = mContext.obtainStyledAttributes(com.android.internal.R.styleable.Window);
    */
  TypedArray a = getWindowStyle();
  ......
  //加载DecorView布局,这些xml文件位于frameworks/base/core/res/res/layout下
  int layoutResource;
  int features = getLocalFeatures();
  if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
    if (mIsFloating) {
      TypedValue res = new TypedValue();
      getContext().getTheme().resolveAttribute(com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
      layoutResource = res.resourceId;
    } else {
      layoutResource = com.android.internal.R.layout.screen_title_icons;
  }
  removeFeature(FEATURE_ACTION_BAR);
  } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
  layoutResource = com.android.internal.R.layout.screen_progress;
  ......
  
  mDecor.startChanging();
  //inflate View,并添加到decorView中
  View in = mLayoutInflater.inflate(layoutResource, null);
  decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
  //id="@android:id/content"的View赋值给mContentParent
  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  if (contentParent == null) {
    throw new RuntimeException("Window couldn't find content container view");
  }

2.2 initWindowDecorActionBar(); 

private void initWindowDecorActionBar() {
       Window window = getWindow();
        window.getDecorView();  //若DecorView为null,则创建DecorView
        if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
            return;
        }

        mActionBar = new WindowDecorActionBar(this); //创建ActionBar
        mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);

        mWindow.setDefaultIcon(mActivityInfo.getIconResource());
        mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
    }

三、Activity添加窗口Window的过程

    在setContentView中只是将需要添加的View的结构添加到DecorView中,此时DecorView并无显示内容,View还未进行绘制,所以在onCreate方法中是无法获取View的宽高等信息。

    在Activity创建完成之后,Ams利用Bindler进程间的通信机制通知客户端调用ActivityThread类中的handleResumeActivity方法来启动Activity。代码如下:

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        ........
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;
            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) { //参数设置
                r.window = r.activity.getWindow();//PhoneWindow
                View decor = r.window.getDecorView(); //窗口顶层视图DecorView
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager(); //Activity窗口的窗口管理器对象WindowManager
                WindowManager.LayoutParams l = r.window.getAttributes();//窗口参数

                a.mDecor = decor;

                //窗口的类型为基础应用窗口
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                //窗口的软输入法模式
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    //标记客户端Activity添加窗口成功
                    a.mWindowAdded = true;
                    //添加窗口
                    wm.addView(decor, l);
                }
    ........
        }
    }

    在这个过程中最主要的操作就是调用了WindowManager.addView方法,而WindowManager是个接口类,继承自ViewManager。ViewManager源码如下:

public interface ViewManager{

    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

    WindowManager继承自ViewManager,WindowManager窗口管理其实就是对窗口的视图View进行管理。WindowManager 源码如下:

public interface WindowManager extends ViewManager {
    ........
     public Display getDefaultDisplay();
     public void removeViewImmediate(View view);
     public static class LayoutParams extends ViewGroup.LayoutParams
            implements Parcelable {
    ........
    }
}

    WindowManager 的实现类WindowManagerImpl,其源码如下:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Display display) {
        this(display, null);
    }

    private WindowManagerImpl(Display display, Window parentWindow) {
        mDisplay = display;
        mParentWindow = parentWindow;
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mDisplay, parentWindow);
    }

   @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);  //如果params.token为null,则token设置为系统默认的
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }
   @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

    @Override
    public Display getDefaultDisplay() {
        return mDisplay;
    }
}

    从源码中可以看出,WindowManagerImpl中并没有做实质性的操作,而是将具体操作全部交给了WindowManagerGlobal去处理,这里到了设计模式中的桥接模式,可以看出WindowManagerGlobal是一个单例,说明在一个进程中只有一个WindowManagerGlobal实例。下面查看WindowManagerGlobal的addView()方法。

public final class WindowManagerGlobal {
    private final ArrayList<View> mViews = new ArrayList<View>(); //保存当前应用所有窗口的DecorView
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); //保存当前应用所有ViewRootImpl
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>(); //保存当前应用所有窗口的布局参数
    ........
    //单例模式构造方法
    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

//添加view
 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
     ......
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
           parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // 如果没有父级,那么此视图的硬件加速从应用程序的硬件加速设置中设置。
           final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
           }
       }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // 监听系统属性变更
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) { //如果该view处于正在删除中,则直接删除
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else { //不能重复添加同一个view对象
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // 如果是子Window,则根据token获取对应的父窗口Window
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            root = new ViewRootImpl(view.getContext(), display); //每一个窗口对应一个ViewRootImpl

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);  //ViewRootImpl 完成绘制view操作
           } catch (RuntimeException e) {
               // 出现异常,则清除该View
               if (index >= 0) {
                   removeViewLocked(index, true);
                }
                throw e;
           }
        }
   }
//主要更新当前窗口的参数LayoutParams
 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false); //绘制操作由ViewRootImpl完成
        }
    }
    //从三个数组里面分别移除DecorView对象,ViewRootIpl对象,WindowManager.LayoutParams对象
    public void removeView(View view, boolean immediate) { 
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }
        }
    }
    在WindowManagerGlobal的方法中最终调用ViewRootImpl的setView来完成View的添加和绘制,ViewRootImpl是一个视图层次结构的顶部,它实现了View与WindowManager之间所需要的协议,作为WindowManagerGlobal中大部分的内部实现。这个好理解,在WindowManagerGlobal中实现方法中,都可以见到ViewRootImpl,也就说WindowManagerGlobal方法最后还是调用到了ViewRootImpl。addView,removeView,update调用顺序

WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl 

    下面查看ViewRootImpl的setView的主要内容:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                ...
                // 在添加到WindowManager中之前,先进行绘制view操作,绘制完毕后才能接受事件
                requestLayout();
                ......
                try {
                ......
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);  
                } 
    }

requestlayout()进行布局的绘制,绘制结束后调用mWindowSession.addToDisplay添加和显示view。mWindowSession是Session对象,继承自IWindowSession.Stub(是一个Binder对象),也就是说这其实是一次IPC过程,远程调用了Session中的addToDisPlay方法。下面查看下Session中的addToDisPlay方法:

@Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

    其中mService就是WindowManagerService,也就是说Window的添加请求,最终是通过WindowManagerService来添加的。下面查看下WindowManagerService的具体方法。

四、WindowManagerService的addWindow方法
public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp); //检查权限,系统窗口需要对应的系统权限
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }

        boolean reportNewConfig = false;
        WindowState parentWindow = null;
        long origId;
        final int callingUid = Binder.getCallingUid();
        final int type = attrs.type; //窗体的类型

        synchronized(mWindowMap) {
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }
	    //通过displayId參数指定加入到哪一个DisplayContent,默认是DEFAULT_DISPLAY手机屏幕
            final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
            if (displayContent == null) {
                Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
                        + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            if (!displayContent.hasAccess(session.mUid)
                    && !mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) {
                Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
                        + "does not have access: " + displayId + ".  Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            if (mWindowMap.containsKey(client.asBinder())) { //判断是否重复添加Window
                Slog.w(TAG_WM, "Window " + client + " is already added");
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }

	    //子窗口必须依附于父窗口,对于子窗体来说,attrs.token表示了父窗体
            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                parentWindow = windowForClientLocked(null, attrs.token, false);
                if (parentWindow == null) {
                    Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
                          + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) { //窗体嵌套只能是两层,子窗口是不能在添加窗口的
                    Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }

            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
                Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display.  Aborting.");
                return WindowManagerGlobal.ADD_PERMISSION_DENIED;
            }

            AppWindowToken atoken = null; //应用窗口token
            final boolean hasParent = parentWindow != null;
            // 子窗口使用父窗口策略
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            // If this is a child window, we want to apply the same type checking rules as the
            // parent window type.
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;

            boolean addToastWindowRequiresToken = false;

            if (token == null) { //token异常判断
               ......
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow); //创建系统WindowToken
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { //应用窗口类型
                atoken = token.asAppWindowToken();
                ......
            } else if (rootType == TYPE_INPUT_METHOD) {
                if (token.windowType != TYPE_INPUT_METHOD) {
                    Slog.w(TAG_WM, "Attempted to add input method window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_VOICE_INTERACTION) {
                if (token.windowType != TYPE_VOICE_INTERACTION) {
                    Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_WALLPAPER) {
                if (token.windowType != TYPE_WALLPAPER) {
                    Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_DREAM) {
                if (token.windowType != TYPE_DREAM) {
                    Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
                            + attrs.token + ".  Aborting.");
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
                    Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_TOAST) {
                // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
                addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
                        callingUid, parentWindow);
                if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
                    Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_QS_DIALOG) {
                if (token.windowType != TYPE_QS_DIALOG) {
                    Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
                            + attrs.token + ".  Aborting.");
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (token.asAppWindowToken() != null) {
                Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
                // It is not valid to use an app token with other system types; we will
                // instead make a new token for it (as if null had been passed in for the token).
                attrs.token = null;
                token = new WindowToken(this, client.asBinder(), type, false, displayContent,
                        session.mCanAddInternalSystemWindow);//创建系统窗口类型的token
            }
	    //WMS为要加入的窗体创建了一个WindowState对象,这个对象维护了一个窗体的全部状态信息
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
            if (win.mDeathRecipient == null) {
                // Client has apparently died, so there is no reason to
                // continue.
                Slog.w(TAG_WM, "Adding window client " + client.asBinder()
                        + " that is dead, aborting.");
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            if (win.getDisplayContent() == null) {
                Slog.w(TAG_WM, "Adding window to Display that has been removed.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

	    //WindowManagerPolicy(实现类PhoneWindowManager),这个函数的调用会调整LayoutParams的一些成员的取值
            mPolicy.adjustWindowParamsLw(win.mAttrs); 
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }

            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }
	    ......

            win.attach();
	    // 然后将WindowState对象加入到mWindowMap中
            mWindowMap.put(client.asBinder(), win);
            if (win.mAppOp != AppOpsManager.OP_NONE) {
                int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
                        win.getOwningPackage());
                if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
                        (startOpResult != AppOpsManager.MODE_DEFAULT)) {
                    win.setAppOpVisibilityLw(false);
                }
            }

            final AppWindowToken aToken = token.asAppWindowToken(); //应用窗口windowToken
            if (type == TYPE_APPLICATION_STARTING && aToken != null) {
                aToken.startingWindow = win;
                if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
                        + " startingWindow=" + win);
            }

            boolean imMayMove = true;

            win.mToken.addWindow(win);
            if (type == TYPE_INPUT_METHOD) {
                win.mGivenInsetsPending = true;
                setInputMethodWindowLocked(win);
                imMayMove = false;
            } else if (type == TYPE_INPUT_METHOD_DIALOG) {
                displayContent.computeImeTarget(true /* updateImeTarget */);
                imMayMove = false;
            } else {
                if (type == TYPE_WALLPAPER) {
                    displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
                    // If there is currently a wallpaper being shown, and
                    // the base layer of the new window is below the current
                    // layer of the target window, then adjust the wallpaper.
                    // This is to avoid a new window being placed between the
                    // wallpaper and its target.
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
            }

            // If the window is being added to a stack that's currently adjusted for IME,
            // make sure to apply the same adjust to this new window.
            win.applyAdjustForImeIfNeeded();

            if (type == TYPE_DOCK_DIVIDER) {
                mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
            }

            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;
            // Check if we need to prepare a transition for replacing window first.
            if (atoken != null && atoken.isVisible()
                    && !prepareWindowReplacementTransition(atoken)) {
                // If not, check if need to set up a dummy transition during display freeze
                // so that the unfreeze wait for the apps to draw. This might be needed if
                // the app is relaunching.
                prepareNoneTransitionForRelaunching(atoken);
            }

            if (displayContent.isDefaultDisplay) {
                final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                final Rect taskBounds;
                if (atoken != null && atoken.getTask() != null) {
                    taskBounds = mTmpRect;
                    atoken.getTask().getBounds(mTmpRect);
                } else {
                    taskBounds = null;
                }
                if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayInfo.rotation,
                        displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
                        outStableInsets, outOutsets)) {
                    res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
                }
            } else {
                outContentInsets.setEmpty();
                outStableInsets.setEmpty();
            }

            if (mInTouchMode) {
                res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
            }
            if (win.mAppToken == null || !win.mAppToken.isClientHidden()) { //设置应用窗体可见
                res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
            }

            mInputMonitor.setUpdateInputWindowsNeededLw(); //以下和输入法窗体有关

            boolean focusChanged = false;
            if (win.canReceiveKeys()) {
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }

            if (imMayMove) {
                displayContent.computeImeTarget(true /* updateImeTarget */);
            }

            // Don't do layout here, the window must call
            // relayout to be displayed, so we'll do it there.
            displayContent.assignWindowLayers(false /* setLayoutNeeded */);

            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);

            if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
                    + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));

            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false, displayId)) {
                reportNewConfig = true;
            }
        }

        if (reportNewConfig) {
            sendNewConfiguration(displayId);
        }

        Binder.restoreCallingIdentity(origId);

        return res;
    }

总结:以上是添加应用窗口的简析,从代码中可以看到还是有点复杂的,这里只是简单介绍添加流程,希望有所帮助。








WindowManagerService理解与深入(Android Q) 一、WindowManagerService的基础概念 1、什么是WindowManagerServie? 首先,什么是Window? 从原理上来讲,Window是独自占有一个Surface实例的显示区域,用于绘制各种UI元素并可以响应用户输入。 什么是WindowManagerService 每个窗口都有一块Surface,就需要一个角色来协调管理所有的Surface。WindowMan... 阅读详情

相关推荐

Android WindowManagerService机制分析:窗口的显示层级

        WindowManagerService(以下简称WMS)是Android Framework中一个重要的系统服务,用来管理系统中窗口(Window)的行为。Window是一个抽象的概念,它是一个矩形区域,用来绘制UI界面,响应用户的输入事件。Android系统的界面,从Framework层的角度来看,就是由一个一个窗口组合而成的。        在WMS中一个重要的功能就是对当前...

u012551754的专栏 1万+

文件管理搜不到Android 里的文件,Android:在原始文件夹中添加文件后窗口找不到内容容器视图...

正在获取窗口找不到内容容器视图。这个错误询问应用程序何时将检查权限Android:在原始文件夹中添加文件后窗口找不到内容容器视图我注意到,对于穿着应用程序,我正在将穿着apk复制到原始文件夹中。它有大约2.5 Mb。添加这个我得到这次崩溃java.lang.RuntimeException: Window couldn't find content container viewat com.and...

weixin_39930276的博客 947

Stata实战:用双向固定效应模型分析房价,从数据清洗到结果解读(附完整代码)

本文详细介绍了如何使用Stata实现房价数据的双向固定效应模型分析,从数据清洗到结果解读的全流程。通过具体代码示例,帮助读者掌握面板模型的应用技巧,特别适合经济学和金融学研究者进行房价影响因素分析。

weixin_31528001的博客 277

从源码探索Activity布局加载过程

众所周知,Activity是承载布局的重要组件,是通过setContentView()方法来设置xml布局文件的,那么我们来提出几个问题 1.布局文件到底是怎么加载到Activity并显示到屏幕的呢? 2.我们给activity设置了主题,它对应的样式代码是在哪设置的呢? 下面我们跟踪源码来探索一下。 首先,我们点开setContentView()方法 publi...

qq_27246079的博客 408

2-6-1-1 QNX编程入门之进程和线程(一)

文章围绕进程和线程展开介绍。先是将进程类比为房子,线程类比为居住者,阐述其基本概念,如进程占用内存,线程可共享内存。接着讲解同步相关内容,介绍了互斥,线程通过互斥锁来控制对特定资源的访问,像使用浴室一样。还提到优先级,线程按优先级和等待时长来决定获取资源顺序。此外,说明了信号量,以厨房为例,区分计数为 1 和大于 1 的信号情况,且指出信号量可当互斥锁用,但互斥锁是特殊用途信号量,二者存在区别,最后提及后续会介绍其他同步机制。

星原飞火的博客 1323

Excel-MCP实战指南:零代码实现大模型与Excel的智能交互

本文详细介绍了Excel-MCP工具如何实现大模型与Excel的无缝交互,无需编写代码即可完成数据读写、图表生成等复杂操作。通过实战案例展示了Excel-MCP在销售报表自动化、财务对账等企业级场景中的应用,大幅提升工作效率。文章还提供了安装指南、核心功能解析和常见问题解决方案。

weixin_29212429的博客 432

setContentView那些事

刨根问底setContentView在平时的android开发中,经常会使用到Activity#setContentView方法来设置我们自己的布局,那么setContentView中到底做了什么,我们的布局 是怎么加载并显示到手机屏幕上的,这就是今天要讨论的内容,看下Activity#setContentView方法public void setContentView(@LayoutRes in

mockingbirds的专栏 2733

Windows10 使用Apple Studio Display显示器的设置

AppleStudioDisplay显示器购买花了近1.5wRMB,由于为windows台式机,显卡为NVIDAGTX-1660,而AppleStudioDisplay只有雷电接口,尝试了很多种转接方式,均以失败告终,最终使用贝尔金VR线成功,,附图如下这边也测试过NVIDARTX-2060Super、NVIDAGTX-1650显卡,都是能点亮的,NVIDA1650存在关机重启后黑屏,不稳定现象,2060也存在关机重启后不亮现象,后面发现1660s很稳定。...

爱意随风起,人生不可弃。 1万+

Window/WindowManager 不可不知之事

前言 从Android app的视角看,Window是比较抽象的概念,它是View的承载者。而WindowManager顾名思义是Window的管理者,通过addView方法将View添加到Window里最终展示到屏幕上。 系列文章: Window/WindowManager 不可不知之事 Android Window 如何确定大小/onMeasure()多次执行原因 通过本篇文章,你将了解到: 1、Window/WindowManager 创建、属性及其使用 2、WindowManager.Layo

1938

大数据分析案例-基于随机森林算法构建二手房价格预测模型

通过应用随机森林算法,我们希望建立一个能够有效预测二手房价格的模型。这个模型可以基于各种因素,如区域位置、房屋属性、市场供需等,来预测未来某一时间点的房价。通过提高预测准确性,我们可以更好地把握房地产市场动态,为决策提供更有价值的参考。揭示影响二手房价格的因素及其影响方式:通过分析随机森林算法生成的模型,我们可以进一步了解哪些因素对二手房价格有重要影响,以及这些因素如何影响房价。这有助于我们深入理解房地产市场的运行规律,为制定更为有效的市场策略提供依据。

m0_64336780的博客 1万+

【毕设实战】飞行器目标识别系统:基于YOLOv5的智能检测全流程教程,助你毕设脱颖而出

亲爱的同学,毕设是不是让你既期待创新又担心技术门槛?如果你的毕设方向是计算机视觉、智能检测,那这个绝对能成为你的“毕设王牌”。从数据准备到界面开发,我会把整个项目拆成“傻瓜式”步骤,让你能一步步做出属于自己的毕设成果。

know_F0727的博客 104

WindowManagerService添加窗口流程(4)

添加窗口流程是WMS很重要的一个功能,如果你是一个app开发,基本上接触WMS最多的场景也就是这个流程,在添加悬浮窗的时候使用的addView就是这个流程,其实activity也是通过类似的流程去添加显示的窗口的,我们会通过介绍addView这个流程了解添加窗口这个过程。和之前介绍SurfaceFlinger一样,在继续了解WMS之前,我们先来介绍一下app和WMS的binder通信架构,以便我们后面更好的了解WMS的其他流程。

起司锅仔的博客 1567

电子图像增稳(EIS)原理详解——从抖动补偿到代码实现

电子图像增稳(EIS)是一种通过软件算法实现视频稳定的技术,广泛应用于GoPro、手机和无人机等设备。与光学防抖(OIS)和机械云台不同,EIS通过图像裁切和运动补偿来消除抖动,具有成本低、补偿范围大的优点,但会损失10-30%的画幅。其核心流程包括运动检测、抖动分离、运动补偿和图像裁切四个步骤。相比硬件方案,纯软件实现的EIS更适合消费级电子产品,已成为移动设备视频拍摄的重要技术。

电脑管家 409

Android窗口管理服务WindowManagerService对窗口的组织方式分析

我们知道,在Android系统中,Activity是以堆栈的形式组织在ActivityManagerService服务中的。与Activity类似,Android系统中的窗口也是以堆栈的形式组织在WindowManagerService服务中的,其中,Z轴位置较低的窗口位于Z轴位置较高的窗口的下面。在本文中,我们就详细分析WindowManagerService服务是如何以堆栈的形式来组织窗口的。

老罗的Android之旅 6万+

【Bug已解决】[Bug]: ValueError: No user query found in messages QWEN 3.5 27B VLLM 0.16.0 NIGHTLY 解决方案

用 vLLM 0.16.0 nightly 给发聊天请求时,服务端在处理消息时抛ValueError,请求失败。

博客 402

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Android窗口管理服务WindowManagerService的简要介绍和学习计划

在前一个系列文章中,我们从个体的角度来分析了Android应用程序窗口的实现框架。事实上,如果我们从整体的角度来看,Android应用程序窗口的实现要更复杂,因为它们的类型和作用不同,且会相互影响。在Android系统中,对系统中的所有窗口进行管理是窗口管理服务WindowManagerService的职责。在本文中,我们就将简要介绍WindowManagerService的职能以及制定学习计划。

老罗的Android之旅 8万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

WindowManagerService 简析

子窗口(1000~1999): 必须依附于一个父窗口。Dialog在Activity不可见的时候,要主动dismiss掉,否则会因为appToken为空crash。系统窗口(2000~2999): 拥有最高的Z-order,例如状态栏、导航栏、Toast、输入法窗口、系统音量条窗口、系统错误窗口等,可以覆盖在所有应用窗口之上。自由模式:类似于桌面操作系统,应用界面的窗口可以自由拖动和修改大小。分屏模式:将屏幕一分为二,同时显示两个应用的界面。画中画模式:窗口可以一直在最顶层显示。

Android 613

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

WMS 窗口添加流程

wms

优快云NETVIP的博客 1184

解析WindowManagerService

解析WindowManagerService WindowManagerService(以后简称WMS)它是 WindowManager 的管理者, WMS 无论对于应用开发还是 Framework 开发都是重要的知识点,究其原因是因为 WMS 有很多职责,每个职责都会涉及重要且复杂的系统。 WMS 的职责主要有以下几点。 1. 窗口管理:负责窗口的启动、添加和删除。另外窗口的大小和层级也是由WMS进行管理的。窗口管理的核心成员有DisplayContent、WindowT...

lixiong0713的博客 2万+

WindowManagerService详述

WindowManagerService,简称wms服务,见名知意首先是管理window窗口的,它在管理当前系统中所有的window窗口,由此可以引申出另一个重要作用,它主管分配设备产生的touch事件、keydown事件,因为wms能知道当前系统中哪个window最适合来处理某个事件(它在管理所有的window,可以遍历来挑选最优window),硬件产生的某个事件最终是要被软件应用所消费的,那些view、button的按键事件就是通过window再传递给View体系来响应按键的。与ams、ims类似都是由

dream_caoyun的博客 3582

Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析

在Android系统中,Activity窗口的大小是由WindowManagerService服务来计算的。WindowManagerService服务会根据屏幕及其装饰区的大小来决定Activity窗口的大小。一个Activity窗口只有知道自己的大小之后,才能对它里面的UI元素进行测量、布局以及绘制。本文将详细分析WindowManagerService服务计算Activity窗口大小的过程。

老罗的Android之旅 8万+

Android窗口管理服务WindowManagerService显示窗口动画的原理分析

在前一文中,我们分析了Activity组件的切换过程。从这个过程可以知道,所有参与切换操作的窗口都会被设置切换动画。事实上,一个窗口在打开(关闭)的过程中,除了可能会设置切换动画之外,它本身也可能会设置有进入(退出)动画。再进一步地,如果一个窗口是附加在另外一个窗口之上的,那么被附加窗口所设置的动画也会同时传递给该窗口。本文就详细分析WindowManagerService服务显示窗口动画的原理。

老罗的Android之旅 5万+

WindowManagerService之addWindow篇

addWindow

gary_qing的博客 959

Android应用程序窗口与WindowManagerService服务的连接过程,即WindowState的创建过程

Activity组件在其窗口对象和视图对象创建完成之后,就会请求与WindowManagerService建立一个连接,即请求WindowManagerService为其增加一个WindowState对象,用来描述它的窗口状态。在本文中,我们就详细分析Activity组件与WindowManagerService的连接过程。 我们从两方面来看Activity组件与WindowManagerService服务之间的连接。一方面是从Activity组件到WindowManagerService服务的连接,.

CallmeZhe的博客 1107

Android窗口管理框架终极指南:WindowManagerService窗口计算流程深度解析

Android窗口管理框架是Android系统中负责管理应用界面显示的核心组件,而WindowManagerService(WMS)则是其中的关键服务,它掌控着窗口的创建、布局、大小计算和显示顺序。本文将带你深入了解WindowManagerService的窗口计算流程,帮助你全面掌握Android窗口管理的核心原理。 ## 一、Android窗口管理框架概述 Android窗口管理框架采用分

gitblog_00097的博客 1113

Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析

在Android系统中,Activity组件在启动之后,并且在它的窗口显示出来之前,可以显示一个启动窗口。这个启动窗口可以看作是Activity组件的预览窗口,是由WindowManagerService服务统一管理的,即由WindowManagerService服务负责启动和结束。在本文中,我们就详细分析WindowManagerService服务启动和结束Activity组件的启动窗口的过程。

老罗的Android之旅 5万+
上一篇: Android屏幕适配
下一篇: Android 四大组件之间的通信
white_wt
博客等级 码龄9年 6粉丝 28原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值