第10章:Activity生命周期源码剖析——onCreate/onStart/onResume的触发时机与底层实现

说实话,做Android开发这么多年,生命周期这东西天天打交道。但你真的知道onCreate是怎么被调用的吗?

我记得刚入行那会儿,面试官问我「onResume和onStart有什么区别」,我背得滚瓜烂熟。后来遇到一个诡异的Bug——App切到后台再切回来,界面卡住了。查了两天,才发现是生命周期回调顺序出了问题。从那以后,我就决定把源码啃一遍。

今天咱们就深入ActivityThread和Instrumentation,看看这些回调到底是怎么串起来的。

10.1 生命周期回调的触发链条

先看整体流程。一个Activity从启动到显示,经历了这么几步:

  1. AMS通过Binder通知App进程
  2. ActivityThread收到消息,开始调度
  3. Instrumentation介入,执行具体回调
  4. 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重建。流程是这样的:

  1. AMS检测到配置变化
  2. 调用ActivityThread的handleConfigurationChanged
  3. 如果Activity没有声明configChanges,就走销毁重建流程
  4. 先调用onPause → onStop → onDestroy
  5. 再走一遍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在底层到底有什么区别。