APP的启动流程梳理

时间:2024-11-17 16:16:39

前言

2022年的第一篇分享文章,整整一周,利用晚上下班时间梳理了桌面点击APP到打开的流程,可以说看似简简单单的一个操作,里边的过程相当复杂,各种进程间通信等等,下面我们一起看看整个流程是怎么样的。

正文

我们先通过一张图来看整个流程的一个概览:

在这里插入图片描述

startActivity

桌面点击app到打开,涉及了三个进程,分别如下:

Launcher进程:它是一个Activity,可以把桌面看成是一个app,里边有多个啊、其他app的入口,当点击app图标时,就会去启动对应的app,并且跳转至页面。

SystemServer进程:在Android系统中有着重要的作用,由Zygote进程fork出来,许多重要的服务,都是在此进程开启的,例如ActivityManagerService、InputManagerService和WindowManagerService等等。

APP进程:我们要启动的app的进程。

startActivity

首先我们从startActivity开始,startActivity最终会调用startActivityForResult:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);
      //...
    } else {
       //...
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这里我们先了解一下Instrumentation,每个Activity都会持有一个Instrumentation引用,整个进程只会有一个Instrumentation的实例,它主要是完成对Application和Activity初始化和生命周期的工具类。

我们可以看到,里边调用了mInstrumentation的execStartActivity方法,其中的核心代码如下:

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    //...
    try {
        //...
        int result = ActivityTaskManager.getService().startActivity(whoThread,
                who.getBasePackageName(), who.getAttributionTag(), intent,      intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

()返回的是一个ActivityManagerProxy,它就是用来与AMS进行通信的,AMS是运行在system_server进程中的,这一次的调用,实际是通过Binder的方式,调用到了AMS的startActivity方法。

fork创建新进程

调用了ActivityManagerService的startActivity方法后,经过一系列的调用,来到了startProcesslocked方法,然后会通过()方法,这次使用的是socket通信方式向zygote进程发送一个创建新进程的请求,也就是请求Zygote去创建App进程。

Zygote进程监听到有创建新进程的请求后,便会fork新的进程,并返回对应的pid。新进程创建后,然后会执行ActivityThread的main()方法。

这里说一下Zygote进程:由init进程fork出来的,当创建app进程时,都是由zygote进程fork而来的。

不知你会不会有两个疑问:

为什么SystemServer进程与Zegote进程通信不是使用Binder而是使用Socket?下面整理了网友的几点回答。

  • zegote比serviceManager先启动,这点从先后顺序看,没有serviceManager可以注册,没法用Binder。
  • 假设它们谁先启动这个顺序不确定,但是如果serviceManager先启动,但没法保证它先初始化完。
  • 在安全性上,socket的所有者是root,group是system,只有系统权限用户才能进行读写。

为什么APP进程需要由Zegote进程fork出来?

我们知道每个APP都运行在独立的Dalvik虚拟机中,如果每启动一个APP就得去单独启动跟初始化,那么是比较耗时的。Zegote进程会把已经加载好的虚拟机代码和内存信息共享,通过它fork会起到一个预加载作用,加快了app的启动。

绑定Application

创建线程后,便会执行ActivityThread的main函数,main函数里边会启动主线程的Looper。在我们初学Java的时候可以知道,main函数是一个应用程序的入口。

main函数里边会调用ActivityThread的attach方法。

//由于在main方法里边调用传进来的system为false,所以我们只看第一个分支。
private void attach(boolean system, long startSeq) {
    //...
    if (!system) {
       final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread, startSeq);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    } else {
        //...
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

attach方法中,会远程调用ActivityManagerService的attachApplication方法,ActivityManagerService最后会通过远程调用ActivityThread的bindApplication,bindApplication会发送一个BIND_APPLICATION的消息,

public final void bindApplication(String processName, ApplicationInfo appInfo,
        ProviderInfoList providerList, ComponentName instrumentationName,/*省略n个参数*/) {
//...
    sendMessage(H.BIND_APPLICATION, data);
}
  • 1
  • 2
  • 3
  • 4
  • 5

接收到此消息后,通过handleBindApplication方法进行处理,然后调用LoadedApk的makeApplication方法,由Instrumentation加载Application实例出来,

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }

    //...

    Application app = null;

    //...

    try {
        final java.lang.ClassLoader cl = getClassLoader();
       //...
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        //...
    }
  //...
    mApplication = app;

    if (instrumentation != null) {
        try {
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            //...
        }
    }
    return app;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

newApplication方法里边会去加载出Application对象,并且调用它的attach方法。

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

创建完之后,回到ActivityThread的handleBindApplication方法,会通过Instrumentation去调用Application的onCreate方法。

try {
    mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
    //...
}
  • 1
  • 2
  • 3
  • 4
  • 5

到这里Application的创建就完成了。

启动Activity

经过一连串的调用,最后会向H发送一个消息,这里的H是一个Handler,由于版本不同,所以这里会有不一样,它们最终都会调用到handleLaunchActivity方法:

public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {
    //...
    final Activity a = performLaunchActivity(r, customIntent);
	//...
    return a;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

performLaunchActivity方法里边会创建Activity,并且走onCreate

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //...

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
 //...
    } catch (Exception e) {
       //...
    }

    try {
        //...设置一些参数等,还有attach进Application
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            //...
        }
        r.setState(ON_CREATE);

    } catch (SuperNotCalledException e) {
        throw e;
    } catch (Exception e) {
        //...
    }

    return activity;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

可以看到,又有Instrumentation的身影,此方法里边通过mInstrumentation去创建Activity,然后设置参数、校验等操作后,再继续调用callActivityOnCreate方法,里边会去调用Activity的onCreate方法,至此,Activity被创建好了。

接下来就是继续通过Handler,然后去调用Activity的onStart、onResume,到这里,Activity就可见了。

小结

最后我们回顾一下整个流程:

  1. 在桌面点击app图标,Launcher响应点击事件,然后经过调用,通过Binder的方式告诉在system_server进程中的ActivityManagerService去startActivity
  2. ActivityManagerService收到调用后,便会去请求创建一个新的进程,它通过Socket的方式,告诉Zygote进程去启动一个新的进程。
  3. 新的进程启动后,会执行ActivityThread的main方法,这是程序的入口,并且会开始Looper。
  4. 在main中,会去请求ActivityManagerService进行attach Application,再经过一系列的调用,会回到app进程,创建Application,并且让Application进行attach。
  5. Application创建绑定完,便开始创建Activity,由AMS告诉APP进程去scheduleLaunchActivity,APP进程会发送一个Handler的消息,收到这个消息后由Instrumentation去创建Activity,接着继续去调用Activity的onCreate、onStart和onResume的生命周期,至此,从桌面点击APP的图标到APP启动至可见已完成。

结语

到这里我们便把APP的启动流程过了一遍,看似很简单的一个操作,实际上系统帮我们做了很多的事情。当然在源码上不同版本会存在不一样,但它们的流程基本是一样的,只是一些方法或者细节做了改变。