美文网首页
Android中WindowManager的理解

Android中WindowManager的理解

作者: MadnessXiong | 来源:发表于2020-04-07 00:44 被阅读0次

    WindowManager简介

    WindowManager(WM)是一个接口类,继承自接口ViewManager,它是用来管理Window的。它的实现类为WindowManagerImpl。对窗口的添加,更新和删除操作,由WM负责。它会将具体的工作交由WMS来处理,WM和WMS通过Binder来进行进程间通信,WMS作为系统服务有很多API是不会暴露给WM的

    WindowManager的关联类

    • WindowManager继承自ViewManager,ViewManager中定义了3个方法,分别用来添加,更新和删除View

      public interface ViewManager{
            //添加view
          public void addView(View view, ViewGroup.LayoutParams params);
            //更新view
          public void updateViewLayout(View view, ViewGroup.LayoutParams params);
            //删除view
          public void removeView(View view);
      }
      

      WindowManager在继承了ViewManager的同时,又加入了很多功能,包括Window的类型和层级相关的常量,内部类以及一些方法,其中有2个方法是根据Window的特性加入的:

            //这个方法能够得知这个windowManager实例将window添加到哪个屏幕上了,也就是得到WindowManager所管理的屏幕
            public Display getDefaultDisplay();
            //规定在这个方法返回前要立即执行View.onDetachedFromWindow(),来完成传入的View的相关的销毁工作
            public void removeViewImmediate(View view);
      
    • Window是一个抽象类,它的具体实现类为PhoneWindow,PhoneWindow在Activity启动时最终会在它的attach()中被创建。

          final void attach() {
                //创建PhoneWindow
              mWindow = new PhoneWindow(this, window, activityConfigCallback);
                    //设置windowManager
              mWindow.setWindowManager(
                      (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                      mToken, mComponent.flattenToString(),
                      (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
                  //对持有的WindowManager引用赋值
              mWindowManager = mWindow.getWindowManager();
          }
      

      可以看到,这首先构造了PhoneWidow,然后设置了WindowManager,WindowManager通过getSystemService()方式获得,看setWindowManager()的代码:

             public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
                     boolean hardwareAccelerated) {
                 if (wm == null) {
                     wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
                 }
                 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
             }
      

      如果传入的WM为null,那么就通过ContextImpl.getSystemService()再获取一次,那么看一下获取的方法:

             @Override
             public Object getSystemService(String name) {
                 return SystemServiceRegistry.getSystemService(this, name);
             }
      

      接着调用了SystemServiceRegistry.getSystemService(),继续看:

             public static Object getSystemService(ContextImpl ctx, String name) {
                 ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
                 return fetcher != null ? fetcher.getService(ctx) : null;
             }
      

      这里从SYSTEM_SERVICE_FETCHERS取出一个ServiceFetcher,最终取出需要的Service,那么看一下这个SYSTEM_SERVICE_FETCHERS:

            //将Service注册
                private static <T> void registerService(String serviceName, Class<T> serviceClass,
                     ServiceFetcher<T> serviceFetcher) {
                 SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
                 SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
             }
                 //在静态代码块中注册具体的Service
                registerService(Context.WINDOW_SERVICE, WindowManager.class,
                         new CachedServiceFetcher<WindowManager>() {
                     @Override
                     public WindowManager createService(ContextImpl ctx) {
                         return new WindowManagerImpl(ctx);
                     }});
      

      SystemServiceRegistry的静态代码块中通过registerService()注册了各种Service,这里可以看到Context.WINDOW_SERVICE对应的是WindowManagerImpl,那么最终getSystemService()得到的就是WindowManagerImpl对象。

      继续看setWindowManager():

             public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
                     boolean hardwareAccelerated) {
                 if (wm == null) {
                     wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
                 }
                 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
             }
      

      前面的分析已经知道wm实际是WindowManagerImpl,这里又调用了createLocalWindowManager(),那么看代码:

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

      可以看到,这里同样也是创建了WindowManagerImpl,不过这次将window传了进去,这样WindowManagerImpl就持有了window的引用。

      WindowManagerImpl持有了window的引用以后,就可以对window进行操作,如addVIew(),看代码:

          public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
              applyDefaultToken(params);
                //mGlobal是WindowManagerGlobal类型
              mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
          }
      
      

      这里可以看到,addView()的功能又转交给了WindowManagerGlobal

      再看WindowManagerGlobal:

      public final class WindowManagerGlobal {
      
          private static IWindowManager sWindowManagerService;
            //负责与WMS进行通信
          private static IWindowSession sWindowSession;
      
            //mView记录了所有View树的根节点(每个布局最外层的view)
          private final ArrayList<View> mViews = new ArrayList<View>();
            //mRoots记录了所有Activity的ViewRootImpl
          private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
          //mParams记录了所有window的属性
          private final ArrayList<WindowManager.LayoutParams> mParams =
                  new ArrayList<WindowManager.LayoutParams>();
          private final ArraySet<View> mDyingViews = new ArraySet<View>();
      
            //单例对象
          public static WindowManagerGlobal getInstance() {
              synchronized (WindowManagerGlobal.class) {
                  if (sDefaultWindowManager == null) {
                      sDefaultWindowManager = new WindowManagerGlobal();
                  }
                  return sDefaultWindowManager;
              }
          }
      
      

      可以看到WindowManagerGlobal中定义了sWindowSession,负责与WMS的通信,mViews记录了所有view树的根节点,mRoots记录了所有Activity的ViewRootlmpl,mParams记录了所有window的属性。

      这里的WindowManagerGlobal是一个单例对象,一个进程只有一个。而WindowManagerImpl从它的创建来看,是有多个实例的

      总结:PhoneWindow继承自WIndow,Window通过setWindowManager()与WindowManager产生关联。WindowManager继承自接口ViewManager,WindowManagerImpl是WIndowManager接口的实现类,但是具体功能会委托给WindowManagerGlobal来实现。

    Window的属性

    Window为了更好地与WMS通讯,它们之间制定了一些协议,这个协议就是Window的属性。这些属性定义在WindowManager的LayoutParams中。Window的属性有很多种,与开发关系密切的有3种,它们分别是Type(window的类型),Flag(Window的标志)和SoftInputMode(软键盘相关模式)

    • Window的类型和显示次序

      • Window的类型分为三大类型:Application Window(应用程序窗口),Sub Window(子窗口),System Window(系统窗口)。每个大类型中又包含了很多种类型,它们都定义在了WindowManager的静态内部类LayoutParams中

        • 应用程序窗口:应用程序窗口的Type值的范围为1-99
        • 子窗口:它不能独立存在,需要附着在其他窗口上才可以。如PopupWindow。它的Type值范围为1000-1999
        • 系统窗口:Toast,输入法窗口,系统音量条窗口,系统错误窗口都属于系统窗口。系统窗口的Type值范围为2000-2999
      • 窗口的显示次序:当一个进程向WMS申请一个窗口时,WMS会为窗口确定显示次序。为了方便窗口显示次序管理,手机屏幕可以虚拟地用X,Y,Z轴来表示,其中Z轴垂直与屏幕,从屏幕内向屏幕外,这样确定窗口显示次序也就是确定窗口在Z轴上的次序,这个次序称为Z_Oder。Type值是Z_oder排序的依据,Type值越大则Z-Oder排序越靠前,就越靠近用户。

    • Window的标志

      window的标志也就是flag,用于控制window的显示,同样被定义在windowmanager的内部类LayoutParams中。

      设置Window的flag有3种方法:

      //第一种,内部也是调用setFlags()
      mWindow.addFlags()
      //第二种
      mWindow.setFlags()
      //第三种
      WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
      layoutParams.flags=WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
      TextView textView = new TextView(this);
      windowManager.addView(textView,layoutParams);
      
      

    Window的操作

    对Window的操作,最终都会交由WMS来进行处理。窗口的处理分为2部分,一部分是WindowManager处理部分,一部分是WMS处理部分。对于不同的窗口类型(应用程序窗口,子窗口,系统窗口)添加过程有所不同,但是对于WMS处理的过程,基本上是一样的。WMS对这三大窗口基本是一视同仁的。

    • 系统窗口的添加过程

      这里看一下StatusBar的添加过程

             private void addStatusBarWindow() {
                 makeStatusBarView();
                 mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
                 mRemoteInputController = new RemoteInputController(mHeadsUpManager);
               
                 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
             }
      
      

      继续再看这个add():

             public void add(View statusBarView, int barHeight) {
                         //通过WindowManager.LayoutParams配置属性
                 mLp = new WindowManager.LayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT,
                         barHeight,
                         WindowManager.LayoutParams.TYPE_STATUS_BAR,
                         WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                                 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                                 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                                 | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                         PixelFormat.TRANSLUCENT);
                 //调用windowManager的addView()
                 mWindowManager.addView(mStatusBarView, mLp);
                 mLpChanged = new WindowManager.LayoutParams();
             }
      
      

      这里调用了mWindowManager.addView()添加status,前面说过,具体是由WindowMangerImpl实现的,那么看一下它的addView():

          public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
              applyDefaultToken(params);
              mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
          }
      
      

      这里WindowManagerGlobal.addView(),看代码:

             public void addView(View view, ViewGroup.LayoutParams params,
                     Display display, Window parentWindow) {
      
                 ViewRootImpl root;
                 View panelParentView = null;
                 //创建ViewRootImpl
                 root = new ViewRootImpl(view.getContext(), display);
         
                 view.setLayoutParams(wparams);
                         //添加view
                 mViews.add(view);
                 //添加root
                 mRoots.add(root);
                 //添加窗口参数
                 mParams.add(wparams);
                 //将窗口及窗口参数设置进ViewRootImpl中
                 root.setView(view, wparams, panelParentView);
      
             }
      
      

      这里可以看到添加窗口的操作是通过ViewRootImpl进行的,看一下它的setView():

             public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                    //进行进程间通信
                            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                         mAttachInfo.mOutsets, mInputChannel);
             }
      
      

      这里最终调用了mWindowSession.addToDisplay()进行进程间通信,最终会调用到WMS到addView(),并将自身也就是Session传进去。每个应用程序都会对应一个Session,WMS会用ArrayList来保存这些Session。剩下的工作就会交由WMS处理,在WMS中会为这个添加的窗口分配Surface,并确定窗口显示次序。

    • Activity的添加过程

      无论哪种窗口,它的添加过程在WMS处理部分中基本是类似的,只不过会在权限和窗口显示次序等方面会又些不同。但是在WindowManager处理部分会有所不同。

      当Activity启动时会调用ActivityThread的handleResumeActivity():

          @Override
          public void handleResumeActivity() {
      
                    //这里会调用到Activity的attach(),会初始化WindowManager
              final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
                            //获取初始化好的WindowManager
                  ViewManager wm = a.getWindowManager();
                        //wm添加decorView
                  wm.addView(decor, l);
          }
      
      

      可以看到,最后这里将decorView添加到了Activity里,之后的流程和status一样。

    • Window到更新过程

      Window的更新方法是调用upDateViewLayout(),同样是看WindowManagerGlobal.upDateViewLayout(),看代码:

             public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
      
                 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
                         //将更新的参数设置进view中
                 view.setLayoutParams(wparams);
         
                 synchronized (mLock) {
                        //找到要更新的窗口的索引
                     int index = findViewLocked(view, true);
                     //根据所有的到它的ViewRootImpl
                     ViewRootImpl root = mRoots.get(index);
                     //更新布局列表参数
                     mParams.remove(index);
                     mParams.add(index, wparams);
                     //将更新好的参数设置到ViewRootImpl中
                     root.setLayoutParams(wparams, false);
                 }
             }
      
      

      可以看到,这里首先找到要更新的窗口的索引,然后根据索引找到它的ViewRootImpl,最后更新参数后通过ViewRootImpl.setLayoutParams()进行设置,看代码:

             void setLayoutParams() {
                     scheduleTraversals();
             }
      
      

      这里调用了scheduleTraversals(),看代码:

             void scheduleTraversals() {
                //发起一个回调
              mChoreographer.postCallback(
                             Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
      
             }
      
      

      这里通过Choreographer发起一个回调,这个回调在下一帧被渲染时执行它的run(),那么再看它的run():

             final class TraversalRunnable implements Runnable {
                 @Override
                 public void run() {
                     doTraversal();
                 }
             }
      

      这里执行了doTraversal(),继续看:

             void doTraversal() {
              //开始绘制流程
              performTraversals();
            }
      

      这里可以看到,调用performTraversals(),这个方法最终会调用performMeasure(),performLayout(),performDraw()完成整个绘制流程。

    相关文章

      网友评论

          本文标题:Android中WindowManager的理解

          本文链接:https://www.haomeiwen.com/subject/mipwphtx.html