• Activity,Window,View,ViewRootImpl之间的关系


    关于Activity,Window,View的关系一直有个模糊的印象,看别人的分析一般都这么理解:Activity是管理Window, Window用来承载View相当于是View的容器、同时负责Vierw的添加删除等, View是最终的视图,ViewRootImpl负责管理View测量、布局、绘制等。也有说Window的作用可有可无的,作用并不大的。并不是说这些观点有问题,而是看了这么多后,会更迷惑,管理是怎么管理的,承载是怎么实现的,如果不自己根据源码看一些,这些概念会一直是抽象的,遇到问题还是没法理解, 例如:

    • 在Activity里调用
      WindowManager.LayoutParams wl = new WindowManager.LayoutParams(); getWindowManager().addView(mView,wl)

      LayoutParams wmParams =...
      addContentView(mView,wmParams); //activity里的方法
      这两种方式背后的实现是怎样的,有什么区别?
    • Dialog和PopupWindow的区别在哪里?为什么Dialog传入application的Context会报错?
    • ViewRootImpl是什么,一个Activity有多少个ViewRootImpl对象?
    • 该怎样理解Window?

    Window的创建过程

    之前一篇讲解了Android 应用点击图标到Activity界面显示的过程分析,接着分析界面的显示过程来引入Activity中Window的创建,以及View的加载显示过程。

    在ActivityThread.performLaunchActivity中,创建Activity的实例,接着会调用Activity.attach()来初始化一些内容,而Window对象就是在attach里进行创建初始化赋值的。

    Activity.attach
    final void attach(...) { 
        ...    
        mWindow = new PhoneWindow(this);    
        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();    
        ...
    }
    
    Window.setWindowManager
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) { 
        ...    
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
        ...
    }
    

    可看出Activity里新建一个PhoneWindow对象。在Android中,Window是个抽象的概念,Android中Window的具体实现类是PhoneWindow,Activity和Dialog中的Window对象都是PhoneWindow。

    同时得到一个WindowManager对象,WindowManager是一个抽象类,这个WindowManager的具体实现实在WindowManagerImpl中,对比Context和ContextImpl。

    每个Activity会有一个WindowManager对象,这个mWindowManager就是和WindowManagerService(WMS)进行通信,也是WMS识别View具体属于那个Activity的关键,创建时传入IBinder 类型的mToken。

    mWindow.setWindowManager(...,mToken, ...,...)
    

    这个Activity的mToken,这个mToken是一个IBinder,WMS就是通过这个IBinder来管理Activity里的View。

    接着在onCreate的setContentView中,

    Activity.setContentView() 
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);        
        initWindowDecorActionBar();    
    }
    
    PhoneWindow.setContentView() 
    public void setContentView(int layoutResID) {
        ...    
        installDecor(); 
        ... 
    } 
    
    PhoneWindow.installDecor 
    private void installDecor() {    
        //根据不同的Theme,创建不同的DecorView,DecorView是一个FrameLayout 
    }
    

    这时只是创建了PhoneWindow,和DecorView,但目前二者也没有任何关系,产生联系的时刻是在ActivityThread.performResumeActivity中,再调用r.activity.performResume(),调用r.activity.makeVisible,将DecorView添加到当前的Window上。

    void makeVisible() {    
        if (!mWindowAdded) {        
            ViewManager wm = getWindowManager();        
            wm.addView(mDecor, getWindow().getAttributes());        
            mWindowAdded = true;    
        }    
        mDecor.setVisibility(View.VISIBLE);
    }
    

    WindowManager的addView的具体实现在WindowManagerImpl中,而WindowManagerImpl的addView又会调用WindowManagerGlobal.addView。

    WindowManagerGlobal.addView
    public void addView(View view, ViewGroup.LayoutParams params,            Display display, Window parentWindow) {
        ...
        ViewRootImpl root = new ViewRootImpl(view.getContext(), display);        
        view.setLayoutParams(wparams);    
        mViews.add(view);    
        mRoots.add(root);    
        mParams.add(wparams);        
        root.setView(view, wparams, panelParentView);
        ...
    }
    

    这个过程创建一个ViewRootImpl,并将之前创建的DecoView作为参数闯入,以后DecoView的事件都由ViewRootImpl来管理了,比如DecoView上添加View,删除View。ViewRootImpl实现了ViewParent这个接口,这个接口最常见的一个方法是requestLayout()。

    ViewRootImpl是个ViewParent,在DecoView添加的View时,就会将View中的ViewParent设为DecoView所在的ViewRootImpl,View的ViewParent相同时,理解为这些View在一个View链上。所以每当调用View的requestLayout()时,其实是调用到ViewRootImpl,ViewRootImpl会控制整个事件的流程。可以看出一个ViewRootImpl对添加到DecoView的所有View进行事件管理。

    他们的关系可以用下面一张图来大概表示,

     
    Activity,Window,ViewRootImpl,View关系图

    注意mView和DecoView是同级关系,由不同ViewRootImpl控制,不在同一个View链上,之间没有联系。这个类似于PopupWindow。

    问题解读

    第一个问题
    现在来看在Activity通过 getWindowManager().addView(mView,wl)和 addContentView(mView,wmParams)的区别。第一种情况会调用到WindowManagerGlobal.addView,这时会创建一个新的ViewRootImpl,和原来的DecoView不在一条View链上,所以它们之间的任何一个调用requestLayout()不会影响到另一个。而addContentView(mView,wmParams)是直接将mView添加到DecoView中,会使ViewRootImpl链下的所以View重绘。

    第二个问题
    Dialog在创建时会新建一个PhoneWindow,同时也会使用DecoView作为这个PhoneWindow的根View,相当于走了一遍Activity里创建PhoneWindow和DecoView的流程,而调用Dialog的show方法时,类似于ActivityThread.performResumeActivity,将DecoView添加到Window,同时创建管理DecoView链的RootViewImpl来管理DecoView。PopupWindow就和第一个问题中 getWindowManager().addView(mView,wl)类似了,只是创建一条新的View链和ViewRootImpl,并没有创建新的Window。而Dialog通过非Activity的Context,如Application 和 Service,这是因为Dialog通过传入的Context来得到context里的mWindowManager(也就是WindowManagerImpl)与mToken,这是为了表明Dialog所属的Activity,在Window.addView时,需要这个mToken(IBinder对象),而Application 和 Service传入的情况下Token是null。

    第三个问题
    上面的分析可看出,ViewRootImpl是实际管理Window中所以View的类,每个Activity中ViewRootImpl数量取决于调用mWindowManager.addView的调用次数。

    第四个问题
    Activity提供和WMS通信的Token(IBinder对象),DecoView结合ViewRootImpl来管理同一View链(有相同的ParentView的View,ViewRootImpl也就是ParentView)的所以View的事件,绘制等。那Window的意义在哪?虽然Window也就是PhoneWindow没有具体做什么,但Window把Activity从View的一些创建,管理以及和ViewRootImpl的交互中脱离出来,让Activity与View尽量解耦,要不然这些工作都要放在Activity中午处理,Activity的任务就会变得更杂更重。为什么不能用一个ViewGroup比如DecoView来管理所有的View呢,因为一个Activity可能有不止一条View链,总要有一个进行管理的地方。View的意义就是将Activity和View的繁琐工作中脱离出来。

    总结

    一句话总结:Activity提供与AMS通信的Token(IBinder对象),创建Window为View提供显示的地方,而具体的View管理任务由ViewRootImpl来完成

    读别人的博客有个很大的好处是可以快速定位到关键点,但具体想要真正的理解还需要自己趣深入研究。

    每个Window都对应一个View和一个ViewRootImplWindowView通过ViewRootImpl来建立联系。Window不可见,它实际以View的形式存在,它是View的直接管理者

    这点从ViewManager(WindowManager extends ViewManager的定义也可以看得出来,它只提供三个接口方法:addViewupdateViewLayoutremoveView,这些方法都是针对View的。这说明View才是Window存在的实体。



    作者:SilenceDut
    链接:https://www.jianshu.com/p/c223b993b1ec
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    C/C++ 编程中的内存屏障(Memory Barriers) (1)
    ubuntu 10.04 源
    内存屏障(经典)
    VMware设置桥接上网
    C/C++ 编程中的内存屏障(Memory Barriers) (2)
    寒假Day55:指针
    寒假Day54:poj2378Tree Cutting没用树形dp写的树的题dfs
    寒假Day50:CodeForces1324CFrog Jumps思维
    寒假Day50:51nod3047位移运算
    寒假Day53:Codeforces519B水题
  • 原文地址:https://www.cnblogs.com/wytiger/p/12952231.html
Copyright © 2020-2023  润新知