
本文参考掘金-志跃同学的博文 面试?看完这篇就够了-深入分析从点击应用图标到应用界面展示 写成,强烈推荐这位技术博主的其它文章,简洁干练,值得阅读
从点击桌面图标到应用首帧展示
这里分析冷启动的过程,从点击桌面icon到APP的首个页面展示出来,这里面根据主体不同,可以分为4个阶段:
- 阶段一:应用进程启动
- 阶段二:应用进程初始化
- 阶段三:Activity启动
- 阶段四:View绘制
阶段一:应用进程启动
该过程涉及到4个进程之间的通信:
Launcher
进程,桌面本身也是一个应用程序,拥有自己的进程,在手机开机时启动system_server
进程,AMS就是运行在这个进程中,在手机开机时启动,由zygote孵化出zygote
进程,孵化器,所有应用进程都由它fork
出来,在手机开机时由Init
进程创建- 应用进程
它们的作用顺序如下:
- 当我们点击桌面应用图标时,
Launcher
进程通过Binder
向AMS发起startActivity
请求 - AMS收到请求时,处理
intent
信息,通过ProcessRecord
判断应用进程是否存在,如果不存在,则通过socket IPC
向zygote
进程发送创建新进程的请求 zygote
收到请求,fork
出新的应用进程,应用进程加载ActivityThread
类,并调用ActivityThread.main()
函数,是应用进程的入口。到此完成应用进程启动

阶段二:应用进程初始化
初始化阶段,主要做了三件事:
- 初始化并开启主线程
Looper
循环 - 初始化
Application
- 和AMS建立
Binder
通信
这三件事都是在ActivityThread.main()
函数中进行的。
ActivityThread.java
java
public static void main(String[] args) {
Looper.prepareMainLooper(); // 初始化主线程Looper
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
Looper.loop(); // 开启主线程消息循环
}
上述代码初始化主线程Looper
并开启消息循环,在attach()
中进行Application的初始化工作。
java
final ApplicationThread mAppThread = new ApplicationThread();
private void attach(boolean system, long startSeq) {
// 传入false代表非系统进程
if (!system) {
// 将ApplicationThread传入AMS,建立应用-AMS之间的Binder通信通道
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
在IActivityManager.attachApplication()
中,完成应用进程向SystemServer
注册的过程。
ApplicationThread.java
java
private class ApplicationThread extends IApplicationThread.Stub {
@Override
public final void bindApplication(...) {
sendMessage(H.BIND_APPLICATION, data);
}
}
ApplicationThread
类实现了Stub
接口,表明它适用于跨进程通信的AIDL生成的接口文件,Stub
是由服务端(这里是应用进程)实现得,客户端是运行在system_server
中的AMS。
通过ActivityThread.attach()
,AMS得到了ApplicationThread
的Binder
对象,可以通过它与应用进程进行交互,调用应用的各项生命周期方法。ApplicationThread
是AMS与ActivityThread
沟通的桥梁。
AMS调用ApplicationThread
接口,后者会把任务和数据通过消息机制抛到主线程处理,H
就是ActivityThread
的主线程Handler
,用于接收AMS过来的任务。
H.BIND_APPLICATION
消息的处理,最终落到ActivityThread.handleBindApplication()
函数。
ActivityThread.java
java
private void handleBindApplication(AppBindData data) {
try {
app = data.info.makeApplication(data.restrictedBackMode, null); // 实例化Application,内部调用了Application.attachBaseContext()
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers); // 实例化ContentProviders,内部调用了其onCreate()
}
}
try {
mInstrumentation.callApplicationOnCreate(app) // 内部调用Application.onCreate()
}
...
}
分析了上面的代码,就解释了为什么是Application.attachBaseContext()
-> ContentProvider.onCreate()
-> Applicatoin.onCreate()
这个执行顺序了。
阶段三:Activity启动
应用进程启动之后,AMS通过Binder
调用,借助ApplicationThread
向ActivityThread
的主线程发送EXECUTE_TRANSACTION
消息,来执行Activity
的生命周期。最终会在主线程执行handleLaunchActivity()
、handleResumeActivity()
等,在其内部调用Activity
的onCreate()
、onResume()
等生命周期方法。
ActivityThread.java
java
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
final Activity a = performLaunchActivity(r, customIntent);
return a;
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity activity = null;
try {
// 通过反射创建Activity对象
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
}
...
try {
// 调用Activity.onCreate()
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
...
return activity;
}
这里做了两件事情:
- 通过反射实例化Activity
- 调用其
onCreate()
接下来是handleReusmeActivity()
函数。
java
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest, boolean isForward, String reason) {
// 内部执行onResume(),执行成功后返回true
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
// 获取decorView
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
// 将decorView添加到WM
wm.addView(decor, l);
}
WindowManager.addView()
内部会把任务交给真正的执行者(被代理对象)WindowManagerGlobal
。
WindowManagerGlobal.java
java
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) {
// 实例化ViewRootImpl,它是View与Window交互的桥梁
root = new ViewRootImpl(view.getContext(), display);
try {
// 核心代码,调用setView()添加View
root.setView(view, wparams, panelParentView, userId);
}
...
}
ViewRootImpl.setView()
最核心的代码是调用了requestLayout()
,触发View的绘制过程。
阶段四:View绘制
前文提到在Activity创建过程中,AMS通过Binder
调用到Activity.onCreate()
、Activity.onResume()
,在这两个函数里会进行UI的创建和渲染。
通常我们在onCreate()
中会setContentView(layoutResId)
。
这部分代码在不同版本的SDK上差异较大,本文以下代码基于Android 13(SDK 33)分析
java
// AppCompatActivity.java
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
getDelegate().setContentView(layoutResID);
}
getDelegate()
返回的是AppCompatDelegateImpl
java
// AppDelegateImpl
@Override
public void setContentView(int resId) {
// 实例化DecorView
ensureSubDecor();
// DecorView = title + content
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
// 将自定义布局添加进content
LayoutInflater.from(mContent).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
DecorView
是一个ViewGroup,继承自FrameLayout,包含title
和content
两部分。它是Window与应用UI交互的桥梁,是整个Activity的根布局。

执行完setContentView()
后,完成DecorView
的创建,并且把自定义布局加入到了它的content
中。但此时View
只是走完了创建,并没有真正绘制,并不可见。只有完成绘制后,才可以获取其真正宽高。
requestLayout()
AMS借助ApplicationThread
,向ActivityThread
发起绘制请求,会执行到ActivityThread.handleResumeActivity()
函数,最终调用到ViewRootImpl.setView()
,执行requestLayout()
发起绘制流程。
ViewRootImpl
是Window和View沟通的桥梁,它实现了ViewParent
接口,其构造函数中有两个成员变量值得关注:
java
public final class ViewRootImpl implements ViewParent, View.AttachInfo.callbacks... {
public ViewRootImpl(@UIContext context, Display display, IWindowSession session, boolean usefChoreographer) {
// 保存当前线程(主线程)
mThread = Thread.currentThread();
// 编舞者,屏幕刷新机制的关键
mChoreographer = usefChoreographer ? Choreographer.getSfInstance() : Choreographer.getInstance();
}
requestLayout()
主要做了2件事:
- 检查线程,保证当前线程就是创建
ViewRootImpl
的线程 - 调用
scheduleTraversals()
在View.attach()完成之前,可以在非主线程更新UI
scheduleTraversals()
java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 1.发送同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 2.向Choreographer post 一个 callback
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyrendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
// 3.在callback中执行doTraversal
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 4.移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
// 5.onMeasure, onLayout, onDraw
performTraversals();
...
}
}
- 关于消息同步屏障,详细介绍可以移步到 Android 消息机制,这里简单理解一下其目的就是为了保证渲染任务得到高优先级执行
Choreographer
是屏幕刷新机制的关键实现,详细介绍可以移步到 屏幕刷新机制,这里也简单了解一下:往Choreographer
里面postCallback
目的是为了在系统下一帧渲染时间到来的时候执行Runnable
中的逻辑,对应上面也就是执行performTraversals()
performTraversals()
最关键的作用是调用performMeasure()
、performLayout()
、performDraw()
,对应View内部的onXXX()
函数。
View的三步绘制流程,可以参考以下这张图:

全部流程精简总结
Launcher
进程通过Binder
向system_server
进程的AMS发送启动Activity请求- AMS判断如果应用进程不存在,通过
socket
通知zygote
进程,fork
出应用进程 - 应用进程启动以后,调用
ActivityThread.main()
启动消息循环,建立与AMS之间的Binder
通信 - AMS通过
Binder
调度Activity
的onCreate()
、onResume()
生命周期 onCreate()
中通过setContentView()
传入自定义布局构建以DecorView
为Root
的View
树onResume()
后通过Choreographer
屏幕刷新机制,开启View的绘制流程,执行onMeasure()
、onLayout()
、onDraw()
附录
什么是fork进程
当一个进程调用fork()
时,会创建一个新的子进程,它是父进程的副本,包括代码、数据、堆栈等。在fork()
之后,父进程和子进程将并发继续执行之前在父进程中相同的代码。
父进程在fork()
后会获得子进程的pid
。
因此,在fork()
后通常伴随着pid
的检查,如果pid
非0
(即子进程的pid
),表明当前代码执行于父进程中。如果pid=0
,说明当前正位于子进程。
以下是Demo:
cpp
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
// fork failed
perror("fork");
return 1;
} else if (pid == 0) {
// child process
printf("I am the child process, my PID is %d\\n", getpid());
} else {
// parent process
printf("I am the parent process, my PID is %d and my child's PID is %d\\n", getpid, pid);
}
return 0;
}
zygote的作用
Zygote作为应用程序进程的孵化器,在Init进程中创建它,它预加载了很多常用的类和资源,位应用程序进程提供一个初始化好的运行环境。当需要创建新的应用程序进程时,系统通过zygote进程来fork
出新的应用程序进程,这么做的主要原因是为了提高应用程序的启动速度和资源共享,简化启动流程。、
- 提高应用程序启动速度:Zygote 进程在系统启动时预加载了许多常用的类和资源,这些类和资源在内存中只有一份,可以被所有应用程序进程共享。当通过 Zygote 进程 fork 出新的应用程序进程时,新进程可以直接使用这些已加载的类和资源,无需再次加载。这样可以大大减少应用程序启动时的类加载和资源初始化时间,提高启动速度
- 资源共享:由于 Zygote 进程预加载的类和资源在内存中只有一份,它们可以被所有应用程序进程共享复用。这样可以减少系统的内存占用,提高资源利用率
- 简化应用程序启动流程:通过 Zygote 进程来创建应用程序进程,可以简化启动流程,减少启动过程中的错误和异常。Zygote 进程为应用程序提供了一个统一的、经过良好测试的运行环境,有助于提高应用程序的稳定性和兼容性