Launcher应用Activity启动流程(初始篇)

355 阅读4分钟

「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。

Launcher应用Activity启动流程(初始篇)

从前述 应用进程启动流程分析(客户端篇)应用进程启动流程分析(服务端篇)中,我们以Launcher应用进程的启动流程,描述了应用进程的FORK和启动流程,那么接下来我们接着前两篇继续分析一下Launcher应用Activity启动流程。

在分析ActivityThread的main函数之前,我们有必要先整理一下ActivityThread类的基本结构,如下:

classDiagram
ClientTransactionHandler <|-- ActivityThread
Handler <|-- H
ActivityThread *-- H
ActivityThread *-- AppBindData
ActivityThread *-- ActivityClientRecord
ActivityThread *-- ApplicationThread
IApplicationThread *-- Stub
Stub <|-- ApplicationThread
<<abstract>> ClientTransactionHandler
<<interface>> Stub
class ActivityThread {
    +main(String[] args) void
    -attach(boolean system, long startSeq) void
    -installContentProviders(Context context, List<ProviderInfo> providers) void
    -handleBindApplication(AppBindData data) void
    +handleLaunchActivity(ActivityClientRecord, PendingTransactionActions, Intent) Activity
}
class ClientTransactionHandler {
    #scheduleTransaction(ClientTransaction transaction) void
}
class H {
    +handleMessage(Message msg) void
}

从这边可以看到,ActivityThread类继承自ClientTransactionHandler抽象类,且包含有四个内部类,其中

  1. H类继承自Handler,其重写了Handler的handleMessage函数
  2. ApplicationThread类继承自IAppliactionThread.Stub对象,使用Binder通信传递数据
  3. AppBindData保存有应用进程的一些基本信息

接下来,我们继续分析ActivityThread的main函数具体做了什么

public static void main(String[] args) {
    // ......
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    // ......
}

在ActivityThread的main函数中主要初始化ActivityThread对象并调用其attach函数。

不过,在分析attach函数之前,首先我们要先确认一下其第二个参数startSeq具体是什么?

ActivityThread的attach函数的第二个参数startSeq表示什么

追踪这个startSeq参数值,可以看到,其在ActivityThread的main函数,通过传入参数args中包含"seq="字串的对应参数值

public static void main(String[] args) {
    // ......
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    // ......
}

而main函数的args是根据进程启动过程中传递的参数,说到进程启动的流程,服务端的参数数据是通过zygote的socket从客户端传递过来的,因此,追溯到客户端的代码可知

ProcessList.java
boolean startProcessLocked(......) {
    // ......
    final long startSeq = app.startSeq = ++mProcStartSeqCounter;
    // ......
    mPendingStarts.put(startSeq, app);

    // ......
    final Process.ProcessStartResult startResult = startProcess(hostingRecord,
            entryPoint, app,
            uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
            requiredAbi, instructionSet, invokeWith, startTime);
    // ......
}

private Process.ProcessStartResult startProcess(......) {
    // ......
    // 注意最后一个参数
    startResult = Process.start(......, new String[]{PROC_START_SEQ_IDENT + app.startSeq});
    // ......
}

ZygoteProcess.java
private Process.ProcessStartResult startViaZygote(args[......]
                                                  @Nullable String[] extraArgs)
                                                  throws ZygoteStartFailedEx {
    // ......

    if (extraArgs != null) {
        Collections.addAll(argsForZygote, extraArgs);
    }
    // ......
    return zygoteSendArgsAndGetResult(......, argsForZygote);
    // ......
}

因此,上述ActivityThread对象的attach函数中传递的startSeq参数的数值是从进程启动过程中,在ProcessList对象中生成的一个索引值,这个索引值和对应的进程启动包含Activity信息的ProcessRecord对象生成<key, value>,并保存在ProcessList的mPendingStarts参数对象中

ActivityThread的attach函数运行

此后,我们来看一下ActivityThread的attach函数具体做了哪些操作

final ApplicationThread mAppThread = new ApplicationThread();

private void attach(boolean system, long startSeq) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    // 传入参数false
    if (!system) {
        // ......
        // 获取对应的ActivityManagerService对象
        final IActivityManager mgr = ActivityManager.getService();
        try {
            // 调用AMS的attachApplication函数
            mgr.attachApplication(mAppThread, startSeq);
        }
        // ......
        // Watch for getting close to heap limit.
        // 监听当前的堆内存信息,及时回收内存空间
        BinderInternal.addGcWatcher(new Runnable() {
            @Override public void run() {
                // ......
            }
        });
    }
    // ......
    ViewRootImpl.ConfigChangedCallback configChangedCallback
            = (Configuration globalConfig) -> {
        // ......
    };
    ViewRootImpl.addConfigCallback(configChangedCallback);
}

在这个函数中主要调用AMS的attachApplication函数注,此处还添加了一个堆内存信息监听,用作及时释放和回收对应的内存空间

ActivityManagerService.java
public final void attachApplication(IApplicationThread thread, long startSeq) {
    // ......
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid, callingUid, startSeq);
        Binder.restoreCallingIdentity(origId);
    }
}

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
    // ......
    // 可以看到,此处直接从ProcessList的mPendingStarts参数对象中获取key为startReq对应的ProcessReocrd对象
    // 当然,此处是Launcher应用对应的启动Activity的信息
    // It's possible that process called attachApplication before we got a chance to
    // update the internal state.
    if (app == null && startSeq > 0) {
        final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
        if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
                && mProcessList.handleProcessStartedLocked(pending, pid, pending
                        .isUsingWrapper(),
                        startSeq, true)) {
            app = pending;
        }
    }

    // ......
    
    // 获取当前的应用中包含的所有的ContentProvider信息
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    // ......
    final BackupRecord backupTarget = mBackupTargets.get(app.userId);
    try {
        // ......
        ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
        app.compat = compatibilityInfoForPackage(appInfo);

        // ......

        mAtmInternal.preBindApplication(app.getWindowProcessController());
        final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
        if (mPlatformCompat != null) {
            mPlatformCompat.resetReporting(app.info);
        }
        final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
        // ......
        thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
                null, null, null, testMode,
                mBinderTransactionTrackingEnabled, enableTrackAllocation,
                isRestrictedBackupMode || !normalMode, app.isPersistent(),
                new Configuration(app.getWindowProcessController().getConfiguration()),
                app.compat, getCommonServicesLocked(app.isolated),
                mCoreSettingsObserver.getCoreSettingsLocked(),
                buildSerial, autofillOptions, contentCaptureOptions,
                app.mDisabledCompatChanges);

        // Make app active after binding application or client may be running requests (e.g
        // starting activities) before it is ready.
        // 注意,ProcessRecord对象的makeActive函数会将当前的ApplicationThread对象设置给ProcessRecord
        // 此处在后续的分析中会用到
        app.makeActive(thread, mProcessStats);
        // ......
    }
    
    // See if the top visible activity is waiting to run in this process...
    if (normalMode) {
        try {
            didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
            badApp = true;
        }
    }
    
    // ...... 本篇将只讨论Activity的启动流程,后续的服务,广播等启动暂不做分析
    return true;
}

从这边可以看到,最终会

  1. 调用到此处的ApplicationThread对象的bindApplication函数, 来启动整个Application
  2. 此后会通过调用ActivityTaskManagerService.LocalService对象的attachApplication函数来启动Activity,进入Activity的整个声明周期