前语

前段时间分析了 Window 的增加、更新和删去流程,也知晓了 Activity 、Dialog 和 Toast 中 Window 的创立进程,今天就接着上篇文章,看一下 WMS 的创立 以及WindowManager 增加 WIndow 后 WMS 是怎样进行操作的。上篇文章点这儿直达;

WindowManagerService 简称 WMS ,是体系的中心服务,首要分为四大部分,风别是 窗口办理窗口动画,输入体系中转站,Surface 办理

了解 WMS

WMS 的责任很多,首要的便是下面这几点:

  • 窗口办理:WMS是窗口的办理者,负责窗口的发动,增加和删去,别的窗口的巨细也时有 WMS 办理的,办理窗口的中心成员有DisplayContent,WindowTokenWindowState
  • 窗口动画:窗口间进行切换时,运用窗口动画能够更好看一些,窗口动画由 WMS 动画子体系来负责,动画的办理体系为 WindowAnimator
  • 输入体系的中转站:经过对窗口接触而发生的接触事情,InputManagerServer(IMS) 会对接触事情进行处理,他会寻觅一个最合适的窗口来处理接触反应信息,WMS 是窗口的办理者,因而天经地义的就成为了输入体系的中转站。
  • Surface 办理:窗口并不具有制作的功能,因而每个窗口都需求有一个块 Surface 来供自己制作,为每个窗口分配 Surface 是由 WMS 来完结的。

WMS 的责任能够总结为下图:

202210191446431.webp

WMS 的发动

WMS 是在 SystemServer 内部发动的

Android 体系在发动的时分,会发动两个重要的进程,一个是 Zygote 进程,两一个是由 Zygote 进程 fork 出来的 system_server 进程,SystemServer 会发动咱们在体系中所需求的一系列 Service。

//#SystemServer.java
private void startOtherServices() {
 	  //.....
  	//1
    WindowManagerService wm = null;
    InputManagerService inputManager = null;
		//2
    traceBeginAndSlog("StartInputManagerService");
    inputManager = new InputManagerService(context);
    traceEnd();
    traceBeginAndSlog("StartWindowManagerService");
    //WMS needs sensor service ready
    ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
    mSensorServiceStart = null;
    //3
    wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
            new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
  	// WMS 初创立完结后调用,后边会讲到
    wm.onInitReady();
    //4
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
    //5
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
            /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
    traceEnd();
		//...
    try {
        //6 
        wm.displayReady();
    } catch (Throwable e) {
        reportWtf("making display ready", e);
    }
    traceEnd();
    try {
        //7
        wm.systemReady();
    } catch (Throwable e) {
        reportWtf("making Window Manager Service ready", e);
    }
    traceEnd();
		///...
}

startOtherServer 用于发动其他服务,大概有70多个,上面列出了 WMS 和 IMS 的发动逻辑。

在注释1的当地声明晰 WMS 和 IMS, 这儿值列出了两个,其实十分多。

注释2处创立了 IMS ,

注释 3 调用了 WMS 的 main 办法,而且传入了 IMS,由于 WMS 是 IMS 的中转站。调查 WindowManagerService.main 办法能够知道他是运转在 SystemServer 的 run 办法中,换句话说便是运转在 system_server 线程中。

注释4和5处将 WMS 和 IMS 注册到 ServerManager 里边,这样客户端想要运用 WMS 就需求先去 ServiceManager 中查询信息,然后与 WMS 地点的进程树立通讯,这样客户端就能够运用 WMS 了。

注释 6 用来初始化显示信息,注释 7 处用来告诉 WMS 体系初始化作业现已完结,内部调用了 WindowManagerPolicy 的 systemReady 办法。

接着,咱们看看 WMS 的 maain 办法:

public static WindowManagerService main(final Context context, final InputManagerService im,
        final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
        ActivityTaskManagerService atm) {
    return main(context, im, showBootMsgs, onlyCore, policy, atm,
            SurfaceControl.Transaction::new, Surface::new, SurfaceControl.Builder::new);
}
public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,
            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
                        atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
        return sInstance;
    }

上面经过 DisplayThread 的 getHandler 办法获取到了 DisplayThread 的 Handler 实例。

DisplayThread 是一个单例的前台线程,用来处理需求低延时显示的相关操作,而且只能由 WindowManager,DisplayManager 和 INputManager 试试履行快速操作。

runWithScissors 表达式中创立了 WMS 目标。至于 Handler.runWithScissors 办法,有爱好的能够看一下这篇文章

到这儿咱们就知道 WMS 是从何处发动的了,下面咱们来看一下 WMS 的结构办法

WMS结构办法

private WindowManagerService(Context context, InputManagerService inputManager,
            boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
            ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
       ...
     		//1
        mInputManager = inputManager; // Must be before createDisplayContentLocked.
  			//2
  		  mPolicy = policy;
        //3
        mAnimator = new WindowAnimator(this);
  			//4
        mRoot = new RootWindowContainer(this);
				//5
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
				//6
        mActivityManager = ActivityManager.getService();
				//7
        LocalServices.addService(WindowManagerInternal.class, new LocalService());
  		...
    }
  1. 保存 IMS ,这样就持有了 IMS 的引证。
  2. 初始化 WindowManagerPolicy ,它用来界说一个窗口丈量所需求遵从的标准。
  3. 创立了 WindowAnimator,它用于办理一切的窗口动画。
  4. 创立 RootWindowContainer 目标,根窗口容器
  5. 获取 DisplayManager 服务
  6. 获取 AMS,并持有他的引证
  7. 将 LocalService 增加到 LocalServices 中

在上面 WMS的发动中 WMS 创立完结后会调用 wm.onInitReady 办法,下面咱们来看一下这个办法:

 public void onInitReady() {
   initPolicy();
   //将 WMS 增加到 Watchdog 中,Watchdog 用来监控一下体系关键服务的运转状况。
   //这些被监控的服务都会完结 Watchdog.Monitor 接口,Watchdog 每分钟都会对
   //被监控的服务进行检测,假如被监控的服务出现了死锁,则会杀死 Watchdog 地点的进程
   Watchdog.getInstance().addMonitor(this);
   ......
}
private void initPolicy() {
    UiThread.getHandler().runWithScissors(new Runnable() {
        @Override
        public void run() {
            WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
            mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
        }
    }, 0);
}

上面的和 WMS 的 main 办法类似,WindowManagerPolicy (简称 WMP) 是一个接口,init 的详细完结在 PhoneWindowManager(PWM) 中,而且经过上面代码咱们知道,init 办法运转在 android.ui线程中。因而他的线程优先级要高于 android.display 线程,有必要等 init 办法履行完结后,android.display线程才会被唤醒从而继续履行下面的代码。

WindowManagerPolicy 用来界说一个窗口战略所要遵从的通用标准,,并供给了 WindowManager 一切的特定 UI 行为
他的详细完结类为 PhoneWindowManager

在上面的文章中,总共供给了三个线程,分别是 system_serverandroid.displayandroid.ui,他们之间的联系如下图所示:

35908136f7444e90b2225c99c265342f_tplv-k3u1fbpfcp-zoom-in-crop-mark_3024_0_0_0.webp

system_server 线程中会调用 main 办法,mian 办法中会创立 WMS,创立的进程实在 android.display 线程中,他的优先级会高一些,创立完结后才会唤醒处于 system_server 线程。

WMS 创立完结后会调用 onInitReady 中的 initPolicy 办法,该办法中会调用 PWM 的 init() 办法,init 办法调用完结之后就会唤醒 system_server 线程。init 办法运转中 android.ui 线程中。

之后就会接着履行 system_server 中的代码,例如 displayReady 等。

WIMS 窗口办理

Window 的操作有两大部分,一部分是 WindowManager 来处理,一部分是 WMS 来处理,如下图所示:

e9e3016681ac4c9ba3e9bafcb9e99647_tplv-k3u1fbpfcp-zoom-in-crop-mark_3024_0_0_0.webp

  • WindowManager 中,经过 WindowManagerGlobal 创立 ViewRootImpl ,也便是 View 的根。在 ViewRootImpl 中完结对 View 的制作等操作,然后经过 IPC 获取到 Session ,最终经过 WMS 来进行处理。在理解 Window 和 WindowManager 这篇文章中,讲解了 WIndow 的创立,增加和删去部分,有爱好的能够看一下。
  • WMS 来处理后边的部分,详细见下文

WindowManager -> WMS

咱们都知道 Window 的增加最终是经过 ViewRootImpl.addTodisplay 办法来完结的,咱们先来看一下:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
        if (mView == null) {
         	....
            try {
                res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mDisplayCutout, inputChannel,
                        mTempInsets, mTempControls);
            }
            ...
    }
}

这儿调用了 mWindowSession.addToDisplayAsUser 来完结最终的增加,咱们先看看 mWindowSession 是个啥东西。

public ViewRootImpl(Context context, Display display) {
    this(context, display, WindowManagerGlobal.getWindowSession(),
            false /* useSfChoreographer */);
}
//WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
     			....
                IWindowManager windowManager = getWindowManagerService();
                //经过 windowManager 创立一个 IWindowSession 
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        });
            }
        return sWindowSession;
    }
}
public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                // 经过 AIDL 获取 IWindowManager 
                sWindowManagerService = IWindowManager.Stub.asInterface(
                    //获取 WindowManagerService 的 IBinder 
                    ServiceManager.getService("window"));
			//...
            }
            return sWindowManagerService;
        }
    }
// WindowManagerService.java    
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
    return new Session(this, callback);//传入了 this,将 Session 就会持有 WindowManagerService 的引证
}

mWindowSession 是 IwindowSession 的目标,完结类是 Session。

上面代码中,在 VIewRootImpl 初始化的时分,经过IPC 获取到 IWindowManager,然后经过 IPC 的调用创立了 Session 目标。

接着咱们来看一下 mWindowSession.addToDisplayAsUser 办法,也便是在 Session 类中

@Override
public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
        int viewVisibility, int displayId, int userId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
            outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
            outInsetsState, outActiveControls, userId);
}

能够看到,最终是经过 mService 来完结增加的,

需求留意的是,WMS 并不联系 View 的详细内容,他只关心各个使用显示的界面巨细,层级值等,这些数据到包含在 WindowManager.LayoutParams 中。也便是上面的 atrs 属性。

留意 addWindow 的第二个参数是一个 IWindow 类型,这是 App 露出给 WMS 的笼统实例,在 ViewRootImp 中实例化,与 ViewRootImpl 一一对应,搭档也是 WMS 向 App 端发送音讯的 Binder 通道。

从 WindowManager 到 WMS 的详细流程如下所示:

202210221957806.png

WMS 重要成员


final WindowManagerPolicy mPolicy;
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
final AppOpsManager mAppOps;
final DisplaySettings mDisplaySettings;
...
final ArraySet<Session> mSessions = new ArraySet<>();
final WindowHashMap mWindowMap = new WindowHashMap();
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();
final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
WindowState[] mPendingRemoveTmp = new WindowState[20];
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
...
final H mH = new H();
...
final WindowAnimator mAnimator;
...
 final InputManagerService mInputManager
  • WindowManagerPolicy mPolicy

    窗口办理战略的接口类,用来界说一个窗口战略所要遵从的通用标准,并供给了 WindowManager 一切的特定 UI 行为
    他的详细完结类为 PhoneWindowManager,这个完结类在 WMS 创立时被创立。WMP 答应定制窗口层级和特殊窗口类型以及关键字的调度和布局

  • ArraySet mSessions

    ArraySet 类型的变量,元素类型为 Session,他首要用于进程间的通讯。而且每个使用程序都会对应一个 Session,WMS 保存这些 Session 用来记载一切向 WMS 提出窗口办理服务的客户端

  • WindowHashMap mWindowMap

    承继自 HashMap,它约束 key 为 IBinder,value 的值类型为 WindowState,WindowState 用于保存窗口信息,在 WMS 中用来描绘一个窗口。所以 mWindowMap 便是用来保存 WMS 中各种窗口的调集。

  • ArrayList mFinishedStarting

    元素类型是 AppWindowToken,他是 WindowToken 的子类,想要了解 mFinishedStarting 的意义,需求先了解 WindowToken 是什么,WindowToken 首要由两个效果:

    1. 能够理解为窗口令牌,当使用程序想要向 WMS 申请创立一个窗口,则需求向 WMS 出示有用的 WindowToken。AppWindowToken 作为 WindowToken 的子类,首要用来描绘使用程序的 WindowToken 结构。
    2. WindowToken 会将相同组件(例如 Activity)的窗口(WindowState)调集在一起,便利办理。

    mFinishedStarting 便是用于存储现已完结发动的使用窗口程序窗口(比如Activity) 的 AppWindowToken 的列表。

    除了 mFinishedStarting,还有类似的 mFinishedEarlyAnim和 mWindowReplacementTimeouts

    其间 mFinishedEarlyAnim 存储了现已完结窗口制作而且不需求再展现任何现已保存 surface 的使用程序窗口的 AppWindowToken

    mWindowReplacementTimeout 存储了等待替换的使用程序窗口的 AppWindowToken,假如替换不及时,旧窗口就需求被处理

  • ArrayList mResizingWindow

    用来存储正在调整巨细的窗口列表。与 mResizingWindows 类似的还有个 mPendingRemove,mDestorySurface 和 mDestoryPreservedSurface 等等。

    其间 mPendingRemove 是在内存耗尽时设置的,里边存有需求强制删去的窗口。

    mDestorySurface 里边存有需求被 Destory 的 Surface ,mDestoryPreservedSurface 里边存有窗口需求保存的等等销毁的 Surface ,为什么窗口需求保存这些 Surface ?这是由于当窗口阅历 Surface 变化时,窗口需求一直保持旧 Surface,直到新 Surface 的榜首帧制作完结。

  • WindowAnimator mAnimator

    WindowAnimator 类型的变量,用于办理窗口的动画以及特效动画

  • H mH

    承继自 Hnadler 类,用于将任务加入到主线程的音讯队列中,这样代码逻辑就会在主线程履行

  • InputManagerService mINputManager

    IMS ,输入体系的办理者。IMS 会对接触事情进行处理,他会寻觅一个最合适的窗口来处理接触反应信息,WMS 是窗口办理者,因而 WMS 理所应当的成为了输入体系的中转站,WMS 包含了 IMS 的引证不足为怪。

WMS 增加 Window

Part 1
public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
        int requestUserId) {
    int[] appOp = new int[1];
    //1
    int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
            appOp);
    if (res != WindowManagerGlobal.ADD_OKAY) {
        return res;
    }
    synchronized (mGlobalLock) {
        //2
        final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
        ...
				//3
        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
            parentWindow = windowForClientLocked(null, attrs.token, false);
            if (parentWindow == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
            }
            if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                    && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
                        + "%s.  Aborting.", attrs.token);
                return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
            }
        }
        ...		
}

WMS 的 addWindow 办法回来的是 addWindow 的各种状况,例如 增加成功,失利,无效的 display 等,这些状况界说在 WindowManagerGloabl 中 。

注释1 处调用了 checkAddPermission 办法来查看权限,mPolicy 的完结类是 PhoneWindowManager

注释2 经过 displayId 来获得 Window 要增加到那个 DisplayContent,假如没有找到,则回来 WindowManagerGlobal.ADD_INVALID_DISPLAY 状况。其间DisplayContent 用来描绘一块屏幕

如下面代码,从 mRoot(RootWindowContainer) 对应的 DisplayContent,假如没有,则创立一个再回来,RootWindowContainer 是用来办理 DisplayContent 的。

  •   private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) {
          if (token != null) {
              final WindowToken wToken = mRoot.getWindowToken(token);
              if (wToken != null) {
                  return wToken.getDisplayContent();
              }
          }
          DisplayContent displayContent = mRoot.getDisplayContent(displayId);
          // Create an instance if possible instead of waiting for the ActivityManagerService to drive
          // the creation.
          if (displayContent == null) {
              final Display display = mDisplayManager.getDisplay(displayId);
              if (display != null) {
                  displayContent = mRoot.createDisplayContent(display, null /* controller */);
              }
          }
          return displayContent;
      }
    

注释3 判别 type 的窗口类型(100 – 1999),假如是子类型,有必要要有父窗口,而且父窗口不能是子窗口类型

Part 2
 				ActivityRecord activity = null;
        final boolean hasParent = parentWindow != null;
        //1
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);
        final int rootType = hasParent ? parentWindow.mAttrs.type : type;
        boolean addToastWindowRequiresToken = false;
			  //2
        if (token == null) {
        				...
           			//3
                token = new WindowToken(this, binder, type, false, displayContent,
                        session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
          			//4
            } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {							  //5	
                atoken = token.asAppWindowToken();
                if (atoken == null) {
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } 
          			....
            } else if (rootType == TYPE_INPUT_METHOD) {
                if (token.windowType != TYPE_INPUT_METHOD) {
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } 
						.....

注释 1 处经过 displayContent 的 getWindowToken 办法得到父窗口的 WindowToken 或者是当时窗口的 WindowToken。RootType 也相同如此。

注释2处假如 token 等于 null,而且不是使用窗口或者是其他类型的窗口,则窗口便是体系类型的了(例如 Toast),就进行隐式创立 WindowToken,这说明咱们增加窗口时是能够不向 WMS 供给 WindowToken 的,WindowToken 的隐式和显式创立是需求区别的,第四个参数 false 表明隐式创立。一般体系窗口都不需求增加 token,WMS 会隐式创立。例如 Toast 类型的床

接着便是 token 不为空的状况,会在注释 4 处判别是否位 使用窗口,假如是 使用窗口,就会讲 WindowToken 转换为针对于使用程序窗口的 AppWindowToken,然后再继续进行判别。

part3
//1
final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
          //2  
					 if (win.mDeathRecipient == null) {
                return WindowManagerGlobal.ADD_APP_EXITING;
            }
						//3
            if (win.getDisplayContent() == null) {
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
						//4
            displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
                    Binder.getCallingUid());
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
						//5
            res = displayPolicy.prepareAddWindowLw(win, attrs);
            ....
						//6
            mWindowMap.put(client.asBinder(), win);
            boolean imMayMove = true;
						//7,增加窗口
            win.mToken.addWindow(win);
            ...

在注释1 处创立了WindowState,保存了窗口的一切状况信息(例如 WMS ,Session,WindowToken等),在 WMS 中它代表一个窗口。WindowState 与窗口时一一对应的联系

在注释2和注释3处判别请求增加窗口的客户端是否现已逝世,假如逝世则不会履行下面逻辑。

注释 4处调用了 adjustWindowParamsLw 办法,这儿会依据窗口的 type 类型对窗口的 LayoutParams 的一些成员变量进行修正。源码注释信息为 清理来自客户端的布局参数。答应战略 做一些事情,比如保证特定类型的窗口不能 输入焦点

注释 5处调用了 prepareAddWindowLw 办法用于预备将窗口增加到体系中

注释 6处将 WindowState 增加到 mWindowMap 中,mWindowMap 是各种窗口的调集。

注释 7 处将 WindowState 增加到该 WindowState 对应的 WindowToken 中(实际上便是保存在 WindowToken 的父类 WindowContainer),这样 WindowToken 就包含了相同组件的 WindowState。

相关类
  • IWindow

    App 端露出给 WMS 的笼统实例,在 ViewRootImpl 中实例化,与 ViewRootImpl 一一对应,也是 WMS 和 APP 端交互的通道

  • RootWindowContainer

    设备窗口层次结构的根,再 WMS 结构办法中被创立。负责办理 DisplayContent。

  • WindowState

    WMS 端的窗口令牌,与窗口一一对应,是 WMS 办理窗口的重要依据,内部保存了窗口的一切状况信息

  • WindowToken

    WindowToken 首要有两个效果

    1. 能够理解为窗口令牌,当使用程序想要向 WMS 申请创立一个窗口,则需求向 WMS 出示有用的 WindowToken。而且窗口类型有必要与所持有的 WindowToken 的类型共同。

      从上面的代码中能够看到,在创立体系类型窗口时不需求供给有用的 Token,WMS 会隐式的创立一个 WindowToken,看起来谁都能够增加这个体系窗口,但是在 addWindow 办法一开始就调用 mPolicy.checkAddPermission 来查看权限,她要求客户端有必要拥有 INTERNAL_SYSTEM_WINDOW 或者 SYSTEM_ALERT_WINDOW 权限才能够创立体系类型窗口。

    2. WindowToken 会将相同组件(例如 Activity)的窗口(WindowState)调集在一起,便利办理。

      至于为什么说会调集在一起,由于有些窗口时复用的同一个 token,例如 Activity 和 Dialog 便是复用的同一个 AppToken,Activity 中的 PopWindow 复用的是一个 IWindow 类型 Token,Toast 体系类型的窗口也能够当作 null,就算不是 null,WMS 也会强制创立一个隐式 token。

  • DisplayContent

    假如说 WindowToken 依照窗口之间的逻辑将其分组,那么 DisplayContent 则依据窗口的显示位置将其分组。隶属于同一个 DisplayContent 的窗口会被显示在同一个屏幕中,每一个 DisplayContent 都对应一个唯一的 ID,在增加窗口的时分经过指定这个 ID 决定将被显示在那个屏幕中。

    DisplayContent 有一个隔离的概念,处于不同 DisplayContent 的两个窗口在布局,显示顺序以及动画处理上不会有任何的耦合。因而,就这几个方面来说,DisplayContent 就像是一个孤岛,一切这些操作都能够在内部履行。因而这个本来属于 WMS 全局操作的东西,变成了 DisplayContent 内部的操作了。

    别的,DisplayContent 由 RootWindowContainer 来办理,再增加窗口的最开始,就会依据传入的参数获取 DisplayContent

总结一会儿

经过上面的流程,App 到 WMS 注册窗口的流程就完了,WMS 为窗口创立了用来描绘状况的 WindowState,接下来就会为新建的窗口显示次序,然后再去申请 Surface,才算是真正的分配了窗口。接下来还有一部分,后边再渐渐搞吧。

这儿对 WMS 的 addWindow 流程做一个总结 :

  1. 首要查看权限

  2. 接着从 mRoot(RootWindowContainer)中获取 DisplayContent ,假如没有就会依据 displayId 创立一个新的。DisplayContent

  3. 接着便是 type 类型的判别,假如是子类型,就有必要要获取到他的父窗口,

  4. 接着运用 DisplayContent 获取当时或者父窗口获取 token,假如为 null 就扫除一会儿窗口和其他的窗口,剩余的便是能够不必带着 token 的窗口,WMS 会隐式的创立窗口 token。假如不等于 null 就判别是使用窗口就将 token 转为 AppWindowToken,后边还有一大堆窗口判别,只要是不满足就直接 return。

  5. 类型啥的判别完结后,就会创立 WindowState,而且传入 WMS、IWindow、token 等。WindowState 里边保存了窗口的一切信息。WindowState 与窗口一一对应。

  6. 接着就履行调用了 WindowState 的 attache 、initAppOpsState 等办法,这些先暂时不说。

    WindowState 创立完结后就会被增加到 mWindowMap 中,能够 IWindow 的 Binder 为 key,WindowState 为 value 增加进去。

  7. 最终便是 win.mToken.addWindow(win) ,这儿的 mToken 便是上面 第三步 获取的 token,然后将 WindowState 增加到 WindowToken 中。由于 WindowToken 是能够复用的,所以这儿的联系便是,每个 WindowToken 都会保存对应的 WindowState,而每个 WindowState 也都会都持有 WindowToekn。

经过本文,你应该了解到

  • WindowManager 怎么与 WMS 交互
  • WindowToken 到底是个啥东西,在什么状况下需求传,在啥状况下不必手动传,它的效果是什么。
  • DisplayContent 是用来干啥的,他是被谁办理,他自己又在办理者谁。本篇文章到最终也没太看懂怎么运用 DisplayContent,所以到目前为止只需求知道他的效果就行了。
  • RootWindowContainer 到目前为止干了什么事。
  • 了解 WindowState 是个啥东西,与 WindowToken 的联系是咋样的。
  • WindowContainer 类,上面 WindowTokenWindowStateRootWindowContainerDisplayContent 都是承继自 WindowContainer,至于这儿为啥这么写,我也不太懂,渐渐再看呗。

最终

到这儿这篇文章也写完了,但是 WMS 的窗口办理 还有一部分没写,原因便是由于我也还没看懂,所以就先写到这儿,等后边看懂了,学会了再写一篇。

文章中写的也不一定全部是正确的,我自己也是自己学自己写,然后再渐渐的梳理,假如那些错了,或者是有任何问题可在下方谈论,或者是直接私信我。

参考资料

深入理解 Android 卷3 第四章

WMS 窗口办理

皇叔解析WMS