yunshuipiao / Potato

Read the fucking source code for the Android interview
Apache License 2.0
80 stars 12 forks source link

ActivityThread: app launche flow #20

Open yunshuipiao opened 5 years ago

yunshuipiao commented 5 years ago

ActivityThread: app launche flow

[TOC]

APP 的启动 简单叙述

这里先借用一张图展示整个流程。

image

上面有很多的名词概念,具体了解需要下载源码编译去看,因为这里做个简单解释

Zygote 进程

在 Android 系统中,Zygote 是一个进程的名字。因为 Android 是基于 Linux 系统的,当手机开机的时候,Linux 的内核加载完成之后就会启动一个叫 init 的进程,后面所有的进程都有该进程 fork 而来,就包括 Zygote 进程。

在 Zogyte 进程初始化之后,就会再孵化 System Server 进程,是 Zygote 孵化的第一个进程。并且还会孵化第一额 APP 进程,即上面的 Launcher 进程,即桌面。之后还有浏览器,邮件的系统 APP 进程。每一个 App 都是一个单独的进程。

之后,再开启 App, 就相当于开启一个新的进程,通过 fork 第一个 Zygote 进程实现。

SystemServer 进程

该进程和 Zygote 进程是 Android java 最重要的部分,其中一个出问提都会导致系统崩溃。

系统最重要的服务都是在该进程开启,比如 ActivityManagerService, PackageManagerService, WindowManagerService 等。这些系统服务会在 Zygote 进程开启的时候,调用 Zygoteinit.main() 进程初始化。

ActivityManagerService

简称 AMS,服务端对象,负责系统中所有 Activity 的生命周期。在 SystemServer进程开启时进行初始化。

服务端和客户端

在 Android 中也有分服务端和客户端。服务端指所有 App 共用的系统服务,比如 AMS,PMS,WMS等。

因为每个App 都是单独的进程,所以在调用 startActivity() 时并不能直接打开另一个 App, 这是会通过各种调用,最后告诉 AMS 打开目标 App。

这里的 App, AMS(SystemService)和 Zygote 是三个不同的进程,前两者通过 Binder 进行 IPC 通信,后两者通过 Socker 进行 IPC 通信。

在Android 系统中, 任何一个 Activity 的启动都是由 AMS 和应用程序进程(主要是 ActivityThread)相互配合完成。

Instrumentation

每个 Activity 都持有 Instrumentation 对象的一个引用,但是整个进程只会存在一个 Instrumentation 对象。

调用 startActivity() 的时候,实际上还是调用该对象相关的方法。

// 调用 Activity 的入口函数 onCreate()
final void performCreate(Bundle icicle) {
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

ActivityThread

接触最多的类,就是 Android 中的 UI 线程。App和AMS是通过Binder传递信息的,那么ActivityThread就是专门与AMS的外交工作的。 举例:所以说,AMS是董事会,负责指挥和调度的,ActivityThread是老板,虽然说家里的事自己说了算,但是需要听从AMS的指挥,而Instrumentation则是老板娘,负责家里的大事小事,但是一般不抛头露面,听一家之主ActivityThread的安排。

启动流程

有了上面的基本概念,下面简单介绍一下 App 的启动流程。

当点击桌面 App 的时候,系统已经完成的 Zygote 的初始化,并孵化出 SystemServer 和 Launcher 进程。

此时 Launcher 进程需要启动远程进程,利用 Binder 发送消息给 SystemServer 进程,会先调用 AMS.startProcessLocked() 方法,内部调用 Process.start(ActivityThread); 接着通过 Zygote fork 出需要的子进程,即 app 进程。创建之后将 ActivtyThread 加载进去,执行其 main() 方法。

public static void main(String[] args) {

        ...

    Process.setArgV0("<pre-initialized>");
        // 创建Looper和MessageQueue对象,用于处理主线程的消息
    Looper.prepareMainLooper();

    // 创建 ActivityThread 
    ActivityThread thread = new ActivityThread();
    // 建立Binder通道 (创建新线程)
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // 开启循环,接收消息
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

上面是 ActivityThread.main() 的主要内容,调用 attach() 进行 Binder 通信,looper 启动循环。attach() 内部获取 ActivityManagerProxy 对象,其实现了 IActivityManager 接口,作为客户端调用 attachApplication(mAppThread), 将 thread 信息告诉 AMS。

在 SystemServer 进程中,AMS 会调用 ActivityMangerNative.onTransact() 方法,整个逻辑在服务端 AMS.attachApplication() 方法中,内部调用 AMS.attachApplicationLocked 方法,用于跟之前通过 Process.start() 所创建的进程中 ApplicationThread 对象进行通信。

attachApplicationLocked 方法会处理 Provider, Activity,Service,Broadcast 的相应流程。调用 ApplicationThreadProxy.bindApplication 方法,通过 Binder 通信,传递给 ApplicationThreadNative.onTransact 方法。

在 app 进程中,真正的逻辑在 ActivityThread.bindApplication 方法中。 bindApplication 方法的主要功能是依次向主线程发送消息 H.SET_CORE_SETTINGS 和H.BIND_APPLICATION 。后续创建 Application,Context等。 Activity 的回调也会是通过Binder通信,然后发送不同消息处理。

上述就是 app 进程启动的大概流程。

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

上面的代码中有一个问题,在初始化 UI 线程并开启 loop 循环之后,由于无消息处理,为什么不会被卡住。

首先明确线程和进程的概念:app 运行之前之前创建进程,承载 app 上面的四大组件的运行,大多数情况下都运行在同一个进程中。而线程非常常见,包括 UI 线程,与所在进程共享资源,因此从 linux 角度讲,除了是否共享资源外,并无其他区别。

对于线程来说,是一段可执行的代码,执行完毕生命周期终止,线程退出。而对于 UI 线程,则不希望执行完就退出,否则没法与用户交互。这时就需要代码一直执行下去,所以用了死循环,保证不会被退出。

真正会卡死主线程的操作是在回调方法 onCreate/onStart/onResume 等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop 本身不会导致应用卡死。

在上面的代码中,thread.attach(false) 会创建以一个 binder 线程(ApplicationThread, 服务端,用于接收系统服务 AMS 发送来的消息),该 Binder 线程通过 Handler 将 Message 发送给主线程处理。

其实整个 Android 就是在一个 Looper 的 loop 循环的,整个 Android 的一切都是以Handler机制进行的,即只要有代码执行都是通过 Handler 来执行的,而所谓 ANR 便是 Looper.loop 没有得到及时处理,一旦没有消息,Linux 的 epoll 机制则会通过管道写文件描述符的方式来对主线程进行唤醒与沉睡,Android 里调用了 linux 层的代码实现在适当时会睡眠主线程,因此不回卡死程序。

总结: Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。

入口main()到Applicaiton的onCreate()方法被调用

ActivityThread 中提供了两个核心内部类 ApplicationThread 和 H。

final ApplicationThread mAppThread = new ApplicationThread();
final H mH = new H();

 private class ApplicationThread extends IApplicationThread.Stub {}
class H extends Handler {}

在 main() 函数中, ActivityThread.attach(false) 又会最终到 AMS 的attachApplication,这个工作其实是将本地的 ApplicationThread 传递到 AMS。然后 AMS 就可以通过 ApplicationThread 的代理 ApplicationThreadProxy 来调用应用程序 ApplicationThread.bindApplication, 通知应用程序的 ApplicationThread 已和 AMS 绑定,可以不借助其他进程帮助直接通信了。此时Launcher的任务也算是完成了。

在 bindApplication() 方法中有两个地方比较重要:

public final void bindApplication(...) {
  // 发送消息完成设置, handleSetCoreSettings
    setCoreSettings(coreSettings);
  // 发送消息绑定 handleBindApplication
  sendMessage(H.BIND_APPLICATION, data);
}

这里使用 H 的目的是, 将代码执行的逻辑从 binder 线程池里的线程切换到主线程。

private void handleBindApplication(AppBindData data) {
    // application 对象
         Application app;
        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
                // 实例化 app
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) {
                // 如果 providers 不为空,  初始化并启动
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

            // Do this after providers, since instrumentation tests generally start their
            // test thread at this point, and we don't want that racing.
            try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
            try {
                // application 调用 onCreate() 方法
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!mInstrumentation.onException(app, e)) {
                    throw new RuntimeException(
                      "Unable to create application " + app.getClass().getName()
                      + ": " + e.toString(), e);
                }
            }
        } 
}

在创建 app 的函数中,接着会调用 LoadedApk(已加载 apk 的信息)的 方法:

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    // 一个进程保证只有一个
    if (mApplication != null) {
        return mApplication;
    }

    app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
}  
// 创建 app
public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = getFactory(context.getPackageName())
            .instantiateApplication(cl, className);
    app.attach(context);
    return app;
}
// Application
/* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }

        // ContextWrapper.java
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

总结

本篇文章简单介绍了Android 程序中的几个重要 进程和 App 启动的大概流程,涉及 ActivityThread.main(), 开始消息循环,以及 Application 创建到 执行 onCreate() 的大概过程。

多看源码,多看源码,多看源码。