WindowManager

2018-07-10  本文已影响5人  蒸汽飞船

WindowManagerImpl是WindowManagerGlobal的代理类,
WindowManagerGlobal是单例模式,所以在一个App里面只会有一个
而IWindowSession一个App只有一个,但是每个ViewRootImpl都持有对IWindowSession的引用,所以ViewRootImpl可以和WMS喊话,但是WMS怎么和ViewRootImpl喊话呢?是通过ViewRootImpl::W这个内部类实现的

ActivityThread.main:

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        。。。
        Looper.loop();

ActivityThread.attach();

   final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread);//把ApplitionThread binder 传过去
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
//回调
handleBindApplication(AppBindData data)

handleBindApplication(AppBindData data) :

//data.info = LoadApk;
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
mInstrumentation = new Instrumentation();
  Application app = data.info.makeApplication(data.restrictedBackupMode, null);

data.info.makeApplication:

 ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
   Application    app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
 instrumentation.callApplicationOnCreate(app);

DecorView 根部局:
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。

ViewRoot:
ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。

ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。RootView继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。

下面结构图可以清晰的揭示四者之间的关系:


image.png

详细关系图

handleLaunchActivity
handleLaunchActivity:调用了Activity的attach方法
Activity启动attach时候发生的事情:
1.创建PhoneWindow:mWindow = new PhoneWindow
2.设置window的Callback为Activity mWindow.setCallback(this);//设置回调,向Activity分发点击或状态改变等事件
3.window设置windowmanager

windowmanager是接口,继承自ViewManager,ViewManager接口就3方法:添加\更新\移除view,windowmanager实现类WindowManagerImpl

handleResumeActivity:

  1. WindowManagerImpl#addView方法add decor view
  2. 1中又WindowManagerGlobal#addView方法
  3. 2中实例化了ViewRootImpl类,接着用ViewRootImpl#setView方法,并把DecorView作为参数传递进去,在这个方法内部,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上。
  4. 最后通过WMS调用ViewRootImpl#performTraverals方法开始View的测量、布局、绘制流程,这三大流程在下文会详细讲述,谢谢大家的阅读。

WindowManagerImpl是WindowManagerGlobal的代理类,方法都是调用的WindowManagerGlobal的方法。
WindowManagerGlobal是单例模式,所以在一个App里面只会有一个WindowManagerGlobal实例。在WindowManagerGlobal里面维护了三个集合:

//前三个是addview时加
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
//removeview时加
private final ArraySet<View> mDyingViews = new ArraySet<View>();

参考上下
Activity只看到Window类,Window只有一个实现类PhoneWindow,Window里有WindowManager和DecorView,DecorView是FrameLayout的子类,是最底层的布局,DecorView里面有一个mContentParent,根据用户选择的不同标志如不同Theme这个有不同的布局(上面标题下面内容),但是里面一定有一个R.id.content的ViewGroup,而且我们在xml文件中声明的布局文件,会被添加进去DecorView是PhoneWindow的内部类,继承自FrameLayout,是最底层的界面

RPC方面:WindowManagerGlobal、ViewRootImpl、W
WindowManagerGlobal是和WindowManagerService(即WMS)通信的,但建立连接后将IWindowSession交给ViewRootImpl具体的交互都交给ViewRootImpl了。同时ViewRootImpl在setview()方法时通过IWindowSession将W对象传给WMS,这样,双方都有了对方的接口,WMS中的Session注册到WindowManagerGlobal的成员WindowSession中,ViewRootImpl::W注册到WindowState中的成员mClient中。

  1. IWindowSession为了App改变View结构时请求WMS为其更新布局。
  2. ViewRootImpl::W代表了App端的一个添加到WMS中的View,它可以理解为是ViewRootImpl中暴露给WMS的接口,这样WMS可以通过这个接口和App端通信。每一个像这样通过WindowManager接口中addView()添加的窗口都有一个对应的ViewRootImpl,也有一个相应的ViewRootImpl::W。

ViewRootImpl负责管理视图树和与WMS交互,与WMS交互是通过WindowSession。而且ViewRootImpl也负责UI界面的布局与渲染,负责把一些事件分发至Activity,以便Activity可以截获事件。大多数情况下,它管理Activity顶层视图DecorView,它相当于MVC模型中的Controller。













详细:
DecorView:
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。具体情况和Android版本及主体有关,以其中一个布局为例,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:fitsSystemWindows="true">
    <!-- Popout bar for action modes -->
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
        android:layout_width="match_parent" 
        android:layout_height="?android:attr/windowTitleSize"
        style="?android:attr/windowTitleBackgroundStyle">
        <TextView android:id="@android:id/title" 
            style="?android:attr/windowTitleStyle"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent" 
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子View,就是上面的id为content的FrameLayout中,在代码中可以通过content来得到对应加载的布局。

ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
ViewGroup rootView = (ViewGroup) content.getChildAt(0);

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) {
    ..................................................................
        mWindow = new PhoneWindow(this, window);//创建一个Window对象
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);//设置回调,向Activity分发点击或状态改变等事件
        mWindow.setOnWindowDismissedCallback(this);
     .................................................................
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//给Window设置WindowManager对象
 ....................................................................
    }

结语
上面写了这么多,你可能看了前面忘了后面,下面,凯子哥给你总结一下,这篇文章到底讲了什么东西:

每个Activity,都至少有一个Window,这个Window实际类型为PhoneWindow,当Activity中有子窗口,比如Dialog的时候,就会出现多个Window。Activity的Window是我们控制的,状态栏和导航栏的Window由系统控制。
在DecorView的里面,一定有一个id为content的FraneLayout的布局容器,咱们自己定义的xml布局都放在这里面。
Activity的Window里面有一个DecorView,它使继承自FrameLayout的一个自定义控件,作为整个View层的容器,及View树的根节点。
Window是虚拟的概念,DecorView才是看得见,摸得着的东西,Activity.setContentView()实际调用的是PhoneWindow.setContentView(),在这里面实现了DecorView的初始化和id为content的FraneLayout的布局容器的初始化,并且会根据主题等配置,选择不同的xml文件。而且在Activity.setContentView()之后,Window的一些特征位将被锁定。
Activity.findViewById()实际上调用的是DecorView的findviewById(),这个方法在View中定义,但是是final的,实际起作用的是在ViewGroup中被重写的findViewTraversal()方法。
Activity的mWindow成员变量是在attach()的时候被初始化的,attach()是Activity被通过反射手段实例化之后调用的第一个方法,在这之后生命周期方法才会依次调用
在onResume()刚执行之后,界面还是不可见的,只有执行完Activity.makeVisible(),DecorView才对用户可见
ViewManager这个接口里面就三个接口,添加、移除和更新,实现这个接口的有WindowManager和ViewGroup,但是他们两个面向的对象是不一样的,WindowManager实现的是对Window的操作,而ViewGroup则是对View的增、删、更新操作。
WindowManagerImpl是WindowManager的实现类,但是他就是一个代理类,代理的是WindowManagerGlobal,WindowManagerGlobal一个App里面就有一个,因为它是单例的,它里面管理了App中所有打开的DecorView,ContentView和PhoneWindow的布局参数WindowManager.LayoutParams,而且WindowManagerGlobal这个类是和WMS通信用的,是通过IWindowSession对象完成这个工作的,而IWindowSession一个App只有一个,但是每个ViewRootImpl都持有对IWindowSession的引用,所以ViewRootImpl可以和WMS喊话,但是WMS怎么和ViewRootImpl喊话呢?是通过ViewRootImpl::W这个内部类实现的,而且源码中很多地方采用了这种将接口隐藏为内部类的方式,这样可以实现六大设计原则之一——接口最小原则,这样ViewRootImpl和WMS就互相持有对方的代理,就可以互相交流了
ViewRootImpl这个类每个Activity都有一个,它负责和WMS通信,同时相应WMS的指挥,还负责View界面的测量、布局和绘制工作,所以当你调用View.invalidate()和View.requestLayout()的时候,都会把事件传递到ViewRootImpl,然后ViewRootImpl计算出需要重绘的区域,告诉WMS,WMS再通知其他服务完成绘制和动画等效果,当然,这是后话,咱们以后再说。
Window分为三种,子窗口,应用窗口和系统窗口,子窗口必须依附于一个上下文,就是Activity,因为它需要Activity的appToken,子窗口和Activity的WindowManager是一个的,都是根据appToken获取的,描述一个Window属于哪种类型,是根据LayoutParam.type决定的,不同类型有不同的取值范围,系统类的的Window需要特殊权限,当然Toast比较特殊,不需要权限
PopupWindow使用的时候,如果想触发按键和触摸事件,需要添加一个背景,代码中会根据是否设置背景进行不同的逻辑判断
Dialog在Activity不可见的时候,要主动dismiss掉,否则会因为appToken为空crash
Toast属于系统窗口,由系统服务NotificationManagerService统一调度,NotificationManagerService中维持着一个集合ArrayList<ToastRecord>,最多存放50个Toast,但是NotificationManagerService只负责管理Toast,具体的现实工作由Toast::TN来实现

作者:裸奔的凯子哥
链接:https://www.jianshu.com/p/65295b2cb047
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

上一篇下一篇

猜你喜欢

热点阅读