【再出发】Android11源码分析:进程是如何启动的?

3,046 阅读6分钟

系列文章索引

新系列:Android11系统源码解析

  1. Android11源码分析:Mac环境如何下载Android源码?

  2. Android11源码分析:应用是如何启动的?

  3. Android11源码分析:Activity是怎么启动的?

  4. Android11源码分析:Service启动流程分析

  5. Android11源码分析:静态广播是如何收到通知的?

  6. Android11源码分析:binder是如何实现跨进程的?(创作中)

  7. 番外篇 - 插件化探索:插件Activity是如何启动的?

  8. Android11源码分析: UI到底为什么会卡顿?

经典系列:Android10系统启动流程

  1. 源码下载及编译

  2. Android系统启动流程纵览

  3. init进程源码解析

  4. zygote进程源码解析

  5. systemServer源码解析

前言

众所周知,四大组件都是可以跑在单独的进程中的

在Android中,一个Application(应用)对应着一个或多个进程

对于应用开发者层来说,开启进程往往是被动开启

所谓的“被动开启”,即通过应用的组件,比如Activity,Service来启动对应的进程

今天我们通过Activity和Service的启动流程,来分析下在组件的启动过程中是如何将进程启动起来的

从Activity看如何开启一个进程

要启动一个Activity,我们首先要调用的,是startActivity()

/frameworks/base/core/java/android/app/ContextImpl.java

public void startActivity(Intent intent, Bundle options) {
       ... 
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

此处的mMainThreadActivityThread的对象实例

ActivityThread虽然名字叫Thread,但其实只是一个普通的Java类对象,继承了ClientTransactionHandler,内部持有ApplicationThread的Binder对象,用来跟ActiviyManagerService(后面简称AMS)交互

startActiviy()函数最终调用了InstrumentationexecStartActivity()

如果这个Activity已经被启动过且没有回收,则对其进行拦截

如果未被启动,则调用ActivityManagerServicestartActivity()启动对应的activity

public ActivityResult execStartActivity(
	...
        int result = ActivityTaskManager.getService().startActivity(...);
            checkStartActivityResult(result, intent);
        return null;
    }

在android11的源码中,将启动actvity的任务细化给了ActivityTaskManagerService(后面简称ATM)这个系统服务

SystemServer中启动系统服务时,ATMAMS是组合的关系,当ATM启动后再执行AMS相关的创建启动逻辑

经过一系列的流程,最终调用到了ActvitiyStarterexecute()函数

每一个开启activity的行为,都被封装成一个Request对象,调用到executeRequest()函数来开启activity

如果进程未被启动,则调用到AMSstartProcessLocked(),通过AMSZygote进行通信,fork出新的进程,并通过反射执行ActivityThread的main函数完成ActivityThread的初始化, 然后调用attachApplication()函数将binder对象注册到AMS

至此,进程初始化完成,可以继续启动进程的activity组件,创建对应的ActivityRecord对象,并添加到ActvityStack中进行管理

小结

通过activity的启动流程,我们发现在启动activty时会先检查其对应的进程(ProcessRecord)是否启动,并通过AMS和Zygote进行通信,fork出对应的进程,调用ActivityThread的main函数进行初始化操作

ActivityThread是整个进程在Java层的管理类,在main函数中,会调用prepareMainLooper()完成主线程(即创建ActvitiyThread所在的线程)Looper的初始化,并进入loop()无限循环中。所以在创建Handler对象接收消息时,主线程的Looper已经准备完成

主线程的Looper只能创建一次,且不能退出,因为主线程退出也就意味着进程的结束

从Service看进程启动

开启一个Service,首先要调用startService(),其内部又调用了startServiceCommon()

/frameworks/base/core/java/android/app/ContextImpl.java

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
            ...
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManager.getService().startService( //AMS binder调用
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                    getOpPackageName(), getAttributionTag(), user.getIdentifier());
            ...
            return cn;
       
    }

其中 ActivityManager.getService() 是通过ServiceManager获取到AMS的binder对象进行binder调用,执行到AMS中的startService()方法,最终调用到bringUpServiceLocked()函数

如果serviceRecord中的ProcessRecord对象和ApplicationThread对象不为空,表示进程已经启动,可以直接启动Service(通过binder调用ActivityThread中的scheduleServiceArgs(),通过hander发送消息,交给mH进行进一步初始化)

Service作为四大组件之一,是可以跑在单独的进程空间中的,此时我们需要通过AMS查询该Service是否又对应的ProcessRecord

如果获取到service对应的ProcessRecord不为空,说明宿主进程(HostingRecord)已经启动

假如需要启动的Service是单独的进程(isolated),则需要通过AMS与Zygote交互fork出子进程

如果跟宿主进程是同一个进程,则调用realStartServiceLocked()去启动service组件

/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);//判断service所在的进程是否启动
            if (app != null && app.thread != null) { //说明进程已经启动
                
                    app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                    realStartServiceLocked(r, app, execInFg); //启动service组件
                    return null;
              
            }
        } else { 
                ...
                hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName,
                        r.definingUid);
            
        }

如果未获取到宿主进程的ProcessRecord,说明宿主进程也没有启动,需要先启动对应的进程, 需要通过AMS与Zygote通信fork出新的进程,此处流程与启动activiy处的流程判断是一致的

if (app == null && !permissionsReviewRequired) { //如果开启进程时app为空,则需要先通过AMS启动对应的进程
            ...
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, 
                    hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
                ...
            }
           
        }

小结

service启动时,需要判断是否是独立的进程,如果是独立进程,则fork出一个新进程;如果不是独立进程,先判断下进程是否已经启动,已启动时直接调用realStartServiceLocked()去执行service组件的启动流程即可,如果未启动则仍需要通过AMS与zygote通信fork出新的进程

加餐:聊聊Binder调用那些事

ActivityThread是应用层进程的入口类, 在上面的分析中,我们反复提到了binder调用

所有的系统服务都需要注册到ServiceManager中进行管理

下面是通过ServiceManager获取对应的ActivityManagerService的binder对象的函数

/frameworks/base/core/java/android/app/ActivityManager.java

protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }

应用层需要调用系统服务的函数时,需要获取到对应的系统服务的binder对象,从而调用系统服务中的函数

比如在启动activity和service时都调用了ActivityManagerServcie中的方法

ActivityThread内部,也持有一个ApplicationThread的binder对象,在完成java层的应用初始化后,同样需要调用AMS的attach()方法将这个binder对象传递过去,以完成AMS和ActivityThread的双向调用,AMS也可以通过持有的这个binder对象与ActivityThread进行通信

关于更多的binder细节,我们将在以后的文章中来探讨

写在最后

如果觉得我的文章对你有价值,请多多点赞支持下

接下来我们将探讨四大组件相关的源码内容

下期文章再见啦