Android开发之漫漫长途 Ⅳ——Activity的显示之ViewRootImpl初探


声明:本文转载自https://my.oschina.net/u/2424031/blog/1605234,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!另外,本系列文章知识可能需要有一定Android开发基础和项目经验的同学才能更好理解,也就是说该系列文章面向的是Android中高级开发工程师。


第四篇了,,接着上一篇说 (怎么感觉还是没人评论呢)


在上一篇文章中我们主要分析了android.app.ActivityThread的main函数以及setContentView。另外我们还稍微分析了一下我们自己的源码,通过WindowManager添加View。我们知道调用setContentView把我们自己的xml布局添加到了DecorView ID为ID_ANDROID_CONTENT的布局后,最终还是会调用WindowManager.addView把DecorView加入PhoneWindow。到这里呢,我们把流程梳理一下。还是上图:ActivityThread简化图


相信读者根据上图再结合前面所讲的内容应该对Activity的创建和显示有了初步的认识。那么本章我们来继续讲Activity的显示。该注意的是本系列并不意在带领读者去看清每一步具体的源码。在前面的文章中我也很少贴出源码。本系列文章意在让读者对Android系统有个更整体的把握。我所写的每一章知识都有可能在实际工作中用到。就如前面所讲解的Android下的进程问题以及Activity的生命周期以及本章要讲解的View的五大过程的基础ViewRootImpl。而理解View的**五大过程(一般文章里都是三大过程)**以及View的事件体系是更好的去自定义View的基础。 本章接着第Ⅲ篇的文章来讲解View框架树的动力所在ViewRootImpl


上一章的最后我们讲到了使用WindowManager添加View。这一章我们来具体分析。最近我也发表了三篇文章,可反响度一般,我也是刚写技术博客。肯定有些不足之处。这次稍微改变些风格,在分析的时候贴一些源码上去。


从第一篇文章中我们就知道了使用WindowManager.addView方法添加View。那么看看该类的实现吧。

public interface WindowManager extends ViewManager { 	//这里我们只列出了一部分函数,但是并没有addView、updateViewLayout、removeView这三个函数 	public Display getDefaultDisplay();     public void removeViewImmediate(View view);     ... } 

好吧,果然没有这么简单,WindowManager是个接口,而且在其方法中没有找到addView方法,那么我们只能看看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); } 

还好是找到了,ViewManager没有再继承其他接口了。(要不然真不知道要找到什么时候去。) 既然WindowManager是个接口,那肯定要找它的实现类了。(在这里安利一个比较简单的方法,在Android Studio中)这里写图片描述 这里我们很幸运只找到了一个WindowManager的实现类(有的时候可能有很多个,当出现多个的时候,那只有一个个去看了)。这里我们来看WindowManagerImpl(看到这个 类的名字我们心里了然一笑,果然是java的命名规范)。

public final class WindowManagerImpl implements WindowManager {     private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();     private final Context mContext;     private final Window mParentWindow;      private IBinder mDefaultToken;      public WindowManagerImpl(Context context) {         this(context, null);     }      private WindowManagerImpl(Context context, Window parentWindow) {         mContext = context;         mParentWindow = parentWindow;     }      public WindowManagerImpl createLocalWindowManager(Window parentWindow) {         return new WindowManagerImpl(mContext, parentWindow);     }      public WindowManagerImpl createPresentationWindowManager(Context displayContext) {         return new WindowManagerImpl(displayContext, mParentWindow);     }      /**      * Sets the window token to assign when none is specified by the client or      * available from the parent window.      *      * @param token The default token to assign.      */     public void setDefaultToken(IBinder token) {         mDefaultToken = token;     }      @Override     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {         applyDefaultToken(params);         mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);     }      @Override     public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {         applyDefaultToken(params);         mGlobal.updateViewLayout(view, params);     }      private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {         // Only use the default token if we don't have a parent window.         if (mDefaultToken != null && mParentWindow == null) {             if (!(params instanceof WindowManager.LayoutParams)) {                 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");             }              // Only use the default token if we don't already have a token.             final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;             if (wparams.token == null) {                 wparams.token = mDefaultToken;             }         }     }      @Override     public void removeView(View view) {         mGlobal.removeView(view, false);     }      @Override     public void removeViewImmediate(View view) {         mGlobal.removeView(view, true);     }      @Override     public void requestAppKeyboardShortcuts(             final KeyboardShortcutsReceiver receiver, int deviceId) {         IResultReceiver resultReceiver = new IResultReceiver.Stub() {             @Override             public void send(int resultCode, Bundle resultData) throws RemoteException {                 List<KeyboardShortcutGroup> result =                         resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);                 receiver.onKeyboardShortcutsReceived(result);             }         };         try {             WindowManagerGlobal.getWindowManagerService()                 .requestAppKeyboardShortcuts(resultReceiver, deviceId);         } catch (RemoteException e) {         }     }      @Override     public Display getDefaultDisplay() {         return mContext.getDisplay();     } }  

WindowManagerImpl的源码如上所示,我们可以看到WindowManagerImpl的addView方法,WindowManagerImpl把工作交给了WindowManagerGlobal

/** WindowManagerGlobal 源码比较长,这里我们只列出了一部分 */ public final class WindowManagerGlobal {     private WindowManagerGlobal() {     }      public static void initialize() {         getWindowManagerService();     }      public static WindowManagerGlobal getInstance() {         synchronized (WindowManagerGlobal.class) {             if (sDefaultWindowManager == null) {                 sDefaultWindowManager = new WindowManagerGlobal();             }             return sDefaultWindowManager;         }     }      public static IWindowManager getWindowManagerService() {         synchronized (WindowManagerGlobal.class) {             if (sWindowManagerService == null) {                 sWindowManagerService = IWindowManager.Stub.asInterface(                         ServiceManager.getService("window"));                 try {                     if (sWindowManagerService != null) {                         ValueAnimator.setDurationScale(                                 sWindowManagerService.getCurrentAnimatorScale());                     }                 } catch (RemoteException e) {                     throw e.rethrowFromSystemServer();                 }             }             return sWindowManagerService;         }     }      public static IWindowSession getWindowSession() {         synchronized (WindowManagerGlobal.class) {             if (sWindowSession == null) {                 try {                     InputMethodManager imm = InputMethodManager.getInstance();                     IWindowManager windowManager = getWindowManagerService();                     sWindowSession = windowManager.openSession(                             new IWindowSessionCallback.Stub() {                                 @Override                                 public void onAnimatorScaleChanged(float scale) {                                     ValueAnimator.setDurationScale(scale);                                 }                             },                             imm.getClient(), imm.getInputContext());                 } catch (RemoteException e) {                     throw e.rethrowFromSystemServer();                 }             }             return sWindowSession;         }     }      public static IWindowSession peekWindowSession() {         synchronized (WindowManagerGlobal.class) {             return sWindowSession;         }     }  	//addView方法     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);         }          ViewRootImpl root;         View panelParentView = null;                  int index = findViewLocked(view, false);           if (index >= 0) {               if (mDyingViews.contains(view)) {                   mRoots.get(index).doDie();               } else {               //同一个View不允许被添加2次                   throw new IllegalStateException("View " + view                           + " has already been added to the window manager.");               }           } 		//② 创建一个ViewRootImpl对象并保存在root变量中          root = new ViewRootImpl(view.getContext(), display);           view.setLayoutParams(wparams); 		//③ 保存作为窗口的控件、布局参数以及新建的ViewRootImpl          mViews.add(view);          mRoots.add(root);          mParams.add(wparams);           // do this last because it fires off messages to start doing things          try { 	         // ④ 将作为窗口的控件设置给ViewRootImpl.这个动作将导致ViewRootImpl向WMS添加新的窗口、申请Surface以及托管控件在Surface上的重绘工作。这才是真正意义上完成了窗口的添加工作。              root.setView(view, wparams, panelParentView);          } catch (RuntimeException e) {              // BadTokenException or InvalidDisplayException, clean up.              if (index >= 0) {                  removeViewLocked(index, true);              }              throw e;          }         }     }      public void updateViewLayout(View view, ViewGroup.LayoutParams params) {         if (view == null) {             throw new IllegalArgumentException("view must not be null");         }         if (!(params instanceof WindowManager.LayoutParams)) {             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");         }          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);         }     }      public void removeView(View view, boolean immediate) {         if (view == null) {             throw new IllegalArgumentException("view must not be null");         }          synchronized (mLock) {             int index = findViewLocked(view, true);             View curView = mRoots.get(index).getView();             removeViewLocked(index, immediate);             if (curView == view) {                 return;             }              throw new IllegalStateException("Calling with view " + view                     + " but the ViewAncestor is attached to " + curView);         }     }      }  

我们可以看到WindowManagerGlobal的私有构造函数以及getInstance()这个熟悉的静态方法名字。可以看出WindowManagerGlobal是个典型的单例。 WindowManagerGlobal 的addView方法并不复杂,其主要的关键点我们已经标注并写了注释。也就是说WindowManagerGlobal的职责如下:

  1. 同意管理整个进程中所有窗口的信息。包括控件、布局参数以及ViewRootImpl这三个元素。(这一点从第③个注释可以看出)
  2. WindowManagerGlobal将窗口的创建、销毁、布局更新等任务交给了ViewRootImpl完成。

本篇总结 本篇文章分析了***WindowManager的addView***的过程,***WindowManager***是个接口,它的实现类是***WindowManagerImpl***类,而WindowManagerImpl又把相关逻辑交给了WindowManagerGlobal处理。WindowManagerGlobal***是个单例类,它在进程中只存在一个实例,是它内部的addView方法最终创建了我们的核心类***ViewRootImpl。ViewRootImpl实现了ViewParent接口,作为整个控件树的根部,它是控件树正常运作的动力所在,控件的测量、布局、绘制以及输入事件的派发处理窦世友ViewRootImpl出发。它是***WindowManagerGlobal***的实际工作者。


下篇预告 在下一篇文章中我们将深入介绍ViewRootImpl的工作流程。测量、布局、以及绘制。


此致,敬礼

本文发表于2018年01月10日 18:32
(c)注:本文转载自https://my.oschina.net/u/2424031/blog/1605234,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1965 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1