前言在Activity显示View的过程中,有一些重要的角色总是令人困惑,比如PhoneWindow、DecorView、ViewRootImpl。也经常有面试题问到他们四人的关系?创作时间?第一次绘制View的时间?等问题。那么今天就和大家一起来看看,从Activity的启动到View的显示,会经历哪些步骤,角色之间又是怎样的关系。动画展示为了让大家更容易理解,我先用动画的形式给大家展示一下这几个人的关系:从小爱同学的诞生开始的源码分析。在Activity界面显示出来之前,它还是一个我们看不到的Activity。爱称——小爱。小爱是怎样诞生的?熟悉Activity的启动流程的都知道,小爱同学的创建发生在performLaunchActivity中:try{java.lang.ClassLoadercl=appContext.getClassLoader();//创建Activityactivity=mInstrumentation.newActivity(cl,component.getClassName(),r.intent);}try{if(activity!=null){//完成activity一些重要数据的初始化activity.attach(appContext,this,getInstrumentation(),r.token,r.ident,app,r.intent,r.activityInfo,title,r.parent,r.embeddedID,r.lastNonConfigurationInstances,config,r.referrer,r.voiceInteractor,window,r.configCallback,r.assistToken);//调用activity的onCreate方法if(r.isPersistable()){mInstrumentation.callActivityOnCreate(activity,r.state,r.persistentState);}else{mInstrumentation.callActivityOnCreate(activity,r.state);}}}returnactivity;}这个过程主要做了三件事:实例化Activity,调用attach方法进行初始化。调用onCreate方法开始从布局文件加载布局并使View显示众所周知,小爱创建后,事务繁忙,肯定无法自己管理每个View,所以他找了帮手,帮助她与View交互,管理View。(Activity和View的解耦)这个helper是什么?就是窗口Window,也就是PhoneWindow的实现类。这个过程发生在attach方法中://Activity.javafinalvoidattach(){//CreatePhoneWindowmWindow=newPhoneWindow(this,window,activityConfigCallback);mWindow.setCallback(this);mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken,mComponent.flattenToString(),(info.flags&ActivityInfo.FLAG_HARDWARE_ACCELERATED)!=0);}为了方便记忆,我们称这个PhoneWindow管家为窗口管家。使用窗口管理器加载布局文件(DecorView)后,您可以继续使用onCreate方法。onCreate方法中最重要的是setContentView方法。通过setContentView可以加载布局文件中的View。之前说过,View相关的管理工作交给了窗口管家,所以我直接调用了PhoneWindow的setContentView方法:);}然后开始加载布局文件。但是考虑一点,Activity有不同的主题,不同的主题有不同的布局结构。所以在加载我们自己设置的布局文件之前,设置一个顶层View作为所有View的老大。而这个顶层View就是DecorView。为了方便起见,我称他为一哥,简称为哥。看看DecorView小哥哥是如何创建的://PhoneWindow.java@OverridepublicvoidsetContentView(intlayoutResID){if(mContentParent==null){installDecor();}if(!hasFeature(FEATURE_CONTENT_TRANSITIONS)){mLayoutInflater.inflate(layoutResID,mContentParent);}}privatevoidinstallDecor(){if(mDecor==null){mDecor=generateDecor(-1);}else{mDecor.setWindow(this);}if(mContentParent==null){mContentParent=generateLayout(mDecor);}}protectedDecorViewgenerateDecor(intfeatureId){returnnewDecorView(context,featureId,this,getAttributes());}就这样,小弟DecorView就创建好了,接下来就是小弟干活的时候了。上面说了DecorView小弟创建的目的是什么?根据不同的主题设置不同的布局结构,这个工作发生在generateLayout方法中,我们今天就不分析了。看来小弟的工作也完成了?等等,应用本身的布局还没有加载,重要的事情还没有开始。回到上面的setContentView方法,调用installDecor方法创建小弟后,又做了一件事情://加载xml布局文件mLayoutInflater.inflate(layoutResID,mContentParent);publicViewinflate(@LayoutResintresource,@NullableViewGrouproot,booleanattachToRoot){finalResourcesres=getContext().getResources();finalXmlResourceParserparser=res.getLayout(resource);try{returninflate(parser,root,attachToRoot);}finally{parser.close();}}并且这个inflate是加载我们熟悉布局文件的方法。传入xml布局文件,解析组合我们传入的父视图——mContentParent,转化为完整的树结构,最后返回顶层View。至此,setContentView的工作就完成了。简单的说就是创建小弟DecorView,结合顶层视图和我们传入的xml布局文件,生成多层View。说明这个View(ViewRootImpl)View可用,结构敲定。接下来就是如何显示这个View结构,让我们的手机可以显示屏幕了?是的,它在画画。View的绘图工作委托给谁比较好?召回现有成员:小爱同学活动:大boss,负责统筹即可。窗口管家PhoneWindow:负责管理各个View。DecorView小弟:最顶层的View,负责显示主题布局。好像没有人可以负责View的绘制?画画这么重要,只好再招一个朋友。ViewRootImpl就在现场。为了方便,我称他为小伟。小微是什么时候创建的?然后看Activity的调用过程。onCreate被调用后,onResume方法会被调用,它从handleResumeActivity方法开始。@OverridepublicvoidhandleResumeActivity(){//onResumefinalActivityClientRecordr=performResumeActivity(token,finalStateRequest,reason);//addViewif(r.window==null&&!a.mFinished&&willBeVisible){r.window=r.activity.getWindow();Viewdecor=r.window.getDecorView();ViewManagerwm=a.getWindowManager();WindowManager.LayoutParamsl=r.window.getAttributes()wm.addView(decor,l);}这个方法主要做了两件事:调用onResume方法调用WMaddView方法。小唯好像还没出来?继续看addView方法://WindowManagerGlobal.javapublicvoidaddView(){synchronized(mLock){root=newViewRootImpl(view.getContext(),display);view.setLayoutParams(wparams);mViews.add(view);mRoots。添加(根);mParams.add(wparams);try{root.setView(view,wparams,panelParentView);}}}publicViewRootImpl(Contextcontext,Displaydisplay){mContext=context;mWindowSession=WindowManagerGlobal.getWindowSession();mThread=Thread.currentThread();}最后,小微的ViewRootImpl也创建好了,而在这个ViewRootImpl中,有两个变量值得关注:mWindowSession。类型为IWindowSession,是一个用于进程间通信的Binder对象。它在服务器端的实现是Session,可以用来完成WMS相关的工作。线程。thread变量设置为当前线程,也就是实例化ViewRootImpl时的线程。一般不同线程更新UI时,会判断当前线程和mThread是否相等,不同则抛出异常。下一步是调用ViewRootImpl的setView方法。这个方法自然是小微ViewRootImpl的做法://ViewRootImpl.javapublicvoidsetView(){synchronized(this){//DrawrequestLayout();//调用WMS的addWindow方法res=mWindowSession.addToDisplay(mWindow,mSeq,mWindowAttributes,getHostVisibility(),mDisplay.getDisplayId(),mWinFrame,mAttachInfo.mContentInsets,mAttachInfo.mStableInsets,mAttachInfo.mOutsets,mAttachInfo.mDisplayCutout/RotnmelInput设置为)(设置为)parentview.assignParent(this);}}ofview(decorView)主要有三个功能:触发绘图(具体包括测量、布局、绘图)mLayoutRequested=true;scheduleTraversals();}}->scheduleTraversals()->performMeasure()performLayout()performDraw()->measure、layout、draw方法通过Binder调用WMS的addWindow方法。方法,为窗口分配一个Surface,这个Surface负责显示最终的界面,最终会被绘制到屏幕上。将ViewRootImpl设置为decorView的parent后,当子view请求绘制(requestLayout)时,总能通过parent找到ViewRootImpl,然后由ViewRootImpl负责所有View的绘制工作。整个调用过程是://View.javapublicvoidrequestLayout(){if(mParent!=null&&!mParent.isLayoutRequested()){mParent.requestLayout();}}总结至此,Activity终于完成了它的启动生命周期,界面上也显示小爱同学也变成了成型的Activity。其实不难发现,中间角色虽然很多,但是每个角色都是缺一不可的:PhoneWindow是因为需要管理View而创建的;PhoneWindow是因为需要管理View;根ViewDecorView是因为需要根据主题显示不同的布局结构而创建的;处理View的各种事件,包括绘制和事件分发,创建ViewRootImpl。每个人都忙于自己的工作,服从Activity。本文转载自微信公众号“积木上的代码”,您可以通过以下二维码关注。转载本文请联系代码上的积木公众号。
