1startActivity启动过程
假设从Activity A中跳转到另一个App中的Activity B,过程如下图所示:
整个startActivity的流程分为3部分,也涉及3个进程之间的交互:
1.Activity A 到 AMS;
2.AMS 到 进程B中的ApplicationThread;
3.ApplicationThread 到 Activity B;
1.1 Activity A到AMS阶段
这一过程并不复杂,用一张图表示具体过程如下:
下面具体看看源码:
调用startActivity后,最终会调用到startActivityForResult方法,在startActivityForResult中,调用Instrumentation.exeStartActivity方法,把Activity的跳转交给类Instrumentation类去处理。
而在Instrumentation中,会通过ActivityManager.getService获取AMS的实例,然后调用其startActivity方法,至此,startActivity的工作重心从Activity A转移到类系统进程AMS中。
1.2AMS到ApplicationThread
这一步的流程看起来很负责,实际上就干了两件事:
1.综合处理launchMode和Intent中的Flag标志位,并根据处理结果生成一个目标Activity B的对象(ActivityRecord);
2.判断是否需要位目标Activity B创建一个新的进程(ProcessRecord)、新的任务栈(TaskRecord)。
从AMS的startActivity看起:
经过多个方法的调用,最终通过obtainStarter方法获取了ActivityStarter类型的对象(ActivityStarter类是专门负责一个Activity的启动操作,主要作用包括解析Intent、创建ActivityRecord、创建TaskRecord等),然后调用其execute方法,在execute方法中,会再次调用其内部的startActivityMayWait方法:
从上图可以看出,获取目标Activity信息的操作由mSupervisor来实现,它是ActivityStackSupervisor类型,是负责Activity所处栈的管理类。在startActivityMayWait中调用类一个重载的startActivity方法,而最终会调用ActivityStarter中的startActivityUnchecked方法来获取启动Activity的结果。然后调用到realStartActivityLocked方法,在这个方法中,调用启动事物ClientTransaction的schedule方法,而这个transaction实际上是在创建ClientTransaction时传入的app.thread对象,也就是ApplicationThread。
1.3ApplicationThread到Activity
从ApplicationThread的scheduleTransaction方法开始,调用了ActivityThread中的scheduleTransaction方法,这个方法实际上实在ActivityThread的父类ClientTransactionHandler中实现的,具体代码如下:
调用sendMessage方法,向Handler中发送了一个EXECUTE_TRANSACTION的消息,并且Message中的obj就是启动Activity的事务对象,而这个Handler的具体实现是ActivityThread中的mH对象,代码如下:
最终调用了事务的execute方法,然后调用ActivityThread中的handleLaunchActivity方法:
1初始化Activity的WindowManager,每一个Activity都会对应一个窗口;
2调用performLauncheActivity创建并显示Activity
3通过反射创建目标Activity 对象
4调用attach方法建立Activity与Context之间的联系,创建PhoneWindow对象,并与Activity进行关联操作
5通过Instrumentation最终调用Activity的onCreate方法
2底层剖析Window、Activity、View三者关系
当我们需要显示Activity布局时,只需要在Activity 中添加一行setContentView即可,查看Activity中的源码如下:
可以看到,其实Activity几乎什么都每做,而是将操作直接交给Window处理的,getWindow返回的是Activity中的全局变量mWindow,是一个Window类型,它的赋值是在ActivityThread中的performLaunchActivity中,通过反射创建Activity对象后,执行attach方法,attach方法源码如下:
在Activity的attach方法中将一个PhoneWindow对象赋值给mWindow,接下来调用了setWindowManager方法将系统WindowManager传给PhoneWindow,如下图:
最终,在PhoneWindow中持有一个WindowManagerImpl的引用。
PhoneWindow的setContentView
Activity将setContentView的操作交给了PhoneWindow,接下来看看其实现过程:
图中1处判断mContentParent是否为空,为空则调用installDecor初始化DecorView和mContentParent
图中2处将传入的布局添加到mContentParent中。
可以看出,在PhoneWindow中默认有一个DecorView(是FrameLayout子类),在DecorView中默认自带一个mContentParent(ViewGroup类型),然后把xml中写的布局添加到mContentParent中,因此经过setContentView之后,PhoneWindow内部的View关系如下:
目前为止,PhoneWindow中只是创建类一个DecorView,并在DecorView中填充Activity中传入的布局,可是DecorView还没有跟Activity建立任何联系,也没有被绘制到界面上显示。我们知道,Activity执行到onCreate时并不可见,只有执行完onResume后Activity中的内容才是屏幕可见状态,这是因为在onCreate阶段只是初始化类Activity需要显示的内容,而在onResume阶段才会将PhoneWindow中的DecorView绘制到屏幕上。
在ActivityThread的handleResumeActivity中,会调用WindowManager的addView方法将DecorView添加到WMS,如下图:
WindowManager的addView结果有两个:
1.DecorView被渲染绘制到屏幕上显示;
2.DecorView可以接收屏幕触摸事件。
WindowManager的addView
PhoneWindow只是负责处理一些应用窗口通用的逻辑(设置标题栏,导航栏等),真正完成把一个View作为窗口添加到WMS的过程是由WindowManager来完成的。
WindowManager是接口类型,真正的实现者是WindowManagerImpl类,addView源码如下:
WindowManagerImpl也是一个空壳,它调用了WindowManagerGlobal的addView方法。WindowManagerGlobal是一个单例,每一个进程中只有一个实例对象,如上图红框所示,在addView方法中创建了一个最关键的ViewRootImpl对象,然后通过ViewRootImpl的setView方法将view添加到WMS。
ViewRootImpl的setView
图中1处的requestLayout是刷新布局的操作,调用此方法后ViewRootImpl所关联的View执行measure-layout-draw操作,确保View被添加到Window上显示到屏幕之前,已经完成测量和绘制操作。
图中2处调用mWindowSession的addToDisplay方法将View添加到WMS中。
WindowSession是WindowManagerGlobal中的单例对象,初始化代码如下:
sWindowSession实际上是IWindowSession类型,是一个Binder类型,真正的实现类是System进程中的Session。上图中红框中就是用AIDL获取System进程中Session的对象,addToDisplay方法如下:
图中的mService就是WMS,至此,Window已经成功的被传递给类WMS,剩下的工作就全部转移到系统system_server进程中的WMS来完成最终的添加操作。
再看Activity
上文提到addView成功有一个标志就是能够接收触碰事件,通过对setContentView流程的分析,可以看出添加View的操作实质上是PhoneWindow在全盘操作,背后负责人是WMS,而Activity没有任何参与。但是当触碰事件发生后,Touch事件首先是被传入到Activity,然后才被下发到布局中的ViewGroup和View,那么Touch事件是如何传递到Activity上的呢?
ViewRootImpl中的setView方法中,除了调用IWindowSession执行跨进程添加View之外,还有一项重要的操作就是设置输入事件的处理:
如上图红框中所示,设置了一系列的输入通道。一个触碰事件的发生是由屏幕发起,然后经过驱动层一系列的优化计算通过Socket跨进程通知AndroidFramework层(WMS),最终屏幕事件被发送到上图的输入管道中。
这些输入管道实际上是一个链表结构,当某一个屏幕触摸事件到达其中的ViewPostImeInpustState时,会经过onProcess来处理,如下:
可以看到在onProcess中最终调用来一个mView的dispatchPointerEvent方法,mView实际上就是PhoneWindow中的DecorView,而dispatchPointerEvent是被View.java实现的,如下:
最终调用来PhoneWindow中Callback的dispatchTouchEvent方法,这个Callback就是Activity,在启动Activity阶段,attach中有如下代码:
再看Activity的dispatchTouchEvent:
Touch事件在Activity中绕来一圈,最终回到PhoneWindow中的DecorView来处理。
小结
通过setContentView的流程,分析了Activity、Window、View之间的关系。整个过程Activity表面上参与度比较低,大部分View的添加操作都被封装到Window中实现。而Activity就相当于提供给开发人员的一个管理类,通过它能够更简单的实现Window和View的操作逻辑。以下几点需要注意:
1.一个Activity中有一个Window,也就是PhoneWindow对象,在PhoneWindow中有一个DecorView,在setContentView中会将layout填充到此DecorView中;
2.一个应用进程中只有一个WindowMnagerGlobal对象,因为在ViewRootImpl中它是static静态类型;
3.每一个PhoneWindow对应一个ViewRootImpl对象;
4.WindowManagerGlobal通过调用ViewRootImpl的setView方法,完成window的添加过程;
5.ViewRootImpl的setView方法中主要完成两件事情,View渲染(requestLayout)以及接收触屏事件。
3Android如何通过View进行渲染
上文介绍类Activity、Window和View之间的关系,了解了ViewRootImpl在整个过程中起到承上启下的作用。一方面ViewRootImpl中通过Binder通信机制,远程调用WindowSession将View添加到Window中,另一方面,ViewRootImpl在添加View之前,又需要调用requestLayout方法,执行完整的View树的渲染操作。
屏幕绘制
ViewRootImpl requestLayout流程
requestLayout第一次被调用是在setView中,从名字也能看出,这个方法的主要目的就是请求布局操作,其中包括View的测量、布局、绘制等
requestLayout源码如上图,注释1处检查是否为合法线程,一般情况下就是检查是否为主线程;
注释2处将请求布局标识符设置为true,这个参数决定了后续是否需要执行measure和layout操作;
最后执行scheduleTraversals方法,如下:
注释1处向主线程消息队列插入同步屏障消息,该方法发送一个没有target的Message到Queue中,在next方法中获取消息时,如果发现没有target的Message,则在一定的时间内跳过同步消息,优先执行异步消息,保证UI绘制操作优先执行。
注释2处调用Choregographer的postCallback方法,实际上也是发送一个Message到主线程消息队列。流程代码如下:
可以看出,最终通过Handler发送到MessageQueue中的Message被设置为异步消息。
mTraversalRunnable是一个实现Runnable接口的TraversalRunnable类型对象,其run方法如下:
在run方法中调用了doTraversal方法,并最终调用了performTraversals()方法,在这个方法里面会真正地开始View绘制流程:measure->layout->draw。
ViewRootImpl的performTraversals方法
这个方法比较重,源码有将近900行,但是抽取一下核心部分代码可以发现,这个方法只负责做3件事:
接下来以measure流程举例
我们知道View的测量是一层递归调用,递归执行子View的测量工作后,再最后决定父View的宽高,这个递归的起源就是DecorView。measureHierarchy方法源码如下:
这个方法中,通过getRootMeasureSpec方法获取了根View的MeasureSpec,实际上就是Window的宽高,然后调用performMeasure方法进行测量工作
ViewRootImpl的performMeasure
方法很简单,只是执行了mView的measure方法,这个mView就是DecorView,然后在DecorView的measure方法中会调用onMeasure方法,然后会递归调用子View的onMeasure方法。performLayout方法过程类似。
ViewRootImpl的performDraw
图中可以看出,在performDraw方法中,调用的是ViewRootImpl的draw方法,在draw方法中进行UI绘制操作。Android系统提供了2种绘制方式,图中1表示App开启了硬件加速功能,所以会启用硬件加速绘制;2处表示使用软件绘制。
ViewRootImpl中有一个非常重要的对象Surface,ViewRootImpl会将在draw方法中绘制的UI元素,绑定到这个Surface上,如果说Canvas是画板,那么Surface就是画板上的画纸,Surface中的内容最终会被传递给底层的SurfaceFlinger,最终将Surface中的内容进行合成并显示到屏幕。
软件绘制drawSoftware
1处就是调用DecorView的draw方法将UI元素绘制到画布Canvas对象中,2处将Canvas中的内容显示到屏幕上,实际上就是将Canvas中的内容提交给SurfaceFlinger进行合成处理。默认情况下软件绘制没有采用GPU渲染的方式,全部由CPU完成。
DecorView并没有覆写draw方法,因此实际上是调用顶层View的draw方法:
1处绘制View的背景,2绘制View自身内容,3表示对draw事件进行分发,在ViewGroup中会递归调用子View的draw。
硬件加速
是否启用硬件加速
在ViewRootImpl的draw方法中,通过以下判断是否启用硬件加速:
而启用硬件加速的设置,可以在manifest清单文件中,指定Application或者某个Activity支持硬件加速,如下:
此外还可以通过Window或者VIew在进行更小粒度的硬件加速设置:
需要注意的是,不是所有的2D绘制操作都支持硬件加速,如果在自定义View中使用了以下API,可能会造成程序工作不正常:
Canvas
clipPath()
clipRegion()
drawPicture()
drawPosText()
drawTextOnPath()
drawVertices()
Paint
setLinearText()
setMaskFilter()
setRasterizer()
硬件加速优势
接下来看看为什么硬件加速能够提高UI渲染的性能,ViewRootImpl的draw方法中,硬件加速部分代码如下:
图中mThreadRenderer是ThreadRenderer类型,其draw方法如下:
1处是硬件加速的特殊之处,通过updateRootDisplayList方法将View视图抽象成一个RenderNode对象,并构建View的DrawOp树;2处通知RenderThread进行绘制操作,RenderThread是一个单例线程,每个进程最多只有一个硬件渲染线程,所以不会有并发问题。
updateRootDisplayList方法如下:
Android硬件加速过程中,View属兔被抽象成RenderNode节点,View中的绘制操作都会被抽象成一个个DrawOp,每个DrawOp有对应的OpenGL绘制命令。绘制命令是由GPU执行的,GPU比CPU更擅长进行计算操作,所以整个流程会更加顺畅。
Invalidate轻量级刷新
Invalidate和requestLayout的区别在于,它不一定会触发View的measure和layout操作,多数情况下只执行draw操作。
postInvalidate和invalidate的区别是可以在非UI线程调用,原理是通过Handler方法发送消息到主线程去执行invalidate方法