
本文参考掘金-志跃同学的博文 面试?看完这篇就够了-深入分析从点击应用图标到应用界面展示 写成,强烈推荐这位技术博主的其它文章,简洁干练,值得阅读
从点击桌面图标到应用首帧展示
这里分析冷启动的过程,从点击桌面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 进程为应用程序提供了一个统一的、经过良好测试的运行环境,有助于提高应用程序的稳定性和兼容性