前言
Activity和window,DecorView ,viewRoot是什么关系
今天我们就来讲解下,这样你在面试时候,游刃有余;
一、基本概念介绍
1、Activity
- Activity负责控制生命周期和处理事件;
- 负责统筹视图的添加与显示,以及通过一些回调方法与Window和View进行交互;
- 一个Activity包含一个Window,真正控制视图的是Window,Window才是真正代表一个窗口;
- 统筹视图的添加与显示,通过回调与Window和View进行交互;
2、Window
- Window是视图的承载者,是一个抽象类;
- Activity中持有的实际上是Window的子类PhoneWindow;
- Window通过WindowManager加载了一个DecorView到Window中,并将DecorView交给了ViewRoot;
3、DecorView
- DecorView的父类是FrameLayout,是Android View树的根节;
- 内部包含一个竖直方向的LinearLayout,它有上下三个部分,上面是个ViewStub,延迟加载的视图(ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。setContentView所设置的布局文件其实就是被加到内容栏之中的;
- ViewGroupcontent=(ViewGroup)findViewById(android.R.id.content);
- ViewGrouprootView=(ViewGroup)content.getChildAt(0)
4、ViewRoot
- 控制View的事件处理和逻辑处理;
- ViewRoot子类是ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成;
- ViewRoot并不属于View树的一部分。从源码实现上来看,它既非View的子类,也非View Group,但它实现了ViewParent接口,这让它可以作为View的名义上的父视图;
- RootView继承了Handler类,可以接收事件并分发;
- Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的;
二、DecorView的创建整个流程详解
1、attach
Activity的setContentView()开始
- publicvoidsetContentView(@LayoutResintlayoutResID){
- getWindow().setContentView(layoutResID);
- initWindowDecorActionBar();
- }
可以看到实际上是交给Window来装载视图的;
- finalvoidattach(Contextcontext,ActivityThreadaThread,
- Instrumentationinstr,IBindertoken,intident,
- Applicationapplication,Intentintent,ActivityInfoinfo,
- CharSequencetitle,Activityparent,Stringid,
- NonConfigurationInstanceslastNonConfigurationInstances,
- Configurationconfig,Stringreferrer,IVoiceInteractorvoiceInteractor,
- Windowwindow){
- ..................................................................
- mWindow=newPhoneWindow(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的attach方法中生成了PhoneWindow的实例;
有了Window对象,接下来就将DecorView加载到Window中;
2、setContentView
- publicvoidsetContentView(intlayoutResID){
- if(mContentParent==null){//mContentParent为空,创建一个DecroView
- installDecor();
- }else{
- mContentParent.removeAllViews();//mContentParent不为空,删除其中的View
- }
- mLayoutInflater.inflate(layoutResID,mContentParent);//为mContentParent添加子View,即Activity中设置的布局文件
- finalCallbackcb=getCallback();
- if(cb!=null&&!isDestroyed()){
- cb.onContentChanged();//回调通知,内容改变
- }
- }
mContentParent就是ContentView所对应的的FrameLayout;
Activity的setContentView的流程大致可以总结为:
Activity首先在Attach方法中生成了PhoneWindow的实例;
在setContentView中直接交给Window来装载视图,先在PhoneWindow中创建了一个DecroView;
其中创建的过程中可能根据Theme不同,加载不同的布局格式,即Activity中设置的布局;
3、installDecor
- privatevoidinstallDecor(){
- if(mDecor==null){
- mDecor=generateDecor();//生成DecorView
- mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
- mDecor.setIsRootNamespace(true);
- if(!mInvalidatePanelMenuPosted&&mInvalidatePanelMenuFeatures!=0){
- mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
- }
- }
- if(mContentParent==null){
- mContentParent=generateLayout(mDecor);//为DecorView设置布局格式,并返回mContentParent
- ...
- }
- }
- }
- protectedDecorViewgenerateDecor(){
- returnnewDecorView(getContext(),-1);
- }
很简单,创建了一个DecorView;
再看generateLayout;
4、generateLayout
- protectedViewGroupgenerateLayout(DecorViewdecor){
- //从主题文件中获取样式信息
- TypedArraya=getWindowStyle();
- ...................
- if(a.getBoolean(R.styleable.Window_windowNoTitle,false)){
- requestFeature(FEATURE_NO_TITLE);
- }elseif(a.getBoolean(R.styleable.Window_windowActionBar,false)){
- //Don'tallowanactionbarifthereisnotitle.
- requestFeature(FEATURE_ACTION_BAR);
- }
- ................
- //根据主题样式,加载窗口布局
- intlayoutResource;
- intfeatures=getLocalFeatures();
- //System.out.println("Features:0x"+Integer.toHexString(features));
-
if((features&(1<
- layoutResource=R.layout.screen_swipe_dismiss;
- }elseif(...){
- ...
- }
- Viewin=mLayoutInflater.inflate(layoutResource,null);//加载layoutResource
- //往DecorView中添加子View,即文章开头介绍DecorView时提到的布局格式,那只是一个例子,根据主题样式不同,加载不同的布局。
- decor.addView(in,newViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));
- mContentRoot=(ViewGroup)in;
- ViewGroupcontentParent=(ViewGroup)findViewById(ID_ANDROID_CONTENT);//这里获取的就是mContentParent
- if(contentParent==null){
- thrownewRuntimeException("Windowcouldn'tfindcontentcontainerview");
- }
-
if((features&(1<
- ProgressBarprogress=getCircularProgressBar(false);
- if(progress!=null){
- progress.setIndeterminate(true);
- }
- }
-
if((features&(1<
- registerSwipeCallbacks();
- }
- //Remainingsetup--ofbackgroundandtitle--thatonlyapplies
- //totop-levelwindows.
- ...
- returncontentParent;
- 先从主题中获取样式,然后根据样式;
- 加载对应的布局到DecorView中,然后从中获取mContentParent;
- 获得到之后,可以回到上面的代码,为mContentParent添加View,即Activity中的布局;
5、DecorView的显示
将DecorView建立起来,通过setContentView设置的界面,如何在onResume后对用户可见,需要从ActivityThread说起;
- privatevoidhandleLaunchActivity(ActivityClientRecordr,IntentcustomIntent){
- //就是在这里调用了Activity.attach(),接着调用了Activity.onCreate()和Activity.onStart()生命周期,
- //但是由于只是初始化了mDecor,添加了布局文件,还没有把
- //mDecor添加到负责UI显示的PhoneWindow中,所以这时候对用户来说,是不可见的
- Activitya=performLaunchActivity(r,customIntent);
- ......
- if(a!=null){
- //这里面执行了Activity.onResume()
- handleResumeActivity(r.token,false,r.isForward,
- !r.activity.mFinished&&!r.startsNotResumed);
- if(!r.activity.mFinished&&r.startsNotResumed){
- try{
- r.activity.mCalled=false;
- //执行Activity.onPause()
- mInstrumentation.callActivityOnPause(r.activity);
- }
- }
- }
- }
重点看下handleResumeActivity(),在这其中,DecorView将会显示出来,同时重要的一个角色;ViewRoot也将登场;
6、handleResumeActivity
- finalvoidhandleResumeActivity(IBindertoken,booleanclearHide,
- booleanisForward,booleanreallyResume){
- //这个时候,Activity.onResume()已经调用了,但是现在界面还是不可见的
- ActivityClientRecordr=performResumeActivity(token,clearHide);
- if(r!=null){
- finalActivitya=r.activity;
- if(r.window==null&&!a.mFinished&&willBeVisible){
- r.window=r.activity.getWindow();
- Viewdecor=r.window.getDecorView();
- //decor对用户不可见
- decor.setVisibility(View.INVISIBLE);
- ViewManagerwm=a.getWindowManager();
- WindowManager.LayoutParamsl=r.window.getAttributes();
- a.mDecor=decor;
- l.type=WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
- if(a.mVisibleFromClient){
- a.mWindowAdded=true;
- //被添加进WindowManager了,但是这个时候,还是不可见的
- wm.addView(decor,l);
- }
- if(!r.activity.mFinished&&willBeVisible
- &&r.activity.mDecor!=null&&!r.hideForNow){
- //在这里,执行了重要的操作,使得DecorView可见
- if(r.activity.mVisibleFromClient){
- r.activity.makeVisible();
- }
- }
- }
- }
- }
当我们执行了Activity.makeVisible()方法之后,界面才对我们是可见的;
- voidmakeVisible(){
- if(!mWindowAdded){
- ViewManagerwm=getWindowManager();
- wm.addView(mDecor,getWindow().getAttributes());//将DecorView添加到WindowManager
- mWindowAdded=true;
- }
- mDecor.setVisibility(View.VISIBLE);//DecorView可见
- }
- 到此DecorView便可见,显示在屏幕中;
- 但是在这其中,wm.addView(mDecor, getWindow().getAttributes());
- 起到了重要的作用,因为其内部创建了一个ViewRootImpl对象,负责绘制显示各个子View;
- 具体来看addView()方法,因为WindowManager是个接口,具体是交给WindowManagerImpl来实现的;
7、addView
- publicfinalclassWindowManagerImplimplementsWindowManager{
- privatefinalWindowManagerGlobalmGlobal=WindowManagerGlobal.getInstance();
- ...
- @Override
- publicvoidaddView(Viewview,ViewGroup.LayoutParamsparams){
- mGlobal.addView(view,params,mDisplay,mParentWindow);
- }
- }
- 交给WindowManagerGlobal的addView()方法去实现;
- publicvoidaddView(Viewview,ViewGroup.LayoutParamsparams,
- Displaydisplay,WindowparentWindow){
- finalWindowManager.LayoutParamswparams=(WindowManager.LayoutParams)params;
- ......
- synchronized(mLock){
- ViewRootImplroot;
- //实例化一个ViewRootImpl对象
- root=newViewRootImpl(view.getContext(),display);
- view.setLayoutParams(wparams);
- mViews.add(view);
- mRoots.add(root);
- mParams.add(wparams);
- }
- ......
- try{
- //将DecorView交给ViewRootImpl
- root.setView(view,wparams,panelParentView);
- }catch(RuntimeExceptione){
- }
- }
- 看到其中实例化了ViewRootImpl对象,然后调用其setView()方法;
- 其中setView()方法经过一些列折腾,最终调用了performTraversals()方法,完成绘制,最终界面才显示出来;
总结
- Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图;
- DecorView就是个顶层视图,是所有View的最外层布局;
- ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互;
原文链接:https://mp.weixin.qq.com/s/JJIyRXwaendpsY7_NsbDaQ