在安卓系统中诸多应用程序都会在桌面应用Launcher中创建一个shortcut启动图标,当我们点击这个图标就可以进入其所指示的应用程序首页,这个过程是通过调用ActivityManagerService(以下简称AMS)的方法startActivity()完成的。当调用startActivity()后AMS会首先判断当前进程是否已经创建,如果没有创建则会进入应用进程的创建流程。从这期间涉及到的进程来看,大概流程如下:
当然,上述AMS服务是寄生在SystemServer进程中(对AMS的启动过程不熟悉的同学可以参考深入理解AMS之启动过程这篇文章),此处为了直观描述,单独把AMS给拎出来了。
从流程上来说,调用startActivity()到真正应用进程的页面显示过程经历了多个进程间的协作,甚至这些进程间的通信手段也不尽相同。
AMS中startActivity()方法最终会调用到ActivityStaskSupervisor(以下简称ASS)的startSpecificActivityLocked()中,这个方法正是是否需要启动新进程来承载ActivityRecord的关键点。
// frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
r.task.stack.setLaunchTime(r);
if (app != null && app.thread != null) {//如果当前要启动的Activity已经有承载进程
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
mService.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
}
//执行到此处说明当前要启动的Activity的进程还未启动,需等待进程启动后再执行Activity操作。
//此处调用startProcessLocked启动进程
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
通过阅读上述代码我们可以看到当前ActivityRecord承载进程是否存在的判断逻辑是app.thread,app.thread是一个Binder,当ActivityThread(即应用进程)启动时会通过attachApplication()方法将这个binder传给AMS以作为后续AMS与ActivityThread的binder通信接口。在此处如果app.thread为空则说明对应的ActivityThread并未创建,此后即进入应用进程的创建流程。
上述方法最后通过调用AMS.startProcessLocked()方法来创建了一个新进程。具体的调用时序图如下:
在AMS中有多个重载的startProcessLocked()方法,但最终多调用到以下startProcessLocked()中。
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
//part1 清理环境
if (app.pid > 0 && app.pid != MY_PID) {
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);//移除进程记录
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);//移除对app进程的启动超时计算
}
app.setPid(0);//清空pid
}
mProcessesOnHold.remove(app);
updateCpuStats();//更新cpu使用状态
try {
//part2 gid的获取
int uid = app.uid;
int[] gids = null;
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
int[] permGids = null;
try {
final PackageManager pm = mContext.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName);
//...
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}
if (permGids == null) {
gids = new int[2];
} else {
gids = new int[permGids.length + 2];
System.arraycopy(permGids, 0, gids, 2, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
}
//...
//part3 flags与abi的获取
int debugFlags = 0;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
//...
String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
if (requiredAbi == null) {
requiredAbi = Build.SUPPORTED_ABIS[0];
}
//...
boolean isActivityProcess = (entryPoint == null);
//part4 启动进程
if (entryPoint == null) entryPoint = "android.app.ActivityThread";//启动的目标类
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);//内部通过socket通知Zygote创建进程并返回结果
//part5 进程启动后扫尾工作
app.setPid(startResult.pid);
app.usingWrapper = startResult.usingWrapper;
app.removed = false;
app.killed = false;
app.killedByAm = false;
synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(startResult.pid, app);
if (isActivityProcess) {
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, startResult.usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);//超时计算,超时时间10s
}
}
} catch (RuntimeException e) {
app.setPid(0);
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
}
}
从startProcessLocked()代码的内容来看,大概可以分为5个部分:
应用进程启动是通过Process.start()完成的,也就是上述的步骤4。这个方法的具体内容如下:
// frameworks/base/core/java/android/os/Process.java
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);//via经由
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
start via zygote英文含义是“通过zygote孵化器启动”,由此可见framework方法取名的良苦用心了。上述Process.start()只是作为调用链中转站,真正发起应用进程的启动请求正是通过此处的startViaZygote()方法完成。
// frameworks/base/core/java/android/os/Process.java
private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
//part1 启动参数的设置
ArrayList<String> argsForZygote = new ArrayList<String>();
argsForZygote.add("--runtime-init");//关注点
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
//...
//要启动的进程class,一般为ActivityThread
argsForZygote.add(processClass);//关注点
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
//part2 连接socket,发送进程启动请求并获取结果
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);//此处之后展开来说
}
}
argsForZygote集合用于存放进程启动配置项,这些配置项一般以–开头且是有序存放,比较重要的是我们的启动类processClass即ActivityThread也存放到了这个集合中。此处配置项的处理乃是startViaZygote()方法的第一部分内容。
在处理完应用进程的启动配置项后,接着就要着手创建进程了,这就是通过方法openZygoteSocketIfNeeded()和zygoteSendArgsAndGetResult()完成的,前者负责创建socket连接Zygote进程,后者则通过socket完成进程配置项的传递并获取启动结果。
上一小节的末尾我们知道了Process.start()方法启动进程最终是通过连接Zygote的server socket来实现的。那么Zygote又是什么,它从哪里来,又负责什么内容?以及通过socket方式有是如何connect完成进程间通信呢?
Zygote是一个孵化器进程,它由init进程中通过解析init.rc方式启动,属于安卓系统的主要进程之一,其职责主要负责孵化所有的java进程。java应用程序启动及运行一般都需要一个承载进程,这个需求通过android socket的方式(注意此处并非java socket,java socket通过监听端口形式完成信息传递,而android socket则是通过文件读写方式传递消息的,机制完全不同)传给Zygote进程,Zygote则内部通过fork函数变异出一个子进程来作为这个java应用程序的承载进程。
那么为什么java程序的进程创建一定要通过Zygote进程呢?
这是由于Zygote进程中,在fork之前预先已将Android的java层字节码文件、系统资源等load到了内存中,这样fork出来的新进程就不需要反复做这些工作了。我们所熟知的system_server进程就是通过Zygote孵化出来的一个典型例子。
我们续着上一小节的内容,来看看Process是如何创建socket以及连接Zygote进程的。
// frameworks/base/core/java/android/os/Process.java
public static final String ZYGOTE_SOCKET = "zygote";//socket 1
public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";//socket 2
private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
}//连接socket,如若连接成功则返回
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
}//socket1没有连上,则继续尝试socket2,同样连上则返回
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
//若两个socket都没有连上,则返回异常
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
}
Zygote进程一共为进程孵化准备了两个sokcet通道,这在openZygoteSocketIfNeeded()方法中也可以看出来。上述方法先试图连接primaryZygoteState主socket(socket名称为zygote),如果主socket连接不上,则再试图连接secondaryZygoteState即副socket(socket名称为zygote_secondary),如若两者都连接失败则说明打开socket通道失败,返回异常。
在openZygoteSocketIfNeeded()中,android socket的连接是由ZygoteState的connect()方法完成。
// frameworks/base/core/java/android/os/Process.java$ZygoteState
public static ZygoteState connect(String socketAddress) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
zygoteSocket.connect(new LocalSocketAddress(socketAddress,
LocalSocketAddress.Namespace.RESERVED));
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
zygoteWriter = new BufferedWriter(new OutputStreamWriter(
zygoteSocket.getOutputStream()), 256);
} catch (IOException ex) {
try {
zygoteSocket.close();
} catch (IOException ignore) {
}
throw ex;
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);//从Zygote获取支持的abi列表
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
ZygoteState是Process的内部类,其connect方法所传入的参数就是连接socket所需要的socket名称,在这个方法中,试图打开一个指定名称的socket通道,在查询完系统所支持的abi架构后把socket通道的I/O流封装成ZygoteState对象(有兴趣的话,读者自行可研究下android socket的实现与使用方式,此处不展开说明)。
此处connect()方法负责连接名为zygote的server socket,下边我们来大概了解下socket通信目标端是如何创建的。
Zygote进程由init进程启动后,在main函数执行过程中会调用registerZygoteSocket()方法注册一个server socket,如下:
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
private static void registerZygoteSocket(String socketName) {
if (sServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
String env = System.getenv(fullSocketName);//在init的service_start中被保存
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
sServerSocket = new LocalServerSocket(
createFileDescriptor(fileDesc));//创建server socket
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
LocalServerSocket作为基于C/S的socket机制的Server端,其创建有两种方式:(一)、指定socket名称创建,(二)、根据指定的FD(文件描述符)创建。而此处Zygote进程则使用了文件描述符的方式来创建,FD以 K(name)-V(fd) 的形式作为环境变量预先存放在系统中,registerZygoteSocket()方法中通过System.getenv()将之前保存的fd取出来。
那么有个很显然的问题,对应name的fd是什么时候存放到系统环境中去的呢?
在init进程中,各系统服务启动是通过service_start()完成的,在service_start()中如果发现要启动的服务包含了socket启动需求,则会并通过publish_socket()将创建好的fd保存到environment中。
// system/core/init/init.c
static void publish_socket(const char *name, int fd)
{
char key[64] = ANDROID_SOCKET_ENV_PREFIX;//system\core\include\cutils\sockets.h中定义 :ANDROID_SOCKET_
char val[64];
strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
name,
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
snprintf(val, sizeof(val), "%d", fd);//将fd拷贝到val中
add_environment(key, val);//加入环境变量中
fcntl(fd, F_SETFD, 0);
}
可以看到的是,所有的fd保存所对应的key都增加了前缀,这个前缀具体的值如下:
// system/core/include/cutils/sockets.h
#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_" //所有fd的key前缀
#define ANDROID_SOCKET_DIR "/dev/socket" //socket以驱动形式存在
例如此处我们的Zygote启动时,那么系统环境所保存的fd所对应的的key的全名称为ANDROID_SOCKET_zygote。此处需要注意的是key并不意味着就是/dev/socket驱动下对应socket通道名称,它只是environment中的键而已,socket通道真正的名称还是需要看create_socket()时所传入的si->name(对应zygote进程,则name即为zygote)。
经此,我们大概了解了Zygote进程注册socket的过程。在其LocalServerSocket创建完成后,Zygote进程就会进入循环监听对这个server socket的请求(即runSelectLoop()方法,其内部以循环方式调用socket的accept()监听请求,下一小节会有介绍)。
再回到我们client端,在openZygoteSocketIfNeeded()尝试连接zygote并返回ZygoteState后,接下来我们就可以通过ZygoteState进行进程启动参数的传输与结果的获取了,这就是zygoteSendArgsAndGetResult()的处理任务。
// frameworks/base/core/java/android/os/Process.java
private static ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
//part1 写入部分
writer.write(Integer.toString(args.size()));//首先保存参数数量,方便zygote按数量解析参数
writer.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {//进程启动参数的保存
String arg = args.get(i);
if (arg.indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
writer.write(arg);
writer.newLine();
}
writer.flush();//将buffer交给zygote处理
//part2 读取部分
ProcessStartResult result = new ProcessStartResult();
result.pid = inputStream.readInt();//获取返回结果,pid - 进程号
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
result.usingWrapper = inputStream.readBoolean();
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
zygoteSendArgsAndGetResult()分为向zygote写入数据和读取zygote结果两个部分,需要注意的是整个过程(主要是读取过程)是阻塞式的。
就方法的写入部分来看,首先写入了启动参数的数量,接下来才将进程启动配置的集合数据分条写入buffer,最后通过flush操作把整个写入部分数据通过socket通道的OutputStream传输给zygote(socket通道一般由I/O两部分组成)。
就方法的读取部分来看,主要是读取了fork()函数返回的进程id并封装为ProcessStartResult返回给AMS。
上述我们讲到zygoteSendArgsAndGetResult()中向zygote进程写入了socket数据,那么socket到底是如何接收这些数据,又是如何处理的呢?带着这些疑问,我们接着往下分析。
我们先来看看Zygote进程main()函数socket相关的主体代码:
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
boolean startSystemServer = false;
String socketName = "zygote";
//...
registerZygoteSocket(socketName);
//...
runSelectLoop(abiList);//内部包含fork相关调用
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();//fork后的子进程进入此处
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
在注册server socket后,Zygote就调用runSelectLoop()进入循环以接收来自client socket的请求,这是一种阻塞式循环的方式。
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
FileDescriptor[] fdArray = new FileDescriptor[4];
fds.add(sServerSocket.getFileDescriptor());//fds与peers成对出现
peers.add(null);//此处fds中已放入server socket的fd
int loopCount = GC_LOOP_COUNT;
while (true) {
int index;
//...
try {
fdArray = fds.toArray(fdArray);
index = selectReadable(fdArray);
} catch (IOException ex) {
throw new RuntimeException("Error in select()", ex);
}
if (index < 0) {
throw new RuntimeException("Error in select()");
} else if (index == 0) {//0表示server socket,说明此时客户端peers已经处理完成。
ZygoteConnection newPeer = acceptCommandPeer(abiList);//内部调用accept()阻塞式等待client连接
peers.add(newPeer);
fds.add(newPeer.getFileDescriptor());
} else {//index大于0,说明有peer要处理
boolean done;
done = peers.get(index).runOnce();//处理client请求
if (done) {//处理完成后删除对应信息
peers.remove(index);
fds.remove(index);
}
}
}
}
在runSelectLoop()方法中,有两组集合我们需要关注一下,它们中的元素对应了来自client端的一次次socket连接请求。
这两组集合在还没有进入while循环之前队首元素已经server socket被填充,至于为什么有此次填充,这个我也比较困惑,有知情的读者可留言告知下。
在上述方法进入循环后,会试图从fds中寻找到可读的描述符,因之前队首已经被填充了元素,可想而知selectReadable()方法正常情况下返回值至少大于等于0(此处返回的是下角标,0即队首元素)。如果index==0,则说明可读元素即server socket自身,此刻并无client端socket请求到来,所以会调用acceptCommandPeer()以阻塞式等待socket请求的到来。反之,若index角标大于0,则说明此时fds中已经有至少2个可读文件描述符(server socket +至少1个client socket请求),此时就需要处理对应的peer连接请求。
client端socket请求的处理是通过调用ZygoteConnection的runOnce()方法完成的。
// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
//part1 读取进程启动参数
try {
args = readArgumentList();//将client端的进程启动参数取出
descriptors = mSocket.getAncillaryFileDescriptors();//Ancillary-附加的,辅助的
} catch (IOException ex) {
closeSocket();
return true;
}
//...
try {
//part2 解析进程启动参数,如果需要则处理支持的abi架构查询
parsedArgs = new Arguments(args);//解析进程启动参数
if (parsedArgs.abiListQuery) {//查询abi
return handleAbiListQuery();//返回abi长度及内容
}
//... 省略掉权限检查部分
//part3 fork进程
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (IOException ex) {
//...此处包含多个catch,省略掉
}
//part4 此处一分为二,分别对原进程与新进程进行处理
try {
if (pid == 0) {//子进程
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);//进入子进程处理流程,内部最终通过throw异常方式调用ActivityThread.main()
return true;
} else {//zygote进程
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);//将fork出来的子进程的id等返回client端
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
runOnce方法可以分为4个部分来解读:
读取client端传过来的进程启动配置项。根据之前Process中zygoteSendArgsAndGetResult()方法对进程配置项的保存过程来看,可以猜想此处readArgumentList()首先读取的是配置项的个数,然后根据个数一行一行把配置项读取并村放入args字符串数组中。
解析进程启动参数,如果需要则处理abi查询。解析过程是通过Arguments类完成的,其构造方法调用parseArgs()解析所有传输过来的进程启动参数,并将这些参数保存在Arguments对象本身的各成员变量中,包括一些关键参数如uid、gid、runtime-init、进程启动类(被放入remainingArgs中)等。abi架构的查询则是根据abiListQuery参数是否为true来确定,如果是abi查询,则进入handleAbiListQuery()流程把当前系统支持的cpu架构返回给client端。值得注意的是一般情况下Process.start()启动进程时其内部会主动查询系统所支持的abi架构的。
fork进程。fork过程是通过调用Zygote.forkAndSpecialize()方法完成的,其最终会通过JNI调用到native层,最终是调用linux的fork函数完成进程的创建。在这个方法被调用之后,接下来runOnce()及之后zygote的处理流程就一分为二,分为新建的子进程和原zygote进程两部分了。关于fork()函数的特点,读者可自行查看fork( )函数详解这篇文章。
分别对原进程与新进程进行处理。根据fork()函数一次调用两次返回的特点,如果fork返回的pid是0,则说明当前正处于新建进程中,如果pid大于0(pid的值是新建进程的pid值),则当前处于zygote进程中。由此,这第四部分又细化为两个子部分。
(一):当前处于子进程中。调用handleChildProc()方法执行进程初始化操作。此子部分在下边详细说明。
(二):当前处于zygote进程中。接着调用handleParentProc()方法将创建的进程id等通过socket返回给client端(即Process.start()的调用方,本文指的则是AMS所在的system_server进程)。
下边我们来详细说明当前处于子进程中时的情况,即handleChildProc()方法及后续调用过程。
// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
//...
if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {//根据Process.start所配置的参数来看,进入的是此处
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
//...
}
}
在上述代码中,会根据进程启动参数配置的不同而进入不同的处理逻辑,此处我们仅就本文研究的AMS发起创建进行解读。在Process关于进程启动参数的配置方法startViaZygote()中,第一个被添加的配置项是:–runtime-init。对应到此处parseArgs解析的就是runtimeInit成员为true,而又因没有配置–invoke-with参数,所以在handleChildProc()方法中,进入的处理逻辑是RuntimeInit的zygoteInit()方法。
需要注意的是zygoteInit()所传入的进程启动参数只是部分了,即还未使用的remainingArgs保留参数(这其中就有最重要的进程启动类)。此方法具体处理过程如下:
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
redirectLogStreams();
commonInit();//进程初始化
//此处最终在app_main.cpp中执行onZygoteInit方法启动一个binder线程
//这也是java进程天生支持binder机制的原因
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);//内部最终进入ActivityThread.main
}
在zygoteInit()方法中我们可以看到对应用进程的一些初始化的过程,如nativeZygoteInit()就将为当前进程启动一个binder主线程,使得当前进程支持binder跨进程调用,这也是为什么后续的ActivityThread.main()方法能够与AMS进行通信的原因。
同时我们继续往下看,进入applicationInit()方法中:
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
nativeSetExitWithoutCleanup(true);
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
final Arguments args;
try {
//需要注意的是,此Arguments并非同ZygoteConnection中的Arguments类,这是两个解析方式不同的类
args = new Arguments(argv);
} catch (IllegalArgumentException ex) {
Slog.e(TAG, ex.getMessage());
return;
}
//调用ActivityThread的main()函数,其内部的调用方式比较特别哟
invokeStaticMain(args.startClass, args.startArgs, classLoader);
}
在applicationInit()中首先对VM进行了一些配置,然后对保留下来的启动参数继续进行解析,解析得来的startClass就是进程启动类ActivityThread,而剩余的args则最终会以参数形式传到ActivityThread.main(args)中去。
经历了那么多的风风雨雨,应用进程启动的关键点来了,这就是applicationInit()中最后调用的invokeStaticMain()方法,从名称可以看出,其中必然涉及到反射调用,且调用的是静态的类方法main()。
invokeStaticMain()方法具体内容如下:
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
//关键点,通过throw方式执行方法的invoke,有利于将方法栈清除。
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}
上述方法中,className即AMS的startProcessLocked()方法所传入并流转到此的ActivityThread类,当我们通过java反射方式找到ActivityThread.main()方法后,就将Method与启动参数以MethodAndArgsCaller异常的形式抛出,由子进程进行捕获。
为什么我们说这样的throw方式来达到调用目标比较特别,因为通过throw方式调用方法可以有效去除之前的方法调用链。真因此,我们在查询应用进程的方法栈时,发现其方法调用链最多能追溯到ZygoteInit.main()方法(因为在其main()方法中对此异常进行了捕获)。
invokeStaticMain()方法最后所抛出的MethodAndArgsCaller异常,最终由ZygoteInit.main()捕获。值得注意的是,因为runSelectLoop()执行了fork函数一分为二,而且MethodAndArgsCaller异常也是有新建的子进程抛出,所以下边的代码也是在子进程中执行的。
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
try {
//...
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();//捕获异常,值得注意的是,只有子进程才会抛出这个异常
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
可见,异常被捕获后执行了其run()方法。这个run()方法就是真正invoke()调用ActivityThread.main()的地方了。
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java$MethodAndArgsCaller
public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
至此,连接Zygote并创建承载进程部分已经完成了。但对于本文所探讨的AMS应用进程创建流程却还得继续。
从上一小节我们可知,应用进程被fork出来之后最终要执行ActivityThread.main()方法。这其实是一个很经典的流程,Android系统中当启动一个新进程时都会执行fork函数,并由fork返回值来执行新进程所指定的main()方法。例如system_server进程在启动时会指定执行SystemServer.main()函数,而对于普通java应用而言,则是此处的ActivityThread.main()。
ActivityThread.main()方法如下:
// frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
//...
Looper.prepareMainLooper();//MainLooper的创建,这也是为什么之后我们一颗直接获取的原因
ActivityThread thread = new ActivityThread();
thread.attach(false);//其中包含了向AMS报告启动信息
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();//异步任务池的创建
Looper.loop();//进入消息循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
从main()方法的调用逻辑来看,我们可以发现Handler消息机制的使用(对handler机制不了解的读者可以阅读深入理解Handler机制这篇文章),这证明了android应用程序是基于消息驱动的观点。而且为了方便调用ActivityThread的非静态方法,main()中新建了ActivityThread对象,并调用其attach()方法与AMS进行绑定。
attach()方法在之前深入理解AMS之启动过程一文中我们也介绍过,不过当时是站在系统进程的角度来解读。而对于此处而言,attach()方法执行的是普通应用进程的逻辑。
// frameworks/base/core/java/android/app/ActivityThread.java
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("" ,
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
//...
} else {
//...省略系统进程的创建过程
}
//...
}
在attach()方法中与AMS进行绑定是通过AMS.attachApplication()完成的,需要注意的是attachApplication()方法传入的mAppThread对象,mAppThread是一个binder对象,它继承自IApplicationThread,主要负责AMS与应用进程的通信工作,在之前ProcessRecord中的thread成员就是此处传过去的。
此处进行的attach工作呗AMS接收到之后,AMS就可以将当前进程的激活、移除启动超时消息、并检查进程是否有待做工作如Activity的启动等等。
至此,AMS应用进程的启动就已告一段落。