服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - 理性分析 Window、Activity、DecorView 以及 ViewRoot 之间关系

理性分析 Window、Activity、DecorView 以及 ViewRoot 之间关系

2021-11-08 22:46Android开发编程 Android

ViewRoot并不属于View树的一部分。从源码实现上来看,它既非View的子类,也非View Group,但它实现了ViewParent接口,这让它可以作为View的名义上的父视图。

理性分析 Window、Activity、DecorView 以及 ViewRoot 之间关系

前言

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所设置的布局文件其实就是被加到内容栏之中的;
  1. ViewGroupcontent=(ViewGroup)findViewById(android.R.id.content);
  2. 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进行分发的;

理性分析 Window、Activity、DecorView 以及 ViewRoot 之间关系

二、DecorView的创建整个流程详解

1、attach

Activity的setContentView()开始

  1. publicvoidsetContentView(@LayoutResintlayoutResID){
  2. getWindow().setContentView(layoutResID);
  3. initWindowDecorActionBar();
  4. }

可以看到实际上是交给Window来装载视图的;

  1. finalvoidattach(Contextcontext,ActivityThreadaThread,
  2. Instrumentationinstr,IBindertoken,intident,
  3. Applicationapplication,Intentintent,ActivityInfoinfo,
  4. CharSequencetitle,Activityparent,Stringid,
  5. NonConfigurationInstanceslastNonConfigurationInstances,
  6. Configurationconfig,Stringreferrer,IVoiceInteractorvoiceInteractor,
  7. Windowwindow){
  8. ..................................................................
  9. mWindow=newPhoneWindow(this,window);//创建一个Window对象
  10. mWindow.setWindowControllerCallback(this);
  11. mWindow.setCallback(this);//设置回调,向Activity分发点击或状态改变等事件
  12. mWindow.setOnWindowDismissedCallback(this);
  13. .................................................................
  14. mWindow.setWindowManager(
  15. (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
  16. mToken,mComponent.flattenToString(),
  17. (info.flags&ActivityInfo.FLAG_HARDWARE_ACCELERATED)!=0);//给Window设置WindowManager对象
  18. ....................................................................
  19. }

在Activity的attach方法中生成了PhoneWindow的实例;

有了Window对象,接下来就将DecorView加载到Window中;

2、setContentView

  1. publicvoidsetContentView(intlayoutResID){
  2. if(mContentParent==null){//mContentParent为空,创建一个DecroView
  3. installDecor();
  4. }else{
  5. mContentParent.removeAllViews();//mContentParent不为空,删除其中的View
  6. }
  7. mLayoutInflater.inflate(layoutResID,mContentParent);//为mContentParent添加子View,即Activity中设置的布局文件
  8. finalCallbackcb=getCallback();
  9. if(cb!=null&&!isDestroyed()){
  10. cb.onContentChanged();//回调通知,内容改变
  11. }
  12. }

mContentParent就是ContentView所对应的的FrameLayout;

Activity的setContentView的流程大致可以总结为:

Activity首先在Attach方法中生成了PhoneWindow的实例;

在setContentView中直接交给Window来装载视图,先在PhoneWindow中创建了一个DecroView;

其中创建的过程中可能根据Theme不同,加载不同的布局格式,即Activity中设置的布局;

3、installDecor

  1. privatevoidinstallDecor(){
  2. if(mDecor==null){
  3. mDecor=generateDecor();//生成DecorView
  4. mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
  5. mDecor.setIsRootNamespace(true);
  6. if(!mInvalidatePanelMenuPosted&&mInvalidatePanelMenuFeatures!=0){
  7. mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
  8. }
  9. }
  10. if(mContentParent==null){
  11. mContentParent=generateLayout(mDecor);//为DecorView设置布局格式,并返回mContentParent
  12. ...
  13. }
  14. }
  15. }
  16. protectedDecorViewgenerateDecor(){
  17. returnnewDecorView(getContext(),-1);
  18. }

很简单,创建了一个DecorView;

再看generateLayout;

4、generateLayout

  1. protectedViewGroupgenerateLayout(DecorViewdecor){
  2. //从主题文件中获取样式信息
  3. TypedArraya=getWindowStyle();
  4. ...................
  5. if(a.getBoolean(R.styleable.Window_windowNoTitle,false)){
  6. requestFeature(FEATURE_NO_TITLE);
  7. }elseif(a.getBoolean(R.styleable.Window_windowActionBar,false)){
  8. //Don'tallowanactionbarifthereisnotitle.
  9. requestFeature(FEATURE_ACTION_BAR);
  10. }
  11. ................
  12. //根据主题样式,加载窗口布局
  13. intlayoutResource;
  14. intfeatures=getLocalFeatures();
  15. //System.out.println("Features:0x"+Integer.toHexString(features));
  16. if((features&(1<
  17. layoutResource=R.layout.screen_swipe_dismiss;
  18. }elseif(...){
  19. ...
  20. }
  21. Viewin=mLayoutInflater.inflate(layoutResource,null);//加载layoutResource
  22. //往DecorView中添加子View,即文章开头介绍DecorView时提到的布局格式,那只是一个例子,根据主题样式不同,加载不同的布局。
  23. decor.addView(in,newViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));
  24. mContentRoot=(ViewGroup)in;
  25. ViewGroupcontentParent=(ViewGroup)findViewById(ID_ANDROID_CONTENT);//这里获取的就是mContentParent
  26. if(contentParent==null){
  27. thrownewRuntimeException("Windowcouldn'tfindcontentcontainerview");
  28. }
  29. if((features&(1<
  30. ProgressBarprogress=getCircularProgressBar(false);
  31. if(progress!=null){
  32. progress.setIndeterminate(true);
  33. }
  34. }
  35. if((features&(1<
  36. registerSwipeCallbacks();
  37. }
  38. //Remainingsetup--ofbackgroundandtitle--thatonlyapplies
  39. //totop-levelwindows.
  40. ...
  41. returncontentParent;
  • 先从主题中获取样式,然后根据样式;
  • 加载对应的布局到DecorView中,然后从中获取mContentParent;
  • 获得到之后,可以回到上面的代码,为mContentParent添加View,即Activity中的布局;

5、DecorView的显示

将DecorView建立起来,通过setContentView设置的界面,如何在onResume后对用户可见,需要从ActivityThread说起;

  1. privatevoidhandleLaunchActivity(ActivityClientRecordr,IntentcustomIntent){
  2. //就是在这里调用了Activity.attach(),接着调用了Activity.onCreate()和Activity.onStart()生命周期,
  3. //但是由于只是初始化了mDecor,添加了布局文件,还没有把
  4. //mDecor添加到负责UI显示的PhoneWindow中,所以这时候对用户来说,是不可见的
  5. Activitya=performLaunchActivity(r,customIntent);
  6. ......
  7. if(a!=null){
  8. //这里面执行了Activity.onResume()
  9. handleResumeActivity(r.token,false,r.isForward,
  10. !r.activity.mFinished&&!r.startsNotResumed);
  11. if(!r.activity.mFinished&&r.startsNotResumed){
  12. try{
  13. r.activity.mCalled=false;
  14. //执行Activity.onPause()
  15. mInstrumentation.callActivityOnPause(r.activity);
  16. }
  17. }
  18. }
  19. }

重点看下handleResumeActivity(),在这其中,DecorView将会显示出来,同时重要的一个角色;ViewRoot也将登场;

6、handleResumeActivity

  1. finalvoidhandleResumeActivity(IBindertoken,booleanclearHide,
  2. booleanisForward,booleanreallyResume){
  3. //这个时候,Activity.onResume()已经调用了,但是现在界面还是不可见的
  4. ActivityClientRecordr=performResumeActivity(token,clearHide);
  5. if(r!=null){
  6. finalActivitya=r.activity;
  7. if(r.window==null&&!a.mFinished&&willBeVisible){
  8. r.window=r.activity.getWindow();
  9. Viewdecor=r.window.getDecorView();
  10. //decor对用户不可见
  11. decor.setVisibility(View.INVISIBLE);
  12. ViewManagerwm=a.getWindowManager();
  13. WindowManager.LayoutParamsl=r.window.getAttributes();
  14. a.mDecor=decor;
  15. l.type=WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
  16. if(a.mVisibleFromClient){
  17. a.mWindowAdded=true;
  18. //被添加进WindowManager了,但是这个时候,还是不可见的
  19. wm.addView(decor,l);
  20. }
  21. if(!r.activity.mFinished&&willBeVisible
  22. &&r.activity.mDecor!=null&&!r.hideForNow){
  23. //在这里,执行了重要的操作,使得DecorView可见
  24. if(r.activity.mVisibleFromClient){
  25. r.activity.makeVisible();
  26. }
  27. }
  28. }
  29. }
  30. }

当我们执行了Activity.makeVisible()方法之后,界面才对我们是可见的;

  1. voidmakeVisible(){
  2. if(!mWindowAdded){
  3. ViewManagerwm=getWindowManager();
  4. wm.addView(mDecor,getWindow().getAttributes());//将DecorView添加到WindowManager
  5. mWindowAdded=true;
  6. }
  7. mDecor.setVisibility(View.VISIBLE);//DecorView可见
  8. }
  • 到此DecorView便可见,显示在屏幕中;
  • 但是在这其中,wm.addView(mDecor, getWindow().getAttributes());
  • 起到了重要的作用,因为其内部创建了一个ViewRootImpl对象,负责绘制显示各个子View;
  • 具体来看addView()方法,因为WindowManager是个接口,具体是交给WindowManagerImpl来实现的;

7、addView

  1. publicfinalclassWindowManagerImplimplementsWindowManager{
  2. privatefinalWindowManagerGlobalmGlobal=WindowManagerGlobal.getInstance();
  3. ...
  4. @Override
  5. publicvoidaddView(Viewview,ViewGroup.LayoutParamsparams){
  6. mGlobal.addView(view,params,mDisplay,mParentWindow);
  7. }
  8. }
  9. 交给WindowManagerGlobal的addView()方法去实现;
  10. publicvoidaddView(Viewview,ViewGroup.LayoutParamsparams,
  11. Displaydisplay,WindowparentWindow){
  12. finalWindowManager.LayoutParamswparams=(WindowManager.LayoutParams)params;
  13. ......
  14. synchronized(mLock){
  15. ViewRootImplroot;
  16. //实例化一个ViewRootImpl对象
  17. root=newViewRootImpl(view.getContext(),display);
  18. view.setLayoutParams(wparams);
  19. mViews.add(view);
  20. mRoots.add(root);
  21. mParams.add(wparams);
  22. }
  23. ......
  24. try{
  25. //将DecorView交给ViewRootImpl
  26. root.setView(view,wparams,panelParentView);
  27. }catch(RuntimeExceptione){
  28. }
  29. }
  • 看到其中实例化了ViewRootImpl对象,然后调用其setView()方法;
  • 其中setView()方法经过一些列折腾,最终调用了performTraversals()方法,完成绘制,最终界面才显示出来;

理性分析 Window、Activity、DecorView 以及 ViewRoot 之间关系

总结

  • Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图;
  • DecorView就是个顶层视图,是所有View的最外层布局;
  • ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互;

原文链接:https://mp.weixin.qq.com/s/JJIyRXwaendpsY7_NsbDaQ

延伸 · 阅读

精彩推荐