Android 7.0 ActivityManagerService(3) 启动Activity的过程:二

这一篇博客,我们将继续学习Activity的启动流程。
启动Activity的过程:一中,我们的流程最终分析到AMS通过zygote启动Activity对应的进程,现在我们看看后续的过程如何进行。
关于zygote启动进程的流程,可以参考Android6.0 SystemServer进程。这篇文章中,分析了zygote如何启动SystemServer进程和普通进程。虽然分析的是Android M的代码,但与Android N的思路基本一致。

一、ActivityThread的main函数

通过zygote启动进程时,传入的className为android.app.ActivityThread。
因此,当zygote通过反射调用进程的main函数时,ActivityThread的main函数将被启动:

public static void main(String[] args) {
    ..................
    //开始信息采样
    SamplingProfilerIntegration.start();

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    //和调试有关
    CloseGuard.setEnabled(false);

    //得到当前进程的UserEnvironment
    Environment.initForCurrentUser();
    ...............
    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    // 确保进程能够得到CA证书的路径
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");

    //准备主线程的Looper
    Looper.prepareMainLooper();

    //创建当前进程的ActivityThread
    ActivityThread thread = new ActivityThread();
    //调用attach函数
    thread.attach(false);

    if (sMainThreadHandler == null) {
        //保存进程对应的主线程Handler
        sMainThreadHandler = thread.getHandler();
    }
    .........
    //进入主线程的消息循环
    Looper.loop();

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

从上述代码可以看出,ActivityThread的main函数最主要工作是:
1、创建出一个Looper,并将主线程加入到消息循环中。
2、创建出ActivityThread,并调用其attach函数。

在介绍AMS的启动过程时,我们就提到了ActivityThread。
当时从代码中,我们知道SystemServer进程,为了融入Android体系,调用createSystemContext函数。
在createSystemContext函数中,SystemServer进程创建了自己的ActivityThread,并调用了attach函数。
通过比对代码容易看出,SystemServer进程作为系统进程,attach参数为true,而普通进程传入的参数为false。

现在,我们重新看看ActivityThread的attach函数,这次侧重于普通进程的部分:

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        ViewRootImpl.addFirstDrawHandler(new Runnable() {
            @Override
            public void run() {
                //JIT-Just in time
                //JIT技术主要是对多次运行的代码进行编译,当再次调用时使用编译之后的机器码,而不是每次都解释,以节约时间

                //JIT原理:
                //每启动一个应用程序,都会相应地启动一个dalvik虚拟机,启动时会建立JIT线程,一直在后台运行。
                //当某段代码被调用时,虚拟机会判断它是否需要编译成机器码,如果需要,就做一个标记。
                //JIT线程在后台检测该标记,如果发现标记被设定,就把对应代码编译成机器码,并将其机器码地址及相关信息保存起来
                //当进程下次执行到此段代码时,就会直接跳到机器码执行,而不再解释执行,从而提高运行速度

                //这里开启JIT,应该是为了提高android绘制的速度
                ensureJitEnabled();
            }
        });

        //设置在DDMS中看到的进程名为"<pre-initialized>"
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>", UserHandle.myUserId());

        //设置RuntimeInit的mApplicationObject参数
        RuntimeInit.setApplicationObject(mAppThread.asBinder());

        final IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            //与AMS通信,调用其attachApplication接口
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }

        // Watch for getting close to heap limit.
        // 监控GC操作; 当进程内的一些Activity发生变化,同时内存占用量较大时
        // 通知AMS释放一些Activity
        BinderInternal.addGcWatcher(new Runnable() {
            public void run() {
                if (!mSomeActivitiesChanged) {
                    return;
                }

                Runtime runtime = Runtime.getRuntime();
                long dalvikMax = runtime.maxMemory();
                long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                //判断内存占用量是否过大
                if (dalvikUsed > ((3*dalvikMax)/4)) {
                    ...........
                    mSomeActivitiesChanged = false;
                    try {
                        //通知AMS释放一些Activity,以缓解内存紧张
                        mgr.releaseSomeActivities(mAppThread);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
        });
    } else {
        .............
    }
    ........
}

在分析Activity启动过程的第一部分时,我们提到过AMS创建一个应用进程后,会设置一个超时时间。
如果超过这个时间,应用进程还没有和AMS交互,AMS就认为该进程创建失败。
因此,应用进程启动后,需要尽快和AMS交互。

上述代码中,attachApplication就是应用进程与AMS交互的接口。

二、AMS的attachApplication函数

我们进入AMS看看attachApplication相关的流程。

public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        //进一步调用attachApplicationLocked函数
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}

attachApplicationLocked函数比较长,分段来看一下。

1 Part-I

private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
    ProcessRecord app;

    //根据pid查找对应的ProcessRecord对象
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid);
        }
    } else {
        app = null;
    }

    //如果进程由AMS启动,则它在AMS中一定有对应的ProcessRecord
    //此处app为null,则表示AMS没有该进程的记录,故需要kill掉此异常进程
    if (app == null) {
        ................
        if (pid > 0 && pid != MY_PID) {
            //pid大于0且不是系统进程,则直接kill掉
            //将调用android_util_Process.cpp中的android_os_Process_sendSignalQuiet函数
            //最终通过kill函数杀死进程,kill函数要求pid > 0
            Process.killProcessQuiet(pid);
        } else {
            try {
                //pid < 0时,fork进程失败,因此仅上层完成清理工作即可
                //调用ApplicationThread的scheduleExit函数
                //应用进程将进行一些扫尾工作,例如结束消息循环,然后退出运行
                thread.scheduleExit();
            } catch (Exception e) {
                // Ignore exceptions.
            }
        }
        return false;
    }

    // If this application record is still attached to a previous
    // process, clean it up now.
    // 判断pid对应processRecord的IApplicationThread是否为null
    // AMS创建ProcessRecord后,在attach之前,正常情况下IApplicationThread应该为null

    // 特殊情况下:如果旧应用进程被杀死,底层对应的pid被释放,在通知到达AMS之前(AMS在下面的代码里注册了“讣告”接收对象),
    // 用户又启动了一个新的进程,新进程刚好分配到旧进程的pid时
    // 此处得到的processRecord可能就是旧进程的,于是app.thread可能不为null,因此需要作判断和处理
    if (app.thread != null) {
        handleAppDiedLocked(app, true, true);
    }
    ...................
    final String processName = app.processName;
    try {
        //创建一个“讣告”接收对象,注册到应用进程的ApplicationThread中
        //当应用进程退出时,该对象的binderDied将被调用,这样AMS就能做相应的处理
        //binderDied函数将在另一个线程中被调用,其内部也会调用handleAppDiedLocked函数
        AppDeathRecipient adr = new AppDeathRecipient(
                app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
        app.deathRecipient = adr;
    } catch (RemoteException e) {
        app.resetPackageList(mProcessStats);
        startProcessLocked(app, "link fail", processName);
        return false;
    }
    设置app的一些变量,例如调度优先级和oom_adj相关的成员
    .................
    //启动成功,从消息队列中移除PROC_START_TIMEOUT_MSG
    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
    .................

至此,attachApplicationLocked的第一部分介绍完毕。
这部分代码的核心功能比较简单,其实就是:
1、判断进程的有效性,同时注册观察者监听进程的死亡信号。
2、设置pid对应的ProcessRecord对象的一些成员变量,例如和应用进程交互的IApplicationThread对象、进程调度的优先级等。
3、进程注册成功,AMS从消息队列中移除PROC_START_TIMEOUT_MSG。

2 Part-II

现在,我们看看attachApplicationLocked第二部分的代码:

..............
//AMS正常启动后,mProcessesReady就已经变为true了
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);

//generateApplicationProvidersLocked将通过PKMS查询定义在进程中的ContentProvider,并将其保存在AMS的数据结构中
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

//这里应该是处理:加载ContentProvider时,启动进程的场景
//checkAppInLaunchingProvidersLocked主要将当前启动进程的ProcessRecord,和AMS中mLaunchingProviders的ProcessRecord进行比较
//当判断出该进程是由于启动ContentProvider而被加载的,那么就发送一个延迟消息(10s)
//通过这里可以看出,当由于加载ContentProvider启动进程时,在进程启动后,ContentProvider在10s内要完成发布
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
    Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
    msg.obj = app;
    mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
...........
try {
    ...........
    //回调进程ApplicationThread的bindApplication接口
    thread.bindApplication(..........);

    //更新进程调度策略
    updateLruProcessLocked(app, false, null);
    ..............
} catch () {
    ............
    app.resetPackageList(mProcessStats);
    app.unlinkDeathRecipient();
    //这里的策略比较激进,当bindApplicaiton失败后,将直接重新启动这个进程
    startProcessLocked(app, "bind fail", processName);
    return false;
}

从代码来看,第二阶段最核心工作就是:
调用进程ApplicationThread的bindApplication函数,接下来我们分析一下该函数。

2.1 ApplicationThread的bindApplication函数

从之前的代码,我们知道应用进程由zygote fork得到,然后调用ActivityThread的main函数,进入到Java世界。
但是截至目前,该进程并没有融入到Android的体系中,因此仅能被称为一个Java进程,甚至连进程名也只是“敷衍”地定义为“pre-initialized”。

我们之前分析AMS启动过程时,介绍了SystemServer的createSystemContext函数。
在该函数中,SystemServer在自己的进程中,创建出Android运行环境,才摇身一变成为了Android进程。
同样,此处的bindApplication函数,就是在新进程中创建并初始化对应的Android运行环境。

现在,我们看看bindApplication函数的主要流程:

//参数较多,无需深究,重要的从代码流程中就能知道含义
public final void bindApplication(.......) {
    //按照string-IBinder的方式,保存AMS传递过来的系统service的Binder接口
    //这样进程与系统服务通信时,就不需要先通过SystemServer查询了
    if (services != null) {
        // Setup the service cache in the ServiceManager
        ServiceManager.initServiceCache(services);
    }

    //内部发送H.SET_CORE_SETTINGS消息
    //由handleSetCoreSettings进行处理,主要用于保存新的信息
    setCoreSettings(coreSettings);

    //用AppBindData对象保存参数对应的信息
    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providers;
    ..................
    //发送消息
    sendMessage(H.BIND_APPLICATION, data);
}

由以上代码可知,ApplicationThread的接口被AMS调用后,会将参数保存到AppBindData对象中,然后发送消息让ActivityThread的主线程处理。
由此可以看出,对应用进程而言,ApplicationThread只是与AMS通信的接口,实际的工作一般还是会交给ActivityThread来完成。

ActivityThread中处理该消息的实际函数为handleBindApplication,我们看看这个函数的内容。

2.2 handleBindApplication函数

private void handleBindApplication(AppBindData data) {
    // Register the UI Thread as a sensitive thread to the runtime.
    VMRuntime.registerSensitiveThread();
    .................
    //初始化性能统计对象
    mProfiler = new Profiler();
    if (data.initProfilerInfo != null) {
        mProfiler.profileFile = data.initProfilerInfo
  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值