带着问题学习 Activity 启动流程

学习要抓住重点,提问题是一个不错的方法

一、知识储备型问题


1. 什么是进程?如何创建一个进程?(Android 环境)

2. Zygote 以及其启动流程?

3. SystemServer 及其创建过程?它包含哪些服务?

接下来查阅相关资料开始回答吧。

1.1 进程

《Linux 内核源代码情景分析》中的 “进程四要素”:

  • 有一段程序供其运行;
  • 拥有专用的系统堆栈空间;
  • 在内核存在对应进程控制块;
  • 拥有独立的用户存储空间。

运行需要代码支持,堆栈控件涉及内存的使用,此外还包含文件、CPU、信号(通信用)等信息。

进程的创建

  • 进程通过 fork() 创建,父进程初始拥有很多参数和数据。创建子进程时,只需要父进程进行数据的拷贝,然后将部分数据修改为子进程特有的即可。
  • 这种复制方式效率比较高,避免了重新创建进程的消耗。

回答问题 1 什么是进程?

相对独立的、用来执行代码的一系列计算器活动,是操作系统结构的基础。

回答问题 1 如何创建进程?

通过 Zygote 进程,以类似复制的 fork 方式创建。

1.2 Zygote 进程

Zygote进程在 init 进程中以 Service 的方式启动

Zygote 进程创建流程:

  • 1.创建AppRuntime对象,并且调用其start函数。之后zygote的核心初始化都由AppRuntime中;
  • 2.调用startVm 创建虚拟机,然后调用startReg来注册JNI函数;
  • 3.通过JNI调用com.android.internal.os.ZygoteInit的main函数,从此进入了Java世界;
  • 4.调用registerZygoteSocket创建可以响应子孙后台请求的socket。同时zygote调用preload函数预加载常用的类、资源等,为Java世界添砖加瓦;
  • 5.调用startSystemServer函数fork一个system_server来为Java服务;
  • 6.Zygote完成了Java的初始工作后,便调用runSelectLoop来让自己无限循环等待。之后,如果收到子孙后台的请求,它便会醒来为他们工作。

Zygote 从 init 进程启动之后(C++ 环境),会执行 Java 环境的初始化:

  1. 预加载 Java 类;
  2. 预加载资源;
  3. 预加载OpenGL资源;
  4. 预加载共享库;
  5. 预加载文本资源;

预加载的内容越多,子进程需要做的工作就越少。比如在 Zygote 进程初始化时,会创建 BootClassLoader,然后从磁盘中加载一些通用的 dex 文件。

这些文件经过解析加载后就是每一个 app 进程所需的通用类,比如 Activity、Fragment 等。

因为已经预加载了这些类,所以在创建新的 app 进程时直接 fork 复制 Zygote 进程,由于已经加载了这些类所以新建进程就无需再次加载了。

Zygote 进程不只是预加载这些通用类,还包含 系统资源、so 库等。子进程和父进程共享一些数据,在子进程要修改共享数据时才分配一个新的页面进行复制(“写拷贝”)。

共享内容
  1. 初始化WebView;
  2. 启动 SystemServer,通过 Zygote.forkSystemServer() fork 进程。

自此,Zygote 进入循环等待的状态,等待子进程的 fork 请求。

回答问题 2 Zygote 相关?

Zygote 启动流程主要是在开启虚拟机之后,预加载和开启 Socket,等待其它进程的 fork。

1.3 SystemServer 进程

SystemServer 进程是 Android 系统的核心之一,Zygote 初始化完毕之后就会调用相应方法创建 SystemServer。

具体方法是在 ZygoteInit 中 main 方法的后面 startSystemServer()

启动流程:

  • 1、Zygote fork SystemServer 进程
  • 2、RuntimeInit 反射执行 SystemServer main 方法
  • 3、初始化系统上下文——createSystemContext()方法
  • 4、创建 SystemServiceManager(SSM)
  • 5、通过 SSM 启动各种服务

SystemServer 的主要作用就是创建各种各样的服务:

  • 引导服务(7个)
    ActivityManagerService
    PowerManagerService
    LightsService
    DisplayManagerService
    PackageManagerService
    UserManagerService
    SensorService

  • 核心服务(3个)
    BatteryService
    UsageStatsService
    WebViewUpdateService

  • 其他服务(76个):
    AlarmManagerService
    VibratorService

各种服务创建完毕之后,启动 Android 应用启动器 Launcher 进程,打开桌面应用程序。

回答问题 3 SystemServer

SystemServer 也是 fork 出来的,之后创建 SystemServiceManager,通过它来创建各种服务。

1.4 ActivityManagerService

AMS在Android系统中扮演很重要的角色,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似。当发起进程启动或者组件启动时,都会通过Binder通信机制将请求传递给AMS,AMS再做统一处理。

到这里最开始的图中一些进程和服务都有了一个大概的认识,接下来开始分析 Activity 启动流程。

二、Activity 启动流程问题


1. App 进程如何创建,如何与 AMS 通信?

2. Application 何时创建?

3. Activity 何时创建,如何创建?

4. Window 何时创建?

三、Activity 启动流程分析

本文源码基于 API 26(Android 8.0)

流程

Launcher 其实也是一个 App,可以看作是第一个映入用户眼帘的 App。既然作为 App 就会有自己的进程,既然是进程那么同样是由 Zygote fork 出来的。

个人把 Activity 的启动流程分为三个部分:

  • Launcher 进程到 AMS 进程;
  • AMS 到应用程序进程;
  • 应用程序进程内部到 Activity 启动。

鉴于代码量比较多,我们只挑选相对重要的代码贴出来。

2.1 Launcher 到 AMS

图片来自网络

首先是 Lancher App 调用 startActivitySafely 来启动一个 App:

  • Lancher # startActivitySafely
    需要注意的是启动 Activity 创建的 Intent 设置了 Intent.FLAG_ACTIVITY_NEW_TASK 表示从新的任务栈启动;
  • Activity # startActivity
    执行 Activity#startActivityForResult 传递参数 -1,表示无需知道启动结果;
  • mInstrumentation # execStartActivity
    由 Instrumentation 的实例执行任务;

Instrumentation 可以用来监控应用与系统之间的交互,当 Activity 回调生命周期时会调用 Instrumentation 实例的相应方法。

  • ActivityManager.getService().startActivity
    通过IActivityManagerSingleton.get() 得到一个 AMS 代理对象。

回答问题 1 AMS 如何通信?

7.0及之前,AMS通过代理模式来完成Binder通信
在 Android 8.0 中,是利用 AIDL 实现的 AMS 代理对象的获取以及和 AMS 的通信。

ActivityManager # getService()

public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}
// Singleton是一个抽象类,在调用 get() 方法时会调用 create() 返回单例对象
private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                // 获取本地储存的 AMS Binder 引用
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                // 转化为 AMS 代理对象
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };

其实第一步主要做的就是获取 ActivityManagerService 的代理并由它执行 startActivity 的工作。

2.2 AMS 到 ApplicationThread

与 ApplicationThread 通信
  • ActivityManagerService # startActivity => startActivityAsUser
    这一步主要是检查调用者权限,如果没有权限或进程被隔离则抛出 SecurityException。

接下来把任务交给了 ActivityStarter:

ActivityStarter 是 Android 7.0新加入的类,它是加载 Activity 的控制类,会收集所有的逻辑来决定如何将 Intent 和 Flags 转换为 Activity,并将 Activity 和 Task 以及 Stack 相关联。

  • ActivityStarter # startActivityMayWait=>startActivity...startActivityUnchecked
  1. 创建 ActivityRecord 对象,将即将要打开的 Activity 的信息保存下来并作为参数传递下去;
  2. 创建 TaskRecord 任务栈对象,任务栈只是一个模型,并不真实存在。

创建了任务栈就可以把工作交给任务栈的管理者 ActivityStackSupervisor 了,之后进行栈顶 Activity 暂停等工作:

  • ActivityStackSupervisor # resumeFocusedStackTopActivityLocked =>
    ActivityStack # resumeTopActivityUncheckedLocked =>
    resumeTopActivityInnerLocked

转到 ActivityStack 的主要目的就是暂停栈顶的 Activity,我们常说第二个 Activity 启动第一个会进入 onPause 状态,大概就是这里实现的。

Activity # resumeTopActivityInnerLocked

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    ...
    boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
    ...
}

ActivityStackSupervisor # pauseBackStacks

boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
    boolean someActivityPaused = false;
    for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
        // 获取当前展示的 Activity 栈列表
        ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
            final ActivityStack stack = stacks.get(stackNdx);
            // 持有焦点且是 Resume 状态的 Activity
            if (!isFocusedStack(stack) && stack.mResumedActivity != null) {
                if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
                        " mResumedActivity=" + stack.mResumedActivity);
                // 接下来就是真正执行暂停了
                someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
                        dontWait);
            }
        }
    }
    return someActivityPaused;
}
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
        ActivityRecord resuming, boolean pauseImmediately) {
        ...
      ActivityRecord prev = mResumedActivity;
      if (prev.app != null && prev.app.thread != null) {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
        try {
            ...
            mService.updateUsageStats(prev, false);
            // 重要
            prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                    userLeaving, prev.configChangeFlags, pauseImmediately);
        } catch (Exception e) {
            ...
        }
    } else {
        mPausingActivity = null;
        mLastPausedActivity = null;
        mLastNoHistoryActivity = null;
    }
    ...
  }

注释处的 app.thread 就是 ApplicationThread 的实例,该类是一个 Binder 对象,可以用它来与新建 Activity 进程通信。

接下来调用 ApplicationThread # schedulePauseActivity 方法通知 Activity 进入暂停。

后面就是利用 ActivityThread 的 Handler 对象 mH 发送 PAUSE_ACTIVITY 消息,利用 mInstrumentation 回调 onPause 方法并修改 Activity 状态。

暂停完毕之后还是回到了 ActivityStackSupervisor 的 startSpecificActivityLocked 方法:

  • ActivityStackSupervisor # startSpecificActivityLocked

判断要打开的 Activity 进程是否已创建,如果没有创建则由 AMS 进行创建。

回答问题 2 进程创建流程、问题 1 Application 创建流程

  1. 调用 AMS 的方法创建进程;
  2. 再使用 ZygoteProcess 进行 Socket 发送数据(BufferedWriter write)给服务端创建进程,接收数据(DataInputStream read)接收结果。
  3. ZygoteState 负责创建读写对象以及建立 Socket 连接,framework 层的 Zygote 进程以及 Java 实现 ZygoteServer 负责 fork 进程以及接收数据;
  4. 进程创建完毕之后,反射调用 android.app.ActivityThread 的 main 方法进行主线程的创建;
  5. 将创建完毕的 ActivityThread 使用 AMS 代理进行 attach,执行创建 Application 以及创建尚未创建完成的四大组件的工作

Activity启动源码分析(3)-- 新app进程创建过程

最后一步会利用 AMS 接着创建尚未创建的四大组件,其中包括创建 Activity,其实还是回到了 ActivityStackSupervisor 的 realStartActivityLocked。

  • ActivityStackSupervisor # realStartActivityLocked =>
    app.thread.scheduleLaunchActivity
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
        boolean andResume, boolean checkConfig) throws RemoteException {
        ...
        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                System.identityHashCode(r), r.info,
                mergedConfiguration.getGlobalConfiguration(),
                mergedConfiguration.getOverrideConfiguration(), r.compat,
                r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                r.persistentState, results, newIntents, !andResume,
                mService.isNextTransitionForward(), profilerInfo);
        ...
 }

当前处于 AMS 进程,需要与要创建成功的 Activity 进程进行通信,那么媒介就是 ApplicationThread 对象,是 AppThread 的内部类,继承了 IApplicationThread.Stub,其实是一个 Binder 对象。

说到底还是通过 Binder 进行通信,告知新建 Activity 进程的 ApplicationThread 可以执行 Activity 创建了。

2.3 ActivityThread 启动 Activity

ActivityThread 和 Activity
  • ApplicationThread # scheduleLaunchActivity
    创建 ActivityClientRecord 对象,封装 Activity 信息。使用 ActivityThread 的 Handler mH 发送 LAUNCH_ACTIVITY

  • mH 接收到发送来的 ActivityClientRecord 信息,配合这些信息创建 LoadedApk 对象,该对象把 Apk 文件的信息加载到内存中,以对象的形式存在。
    接着调用 handleLaunchActivity => performLaunchActivity;

ActivityThread # performLaunchActivity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    // 创建 Context
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        // 1. newInstance 反射动态创建
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        ...
    } catch (Exception e) {
        ...
    }
    try {
        // 2. 获取 application 对象,用来给 Activity 绑定
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        if (activity != null) {
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            ...
            Window window = null;
            ...
            // 装饰模式,包裹真正的对象
            appContext.setOuterContext(activity);
            // 3. 创建 PhoneWindow,把这些信息交给 Activity 另存
            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);
            ...
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                // 设置主题
                activity.setTheme(theme);
            }

            activity.mCalled = false;
            // 4. 回调 onCreate
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
             // 5. 回调 onStart
            if (!r.activity.mFinished) {
                  activity.performStart();
                  r.stopped = false;
              }
              // 6. 回调 OnRestoreInstanceState
            if (!r.activity.mFinished) {
            if (r.isPersistable()) {
                if (r.state != null || r.persistentState != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                            r.persistentState);
                }
            } else if (r.state != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
        }
            ...

    } catch (SuperNotCalledException e) {
        throw e;
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to start activity " + component
                + ": " + e.toString(), e);
        }
    }

    return activity;
}

回答问题 3 如何创建 Activity?

  1. 要知道 Activity 各种各样,所以需要动态创建。动态创建需要 ClassLoader、全限定名(包名+类名),有了这些组件就可以反射动态创建了。

  2. 尝试获取 Application 对象,需要注意的是 makeApplication() 方法在获取时,如果已经创建则直接返回。一般来说,Application 对象在 ActivityThread 的 main 函数已经创建过了。

回答问题 4 Window 何时创建?

  1. attach() 方法主要就是赋值。先是创建了 PhoneWindow 作为 Activity 的 mWindow 对象并设置回调,之后把 Activity 的 ActivityThread 对象、Instrumentation 对象、IBinder 对象、Application 等等通过传递的参数赋值。
    这样 Activity 的数据就丰富了起来。
  2. 通过 Instrumentation 对象回调 onCreate(),为什么不直接回调呢?因为 Instrumentation 要监听所有 Activity 的动作并作记录,可以方便测试:

Instrumentation # callActivityOnCreate

public void callActivityOnCreate(Activity activity, Bundle icicle,
        PersistableBundle persistentState) {
    prePerformCreate(activity);
    // 调用了 activity 的方法
    activity.performCreate(icicle, persistentState);
    postPerformCreate(activity);
}

Activity # performCreate

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    restoreHasCurrentPermissionRequest(icicle);
    onCreate(icicle, persistentState);
    mActivityTransitionState.readState(icicle);
    performCreateCommon();
}

public void onCreate(@Nullable Bundle savedInstanceState,
        @Nullable PersistableBundle persistentState) {
    onCreate(savedInstanceState);
}
  1. 同理,使用 Instrumentation 回调 onStart()
  2. Instrumentation 回调 OnRestoreInstanceState(),所以 OnRestoreInstanceState() 是在 onStart() 之后执行的,为的是等待 Activity 准备完毕。

performLaunchActivity 执行完毕,后面就是 onResume() 的回调了,到这里 Activity 的启动流程基本就结束了。

参考:

https://www.jianshu.com/p/327f583f970b
https://blog.csdn.net/yin1031468524/article/details/56009583
https://blog.csdn.net/u012267215/article/details/91406211
https://www.cnblogs.com/ganchuanpu/p/6838725.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容