Android视图框架Activity,Window,View,

作者: SilenceDut | 来源:发表于2016-08-16 19:12 被阅读3623次

    关于Activity,Window,View的关系一直有个模糊的印象,看别人的分析一般都这么理解Activity是管理Window,Window用来承载View,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来完成

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

    相关文章

      网友评论

      • 小周爱吃瓜:第四个问题 倒数第二句 view的意思在于...这个view写错了吧
      • 薛瑄:第二个问题的回答有句话错误了,“Dialog通过非Activity的Context,如Application 和 Service。”改为“Dialog的参数不能传递非Activity的Context,如Application 和 Service”
      • sankemao:终于知道ViewRootImp干嘛的啦
      • and_pu:view是在Activity中的onCreate方法中开始的吗?
        如果不是,那是在那个方法被调用的时候view才开始绘制呢?求大神指导!
        搬砖写Bug:view 的绘制应该在viewrootimpl.java 的requestLayout() 方法中触发的
        SilenceDut:去我博客里看吧http://www.silencedut.com/,可能有更新

      本文标题:Android视图框架Activity,Window,View,

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