Q:从这篇文章你能学到什么?
A:这篇文章从桌面上点开一个APP图标开始分析,一直到里面各个view也就是控件显示到我们眼睛的整个过程。让你了解整个APP启动流程和View绘制流程。看完你肯定会爱上它的,Believe Me!
开始思考:当我们点击手机桌面上的一个软件时,从点击到完整显示这个APP整个过程中发生了什么???
先忍受一下枯燥,理论知识还是得先在脑海中留下印象的
一、从Activity启动流程中理论知识入门
砸门先来了解几个关键的名词,下面会用到哦
1.Launcher--桌面系统APP
Launcher其实就是一个app,从功能上说,是对手机上其他app的一个管理和启动,从代码上说比其他app多了个属性,就是在AndroidManifest.xml文件中多了个“<categoryandroid:name="android.intent.category.HOME" />”属性,这个属性就是在启动系统或者按Home键时会过滤这个属性,如果系统中只有一个应用具有这个属性,就会直接跳转到这个界面,也就是这个launcher,如果有多个,会弹出选择框让用户选择并且提示用户是否选择默认设置。
(其实Launcher就是我们玩手机时按Home键时跳转到桌面一个道理,说白了,Launcher就是我们的桌面,他就是个系统的App)我们点击的每个程序图标就是他的一个item,会触发onclick事件,接收事件然后Launcher会打开我们的应用。
2.Zygote--一个进程
zygote意为“受精卵“。Android是基于Linux系统的,而在Linux中,所有的进程都是由init进程直接或者是间接fork出来的,zygote进程也不例外。在Android系统里面,zygote是一个进程的名字。Android是基于Linux System的,当你的手机开机的时候,Linux的内核加载完成之后就会启动一个叫“init“的进程。在Linux System里面,所有的进程都是由init进程fork出来的,我们的zygote进程也不例外。我们都知道,每一个App其实都是一个单独的dalvik虚拟机和一个单独的进程。
所以当系统里面的第一个zygote进程运行之后,在这之后再开启App,就相当于开启一个新的进程。而为了实现资源共用和更快的启动速度,Android系统开启新进程的方式,是通过fork第一个zygote进程实现的。所以说,除了第一个zygote进程,其他应用所在的进程都是zygote的子进程。这下你明白为什么这个进程叫“受精卵”了吧?因为就像是一个受精卵一样,它能快速的分裂,并且产生遗传物质一样的细胞!
3.SystemServer--一个系统服务进程管家(管家管着很多个服务)
SystemServer也是一个进程,而且是由zygote进程fork出来的。
知道了SystemServer的本质,我们对它就不算太陌生了,这个进程是Android Framework里面两大非常重要的进程之一,另外一个进程就是上面的zygote进程。为什么说SystemServer非常重要呢?因为系统里面重要的服务都是在这个进程里面开启的,比如ActivityManagerService、PackageManagerService、WindowManagerService等等。
4.ActivityManagerService--一个服务(和四大组件相沟通的)
AMS是Android中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,因此它在Android中非常重要,它本身也是一个Binder的实现类。
ActivityManagerService进行初始化的时机很明确,就是在SystemServer进程开启的时候,就会初始化。
5.Instrumentation和ActivityThread(和Activity和Application相关)
每个Activity都持有Instrumentation对象的一个引用,但是整个进程只会存在一个Instrumentation对象。
Instrumentation这个类里面的方法大多数和Application和Activity有关,这个类就是完成对Application和Activity初始化和生命周期的工具类。Instrumentation这个类很重要,对Activity生命周期方法的调用根本就离不开他,他可以说是一个大管家。
ActivityThread,就是UI线程,应用的入口类,通过调用main方法,开启消息循环队列。App和AMS是通过Binder传递信息的,那么ActivityThread就是专门与AMS的外交工作的。
坚持,理论知识快没了
二、Activity启动整体流程
1.基础类介绍
1)ApplicationThread:
提供Binder通讯接口,AMS则通过代理调用此App进程的本地方法
2)ActivityManagerProxy
AMS服务在当前进程的代理类,负责与AMS通信。
3)ApplicationThreadProxy
ApplicationThread在AMS服务中的代理类,负责与ApplicationThread通信。
2.整体流程图
3.流程整体分析--(参考上图分析)
1.点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;
2.system_server进程接收到请求后,向zygote进程发送创建进程的请求;
3.Zygote进程fork出新的子进程,即App进程;
4.App进程,通过Binder IPC向sytem_server进程发起attachApplication请求;
5.AMS向ApplicationThreadProxy发送realStartActivityLocked请求。
6.system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;
7.App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
8.主线程在收到Message后,通过调用handleLaunchActivity创建目标Activity,并回调Activity.onCreate()等方法。
9.到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。
总结:
1.AMS是在System Server中的,是其中的一个服务
2.APP进程和AMS之间通信都是通过Binder进行间接通信的
3.Zygote创建出APP进程之后,然后就是创建Application
4.接着创建Activity
总的就是这么个逻辑顺序
在坚持坚持,我们看会源码,先知道大概流程在看源码会轻松很多的,不看源码很非常容易忘记的
4.Application是怎么创建出来的?
ActivityThread里面有个ApplicationThread内部类,这个内部类继承了IApplicationThread的内部类Stub,所以最终调用的是ActivityThread的内部类ApplicationThread里面的bindApplication方法,故我们找到这个方法:
//ActivityThread.ApplicationThread.java
@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
ProviderInfoList providerList, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {
...
//1.将该方法的参数等信息封装到AppBindData对象中
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providerList.getList();
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
data.autofillOptions = autofillOptions;
data.contentCaptureOptions = contentCaptureOptions;
data.disabledCompatChanges = disabledCompatChanges;
//2.通过Handler发送消息,并携带AppBindData参数
sendMessage(H.BIND_APPLICATION, data);
}
通过上面bindApplication方法的源码可以看出,在该方法里面对入参进行一个封装,然后采用Handler机制携带AppBindData对象发送消息。通过sendMessage方法的第一个参数可以发现,接收消息的what标志是定义在一个H的类里面,由此可以想到,最终处理这条消息的handleMessage方法应该也在这个H类里面,我们进入到这个H类里面,果然,很快就可以发现H类是Handler的子类,并且可以找到handleMessage方法,在这里可以看到对BIND_APPLICATION的处理逻辑:
//ActivityThread.H.java
public void handleMessage(Message msg) {
...
switch (msg.what) {
//1.绑定application
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
//2.获取传过来的AppBindData实例
AppBindData data = (AppBindData)msg.obj;
//3.调用handleBindApplication方法处理逻辑
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
...
}
handleMessage接收到消息后,通过handleBindApplication方法来处理逻辑:
//ActivityThread.java
@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
...
//1.创建ApplicationContext
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
...
//2.声明Application
Application app;
...
//3.创建Application
app = data.info.makeApplication(data.restrictedBackupMode, null);
...
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
//从这里可以看出ContentProvider要先于application.onCreate执行
installContentProviders(app, data.providers);
}
}
try {
//调用Application的onCreate方法
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
}
以上第1处的源码为应用生成了上下文Context,即通过getApplicationContext()获取到的ContextImpl,ContextImpl是Context的子类,然后通过data.info.makeApplication(data.restrictedBackupMode, null)方法创建app,我们看一下这个方法:
//LoadedApk.java
@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
//1.如果不为空,直接返回
return mApplication;
}
...
Application app = null;
//2.获取Application的className
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
//3.如果获取到的appClass为空,则使用默认Application的className
appClass = "android.app.Application";
}
try {
//4.获取类加载器ClassLoader
final java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
...
initializeJavaContextClassLoader();
...
}
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
...
//5.通过反射创建Application
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
...
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
}
通过上面的makeApplication方法源码,可以很清晰的知道,Application是通过类加载器和反射创建的。上面第2步首先获取Application的className,此处获取的className即为我们项目中manifests文件中的application节点中name属性定义的Application,然后在下面第3步对这个className进行判空,如果为空的话,就赋值为默认的className。紧接着获取类加载器,然后在第5步通过类加载器和className利用反射创建Application。
到此Application就创建完成,然后我们回到前面ActivityThread.java类中的handleBindApplication方法,创建完Application之后,调用了mInstrumentation.callApplicationOnCreate(app)方法,该方法源码如下:
//Instrumentation.java
/**
* Perform calling of the application's {@link Application#onCreate}
* method. The default implementation simply calls through to that method.
*
* <p>Note: This method will be called immediately after {@link #onCreate(Bundle)}.
* Often instrumentation tests start their test thread in onCreate(); you
* need to be careful of races between these. (Well between it and
* everything else, but let's start here.)
*
* @param app The application being created.
*/
public void callApplicationOnCreate(Application app) {
app.onCreate();
}
简洁明了,这个方法里面直接调用了Application的onCreate方法,方法上面的注释也说明的很清楚。
5.Activity到底是怎么创建出来的?
与Activity相关的类就是ActivityThread,ActivityThread的final H mH = new H()内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信。Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期
例如:
当msg.what == LAUNCH_ACTIVITY就是调用handleLaunchActivity方法启动一个Activity,在handleLaunchActivity中又调用了performLaunchActivity方法来创建一个Activity实例,完成Activity的启动。 handleLaunchActivity源码如下:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//...调用performLaunchActivity方法完成Activity的启动
Activity a = performLaunchActivity(r, customIntent);
//...
}
H继承与Handle,重写了handleMessage的方法
public final class ActivityThread {
//...
final H mH = new H();
private class H extends Handler {
//...声明的一些常量
public void handleMessage(Message msg) {
//...
switch (msg.what) {
//针对不同的常量,做不同的业务处理
case LAUNCH_ACTIVITY: {
//...启动一个Activity
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
//...
} break;
case RELAUNCH_ACTIVITY: {
//...
handleRelaunchActivity(r);
//...
} break;
case PAUSE_ACTIVITY: {
//...
handlePauseActivity((IBinder) args.arg1, false,
(args.argi1 & USER_LEAVING) != 0, args.argi2,
(args.argi1 & DONT_REPORT) != 0, args.argi3);
maybeSnapshot();
//...
} break;
//...
}
//...
}
private void maybeSnapshot() {
//...这个方法主要统计snapshot
}
}
}
这个类主要作用就是根据不同的情况处理各种业务,而且处理业务的方法一般是以handle开头,handleXXX的格式,如下:
handleActivityConfigurationChanged()
handleBindApplication()
handleBindService()
handleCancelVisibleBehind()
handleConfigurationChanged()
handleCreateService()
handleDestroyActivity()
handleDispatchPackageBroadcast()
handleLaunchActivity()
handleLowMemory()
handleMessage()
handleNewIntent()
handlePauseActivity()
handleReceiver()
handleRelaunchActivity()
handleResumeActivity()
handleSendResult()
handleServiceArgs()
handleStopActivity()
handleStopService()
而这些函数有的又会调用到如下的performXXX系列函数完成最终的事件处理:
performDestroyActivity()
performDestroyActivity()
performLaunchActivity()
performNewIntents()
performPauseActivity()
performPauseActivity()
performRestartActivity()
performResumeActivity()
performStopActivity()
performStopActivityInner()
performUserLeavingActivity()
//ActivityThread.java
@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
...
//创建并启动activity
final Activity a = performLaunchActivity(r, customIntent);
...
}
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//获取activity信息
ActivityInfo aInfo = r.activityInfo;
...
//获取component
ComponentName component = r.intent.getComponent();
//获取上下文
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
//获取ClassLoader
java.lang.ClassLoader cl = appContext.getClassLoader();
//通过ClassLoader加载activity,再通过反射创建activity实例
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
} catch (Exception e) {
...
}
try {
//获取Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
...
// Activity resources must be initialized with the same loaders as the
// application context.
appContext.getResources().addLoaders(app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
appContext.setOuterContext(activity);
//调用Activity的attach方法
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);
...
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
//设置主题
activity.setTheme(theme);
}
...
//调用Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
r.activity = activity;
...
//设置Activity生命周期状态为ON_CREATE
r.setState(ON_CREATE);
...
}
}
performLaunchActivity方法中首先通过ActivityClientRecord获取activity信息,然后就是通过ClassLoader和反射创建activity实例,接着再调用activity的attach方法,然后设置activity的主题啊等一些信息,最后再调用Activity的onCreate方法并将生命周期状态记录为ON_CREATE。
activity的attach方法调用
我们先来简单看看activity.attach方法:
//Activity.java
@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
...
//创建PhoneWindow并赋值给mWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
...
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();
mCurrentConfig = config;
...
}
attach方法里面主要是创建和配置PhoneWindow。在这里不细聊attach方法。下面还会详细介绍的
activity的onCreate方法调用
我们继续看activity的onCreate方法的调用,接着上面mInstrumentation.callActivityOnCreate方法看:
//Instrumentation.java
public void callActivityOnCreate(Activity activity, Bundle icicle) {
//做一些准备工作
prePerformCreate(activity);
//真正执行Activity的onCreate
activity.performCreate(icicle);
postPerformCreate(activity);
}
我们直接看activity.performCreate(icicle)方法,这个方法在Activity中:
//Activity.java
final void performCreate(Bundle icicle) {
//调用下面的重载方法
performCreate(icicle, null);
}
@UnsupportedAppUsage
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
dispatchActivityPreCreated(icicle);
mCanEnterPictureInPicture = true;
...
//此处真正调用了Activity的onCreate生命周期方法
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
}
至此Activity的创建和onCreate生命周期方法的调用结束了
总结:
1.Application创建出来后,会调用scheduleLaunchActivity()发送一个LAUNCH_ACTIVITY消息到消息队列H类中
2.通过 handleLaunchActivity()来处理该消息
3.通过performLaunchActiivty()方法回调Activity的onCreate()方法和onStart()方法
4.后通过handleResumeActivity()方法,回调Activity的onResume()方法
5.最终显示Activity界面。
6.源码流程整体总结
1.创建进程
①先从Launcher的startActivity()方法,通过Binder通信,调用ActivityManagerService的startActivity方法。
②一系列折腾,最后调用startProcessLocked()方法来创建新的进程。
③该方法会通过前面讲到的socket通道传递参数给Zygote进程。Zygote孵化自身。调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid。
④调用ActivityThread.main()方法,ActivityThread随后依次调用Looper.prepareLoop()和Looper.loop()来开启消息循环。
2.创建Application
①上面创建进程后,执行ActivityThread.main()方法,随后调用attach()方法。
②将进程和指定的Application绑定起来。通过Binder IPC向sytem_server进程发起attachApplication请求。这个是通过上节的ActivityThread对象中调用bindApplication()方法完成的。该方法发送一个BIND_APPLICATION的消息到消息队列中, 最终通过handleBindApplication()方法处理该消息. 然后调用makeApplication()方法来加载App的classes到内存中。
3.创建Activity
经过前两个步骤之后, 系统已经拥有了该application的进程。 后面的调用顺序就是普通的从一个已经存在的进程中启动一个新进程的activity了。
实际调用方法是realStartActivity(), 它会调用application线程对象中的scheduleLaunchActivity()发送一个LAUNCH_ACTIVITY消息到消息队列H类中,然后 通过 handleLaunchActivity()来处理该消息。在 handleLaunchActivity()通过performLaunchActiivty()方法回调Activity的onCreate()方法和onStart()方法,然后通过handleResumeActivity()方法,回调Activity的onResume()方法,最终显示Activity界面。
神概括,让你记住这个流程
1.首先,手机上有个桌面(Launcher),桌面里面很多app,这些app也就是Launcher的孩子
2.然后孩子对系统服务管家(system_server)说我要开始启动了,你准备好了吗?
3.然后系统服务管家(system_server)开始通知其他人做好准备,首先通知的是Zygote管家
4.Zygote管家接收到命令,收到,然后他自己就准备给这个app创建一个它的家,也就是属于它自己独有的APP进程
5.然后APP进程开始部署自己的家
6.APP进程对系统服务管家(system_server)说,我要先关联APP的Application,
7.系统服务管家(system_server)做完一系列准备后通知APP进程的ApplicationThread小管家
8.Application已经给你了,你自己处理了。
9.ApplicationThread小管家收到之后,Yes,sir!然后通知另外一个更小的管家ActivityThread
10.ActivityThread管家就是具体某个页面Activity的小管家,然后他就比较忙了
11.接下来他就要完成onCreate,onstart,onResume等工作之后才能休息
12.终于,一系列工作都完成了。这个APP有了自己的家,于是新家诞生了
13.乔迁新居,一家人有了一个温馨的家,夕阳西下,落下帷幕
醒醒醒醒,美好的东西过去了,回归现实哈,划重点啦
到此为止,从桌面点击APP到APP的Activity界面显示的整个流程已经分析完了。但这只是把Activity显示出来了,Activity里面还包含很多的控件,也就是View,这些View才是最终显示给我们看到的,Activity只是管理这些View的生命周期而已。
所以,你懂的,不要犯困,俺们在坚持坚持!接下来分析的是Activity之后自定义View是如何从绘制测量布局等一步步显示到我们眼睛的!
三、View显示过程中常见名词解释
不好意思,又要带你继续带你看那些难懂抽象的丑八怪了,你会不会讨厌我啊,别,亲亲,我发誓,就下面一点丑八怪,后面都是你喜欢的哦,你懂得?砸们坚持住啊,杀啊!
1.Activity--这个在熟悉不过了吧
Activity并不负责视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。一个Activity包含了一个Window,Window才是真正代表一个窗口。Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window、以及View进行交互。
2.Window--一个包含很多视图的容器
Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局R.layout.activity_main。Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
3.DecorView--根视图,老大
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。 具体情况和Android版本及主体有关,以其中一个布局为例,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:fitsSystemWindows="true"
android:orientation="vertical">
<!-- Popout bar for action modes -->
<ViewStub
android:id="@+id/action_mode_bar_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:theme="?attr/actionBarTheme" />
<FrameLayout
style="?android:attr/windowTitleBackgroundStyle"
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize">
<TextView
android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical" />
</FrameLayout>
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foreground="?android:attr/windowContentOverlay"
android:foregroundGravity="fill_horizontal|top" />
</LinearLayout>
在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子View,就是上面的id为content的FrameLayout中。所以我们每天在Activity里自动生成的setContentView(R.layout.activity_main)加载的布局就是到DecorView中的内容栏的。
4.ViewRoot--多事的,测量、布局、绘制和事件分发它都要管
ViewRoot可能比较陌生,但是其作用非常重大。所有View的绘制和事件分发等交互都是通过它来执行或传递的。
ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。
ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。ViewRoot继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。
四、DecorView是怎么创建的
先是从Activity中的setContentView()开始。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
可以看到实际是调用getWindow().setContentView,这个getWindow()指的就是Window的实现类PhoneWindow。接下来我们看看这个PhoneWindow是什么时候被创建的。
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
..................................................................
mWindow = new PhoneWindow(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实例。然后我们在看看DecorView是怎么被嵌入到PhoneWindow中的
public void setContentView(int layoutResID) {
if (mContentParent == null) {//mContentParent为空,创建一个DecroView
installDecor();
} else {
mContentParent.removeAllViews();//mContentParent不为空,删除其中的View
}
mLayoutInflater.inflate(layoutResID, mContentParent);//为mContentParent添加子View,即Activity中设置的布局文件
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();//回调通知,内容改变
}
}
看了下来,可能有一个疑惑:mContentParent到底是什么? 就是前面布局中@android:id/content所对应的FrameLayout。
继续分析PhoneWindow类的setContentView方法
从源码可以知道,这里主要包括三个步骤:
- 如果父容器为空则初始化父容器,否则移除所有子视图;
- 调用LayoutInflater类的inflate方法将xml布局文件加载到父容器;
- 回调Callback通知ContentView发生改变,其中的Callback可能由Activity实现。
后面两步比较简单,这里主要来看第一步的父容器初始化流程,进入PhoneWindow类的installDecor方法:
PhoneWindow类的installDecor方法
这个方法的代码有点儿长,这里只截取重要部分,主要操作包括三部分:
- 调用generateDecor()创建出mDecor,即DecorView对象;
- generateLayout(mDecor)传入mDecor对象,生成mContentParent ;
- 设置标题栏信息。
installDecor方法中调用的generateLayout方法:
这个方法代码非常多,我们只需要关注重点即可。
1. 首先获取<Application android:theme=""/>, <Activity/>节点指定的themes或者代码;
2. 然后获取窗口Features, 设置相应的修饰布局文件,这些xml文件位于frameworks/base/core/res/res/layout下;
3. 接着调用了DecorView的onResourcesLoaded方法将上面选定的布局文件inflate为View,添加到DecorView中;
4. 找到id为content的framlayout赋给mContentParent,由于已经将屏幕View加为mDecor的子View,因此mContentParent也是mDecor的子View;
5. 设置mDecor的背景和标题。
再回到PhoneWindow的setContentView方法中, 继续调用了mLayoutInflater.inflate(layoutResID, mContentParent),在这里就是把我们写的布局文件通过inflater加入到mContentParent中。这样我们写的布局文件成功的添加到DecorView中的mContentParent。
现在只是完成了DecorView的创建并初始化,我们还需要把这个创建并初始化完DecorView添加并显示到屏幕上,这里我们就需要用到WindowManager。
通过上面的流程我们大致知道,在Activity里面有个window的儿子PhoneWindow,PhoneWindow先生了一个老大DecroView,其中创建的过程中可能根据Theme不同,加载不同的布局格式,例如有没有Title,或有没有ActionBar等,然后老大带着很多个弟弟新出生的弟弟妹妹,也就是setContentView加载的布局中的控件。到此位置,window家族有了很多个孩子。
五、DecorView的显示
以上仅仅是将老大DecorView生出来了,还有他的弟弟妹妹layout里面的控件加进来了。但是他们是什么时候被爸爸妈妈带出来给外人看见他们这么可爱的模样的呢,通过setContentView()设置的界面,为什么在onResume()之后才对用户可见呢?
这就要从ActivityThread,也就是那个管理Activity的管家开始说起。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//就是在这里调用了Activity.attach()呀,接着调用了Activity.onCreate()和Activity.onStart()生命周期,
//但是由于只是初始化了mDecor,添加了布局文件,还没有把
//mDecor添加到负责UI显示的PhoneWindow中,所以这时候对用户来说,是不可见的
Activity a = 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也将登场。
final void handleResumeActivity(IBinder token, boolean clearHide,
boolean isForward, boolean reallyResume) {
//这个时候,Activity.onResume()已经调用了,但是现在界面还是不可见的
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
//decor对用户不可见
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = 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()方法之后,界面才对我们是可见的。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());//将DecorView添加到WindowManager
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);//DecorView可见
}
调用addView方法,这个方法非常关键,wm是上面a.getWindowManager()获取到的mWindowManger对象,而这个对象是WindowManagerImpl。
继续进入WindowManagerImpl类的addView方法:
WindowManagerImpl类的addView方法
其实WindowManagerImpl类的方法大部分都是代理的WindowManagerGlobal的方法。继续进入WindowManagerGlobal类的addView方法:
WindowManagerGlobal类的addView方法
从上面的代码可以看出,addView方法中,创建了一个ViewRootImpl对象,然后调用ViewRootImpl.setView()方法,继续查看setView()方法。
ViewRootImpl类的setView方法
该方法首先将传进来的参数view赋值给mView,mView将是这个对象所认识的root节点,也是整个Activity的root的节点,即DecorView。
setView方法调用了requestLayout方法
接着调用了requestLayout()方法,首次调度执行 layout,这里会触发 onAttachToWindow 和 创建 Surface方法。深入查看ViewRootImpl中requestLayout()方法:
该方法首先检查了是否在主线程,然后就执行了scheduleTraversals()方法。
这里需要注意的就是Runnable对象,继续往后看:
这个Runnable的run()方法中,调用了doTraversal()方法:
可以看到doTraversal()方法又调用了performTraversals()方法:
performTraversals方法调用 performMeasure方法
performTraversals方法调用 performLayout方法
performTraversals方法调用 performDraw方法
这个方法非常长,内部逻辑也很复杂,但是主体逻辑很清晰。其执行的过程可简单的概括为:是否需要重新计算视图的大小(measure)、是否需要重新布局视图的位置(layout),以及是否需要重绘(Draw)。也就是我们常说的View的绘制流程,由于这里涉及的内容实在太多,关于View的绘制后续再分享。
回到ViewRootImpl类的setView()方法,继续查看源码:
setView方法调用assignParent方法
从这里可以看到view的父亲注册为自己,于是mDecor知道了自己父亲是谁,即整个Activity设置了一个根节点,在此之前调用setContentView()只是将自己的layout布局add到PhoneWindow.mContentParent,但是mDecor并不知道自己的parent是谁,现在整个view的树形结构中有了根节点,也就是ViewRootImpl,那么requestLayout()就有效了,就可以进行后面的measure、layout、draw三步操作了。
具体流程图如下所示
其实ViewRootImpl的作用不止如此,还有许多功能,如事件分发。
要知道,当用户点击屏幕产生一个触摸行为,这个触摸行为则是通过底层硬件来传递捕获,然后交给ViewRootImpl,接着将事件传递给DecorView,而DecorView再交给PhoneWindow,PhoneWindow再交给Activity,然后接下来就是我们常见的View事件分发了。
硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity
终于熬到头了!!!!作者,你这个骗子!老是骗我说在坚持坚持!其实没骗你,看到这,你肯定不想骂我了,你肯定受益匪浅!
六、从Activity初始化到显示各个View到界面过程总结
1.从Activity的setContentView(R.id.layout)开始,加载一个布局id到DecorView的内容栏中。
2.在Activity中的attach方法中初始化Window抽象类的实例PhoneWindow对象。
3.把加载进来的布局id也就是DecorView添加到PhoneWindow中。到处视图就嵌套上来了。
4.在ActivityThread类中的handleLaunchActivity方法中调用了Activity.attach()方法,然后开始Activity生命周期的创建。
5.调用里面的handleResumeActivity()方法中的makeVisible()显示布局,也就是DecorView.
6.在makeVisible()过程中创建了一个ViewRoot抽象类的对象ViewRootImpl。
7.WindowManager交给WindowManagerImpl实现,再交给WindowManagerGlobal 的addView()方法。
里面实例化了ViewRootImpl对象,然后调用其setView()方法。最终就是将DecorView添加并显示到屏幕上
8.setView()方法经过各种调用最终调用performTraversals()方法,然后就是自定义View的显示流程了
9.先调用peformMeasure,再measure,再onMeasure完成测量
10.同理完成布局Layout
11.在同理完成绘制Draw
12.最终显示出来
神概述--不懂你打我
1.setContentView加载布局,先生了个老大DecorView
2.然后再生layout里面的孩子,把他们交给老大DecorView带
3.怎么显示出来的呢一家人
4.通过onResume方法执行后才解开他们的庐山真面目
5.他们是怎么决定哪个孩子长哪样的呢
6.就是ViewRootImpl这个家伙来决定的,我决定你是白雪公主还是丑小鸭
7.通过自定义View一个个绘制出来
8.一家人都有自己的样子了,然后就给人们带出来看看这个美好的世界了
七、面试总结
1.Application创建流程?
答:
1.在ActivityThread类main方法调用attach() 方法,
2.将进程和指定的Application绑定起来。通过Binder IPC向sytem_server进程发起attachApplication请求。这个是通过ActivityThread对象中调用bindApplication()方法完成的。该方法发送一个BIND_APPLICATION的消息到H hander中, 最终通过handleBindApplication()方法处理该消息. 然后调用 LoadedApk的makeApplication()方法
3.makeApplication()方法获取了我们的 android.app.Application 类 的完整类名,得到了类的加载器,还有上下文,最后调用 Instrumentation 类的 newApplication() 方法,通过反射的方式,创建了 Application 类的实例。 然后调用mInstrumentation.callApplicationOnCreate(app);最终调用Application 类的 onCreate()。
1.Activity创建流程?
答:>1.从Activity的setContentView(R.id.layout)开始,加载一个布局id到DecorView的内容栏中。
2.在Activity中的attach方法中初始化Window抽象类的实例PhoneWindow对象。
3.把加载进来的布局id也就是DecorView添加到PhoneWindow中。到处视图就嵌套上来了。
4.在ActivityThread类中的handleLaunchActivity方法中调用了performLaunchActivity,它里面调用Activity.attach()方法,然后回调Activity的onCreate()方法和onStart()方法
5.调用里面的handleResumeActivity()方法中的makeVisible()显示布局,也就是DecorView.
6.在makeVisible()过程中创建了一个ViewRoot抽象类的对象ViewRootImpl。
7.实例化了ViewRootImpl对象,然后调用其setView()方法。
8.setView()方法经过各种调用最终调用performTraversals()方法,然后就是自定义View的显示流程了
9.先调用peformMeasure,再measure,再onMeasure完成测量
10.同理完成布局Layout
11.在同理完成绘制Draw
12.最终显示出来
八、全剧终
你真的以为完了,上面自定义View只是大概介绍了下,你以为自定义View那么简单啊,还有好多好多呢。别灰心,能学到这说明你是个善于坚持的人,你会接着看下去的,期待下文---《通俗易懂吃透自定义View》
我好累啊,写文章有激情的时候能bb很多,但是有时候你准备写的时候一个字都憋不出来,那你就会很烦躁。发现写完文章,自己一步步的用自己语言概括出来,突然发现android源码都是我写的哈哈哈哈,膨胀了,写文章还是能体会到很多的,但是就是有点耗时间啊哈哈哈哈,亲爱的你们,我知道你们也累了,休息去吧。
慢着慢着,留下你的一键三连在走得唔得!
感谢您的阅读,要是有收获请记得三连点击,别告诉我下次一定!