第10章:Activity生命周期源码剖析——onCreate/onStart/onResume的触发时机与底层实现
说实话,做Android开发这么多年,生命周期这东西天天打交道。但你真的知道onCreate是怎么被调用的吗?
我记得刚入行那会儿,面试官问我「onResume和onStart有什么区别」,我背得滚瓜烂熟。后来遇到一个诡异的Bug——App切到后台再切回来,界面卡住了。查了两天,才发现是生命周期回调顺序出了问题。从那以后,我就决定把源码啃一遍。
今天咱们就深入ActivityThread和Instrumentation,看看这些回调到底是怎么串起来的。
10.1 生命周期回调的触发链条
先看整体流程。一个Activity从启动到显示,经历了这么几步:
- AMS通过Binder通知App进程
- ActivityThread收到消息,开始调度
- Instrumentation介入,执行具体回调
- Activity自身响应,触发onCreate等
说白了,AMS是发令枪,ActivityThread是调度中心,Instrumentation是执行者。
10.2 onCreate的触发时机
onCreate什么时候触发?很多人会说「Activity创建时」。但具体到源码层面,是在performLaunchActivity方法里。
// ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 1. 通过ClassLoader创建Activity实例
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
} catch (Exception e) {
// ...
}
// 2. 创建Application(如果还没创建)
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
// 3. 创建ContextImpl并attach到Activity
Context appContext = createBaseContextForActivity(r, activity);
activity.attach(appContext, this, getInstrumentation(), r.token, ...);
// 4. 调用onCreate
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
// 5. 调用onStart
activity.performStart();
r.activity = activity;
r.stopped = false;
// 6. 调用onResume
mInstrumentation.callActivityOnResume(activity);
return activity;
}
注意看顺序:先newActivity创建实例,再attach绑定上下文,最后才callActivityOnCreate。所以你在onCreate里调用getApplication()是安全的——Application已经在第2步创建好了。
关键点:onCreate调用时,Activity的Window已经创建(在attach里),但还没显示。View的绘制流程要等到onResume之后。
10.3 onStart与onResume的底层差异
很多人分不清onStart和onResume。我直接告诉你源码里的区别:
| 回调 | 触发方法 | 关键行为 |
|---|---|---|
| onCreate | Instrumentation.callActivityOnCreate | 创建View、恢复状态 |
| onStart | Activity.performStart | Activity可见但不可交互 |
| onResume | Instrumentation.callActivityOnResume | 开始接收用户输入 |
看performStart的源码:
// Activity.java
final void performStart() {
mInstrumentation.callActivityOnStart(this);
// 这里会触发onStart
mFragments.dispatchStart();
// Fragment的onStart在这里调用
}
而onResume的调用链是这样的:
// Instrumentation.java
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();
// 注意:这里会触发WindowManager添加Window
if (activity.mWindow != null) {
activity.mWindow.getDecorView().dispatchWindowVisibilityChanged(View.VISIBLE);
}
}
看到了吗?onResume里才真正让DecorView可见。所以你在onStart里做动画?嗯,我建议你放到onResume里,否则可能出现闪一下的情况。
我的经验:曾经有个项目,在onStart里注册了一个广播接收器,结果界面还没完全显示,广播就来了,导致空指针。后来我改成在onResume里注册,onPause里注销,问题解决。
10.4 ConfigurationChange的处理机制
屏幕旋转、语言切换、键盘弹出……这些都属于ConfigurationChange。系统怎么处理的?
默认情况下,ConfigurationChange会导致Activity重建。流程是这样的:
- AMS检测到配置变化
- 调用ActivityThread的
handleConfigurationChanged - 如果Activity没有声明
configChanges,就走销毁重建流程 - 先调用onPause → onStop → onDestroy
- 再走一遍onCreate → onStart → onResume
但如果你在Manifest里声明了:
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize" />
那系统就不会销毁重建,而是直接调用onConfigurationChanged:
// ActivityThread.java
public void handleConfigurationChanged(Configuration config) {
// 遍历所有Activity
for (int i = 0; i < N; i++) {
ActivityClientRecord r = mActivities.valueAt(i);
if (r.activity != null) {
r.activity.mCalled = false;
// 直接回调,不销毁
mInstrumentation.callActivityOnConfigurationChanged(r.activity, config);
}
}
}
注意:声明了configChanges不代表万事大吉。如果你在onConfigurationChanged里没有手动更新资源(比如重新加载布局),界面可能会错乱。我曾经见过一个同事,声明了configChanges但没处理,结果横竖屏切换后按钮位置全乱了。
10.5 生命周期与Fragment的联动
Fragment的生命周期是依附于Activity的。但有个细节很多人忽略:
// Activity.java
protected void onCreate(Bundle savedInstanceState) {
// ...
mFragments.dispatchCreate();
// Fragment的onCreate在这里调用
}
protected void onStart() {
// ...
mFragments.dispatchStart();
}
protected void onResume() {
// ...
mFragments.dispatchResume();
}
顺序是:Activity先回调,再分发到Fragment。所以你在Activity的onCreate里调用FragmentTransaction是安全的——FragmentManager已经初始化好了。
但反过来,Fragment的onResume在Activity的onResume之后。这意味着什么?
如果你在Activity的onResume里做了某些操作,Fragment的onResume还没执行。我遇到过一个问题:在Activity的onResume里弹Dialog,结果Fragment的UI还没准备好,Dialog盖住了Fragment的内容。后来我把弹窗逻辑移到了Fragment里,用getActivity().getSupportFragmentManager()来管理,才解决。
10.6 避坑指南:生命周期常见陷阱
- 不要在onSaveInstanceState里做耗时操作——它可能在onStop之前调用,而且主线程不能卡
- onPause里不要做重量级操作——比如网络请求、数据库写入。因为onPause执行完才能显示下一个Activity
- onDestroy不一定执行——系统杀进程时可能直接跳过。所以资源释放最好在onStop里做
我曾经踩过的坑:有个版本在onPause里保存用户输入的数据,结果用户快速切换App,数据没保存完就被杀了。后来我改成在onStop里保存,配合Room数据库的异步写入,才稳定下来。
10.7 小结
生命周期这东西,背八股文没用。你得理解底层是怎么调度的。
记住三个核心类:ActivityThread负责调度,Instrumentation负责执行,Activity负责响应。它们配合起来,才有了我们熟悉的onCreate → onStart → onResume这条链。
至于ConfigurationChange,我的建议是:能不自己处理就别自己处理。除非你有特殊需求(比如视频播放器要保持播放状态),否则让系统重建Activity是最稳妥的方式。你想想看,系统帮你处理了资源切换、状态保存恢复,你何必自找麻烦呢?
下一章咱们聊聊Service的启动流程,看看bindService和startService在底层到底有什么区别。