@chopsticks
2014-12-22T01:42:25.000000Z
字数 3992
阅读 2820
Android
Android的应用程序进程启动,是通过启动Activity或启动Service来让ActivityManagerService创建新的进程。具体实现都是调用ActivityManagerService.startProcessLocked方法来根据ApplicationInfo、processName等信息创建专属进程。
在ActivityManagerService.startProcessLocked方法中,调用Proces.sStart实现进程的启动。对于启动Service和Activity而言,调用参数如下,参数具体意义参考Process.start。
Process.ProcessStartResult startResult = Process.start(
"android.app.ActivityThread", //class
app.processName,
uid,
uid,
gids,
debugFlags,
mountExternal,
app.info.targetSdkVersion,
app.info.seinfo,
null);
其中app为processRecord
我们关注第一个传入的参数:android.app.ActivityThread
,该类位于frameworks/base/core/java/android/app/ActivityThread.java
,当进程创建完毕时会首先执行该进程类的static main()
方法。需要注意的是,当start方法返回时,新进程已经运行。
我们需要考虑的时,如果我们自己构造一个类似android.app.ActivityThread
的进程类,并指定给Process.start,则是否会被顺利执行并符合预期呢?答案是肯定的
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[] zygoteArgs)
Process.start
方法的部分声明如下:processClass: The class to use as the process's main entry point
If processes are enabled, a new process is created and the static main() function of a processClass is executed there.
The process will continue running after this function returns.
从流程看出,在Process类的几个方法中,仅仅是准备了参数字符串,然后将其通过socket发送个Zygote进程,让其fork新进程。并执行指定的类。
final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
mProcessNames
是ActivityManagerService中维护的一个当前已经成功创建的ProcessRecord的MAP。其定义如下。源自google源码:framework/base/core/java/com/android/internal/app/ProcessMap.java
package com.android.internal.app;
import android.util.ArrayMap;
import android.util.SparseArray;
public class ProcessMap<E> {
final ArrayMap<String, SparseArray<E>> mMap
= new ArrayMap<String, SparseArray<E>>();
public E get(String name, int uid) {
SparseArray<E> uids = mMap.get(name);
if (uids == null) return null;
return uids.get(uid);
}
public E put(String name, int uid, E value) {
SparseArray<E> uids = mMap.get(name);
if (uids == null) {
uids = new SparseArray<E>(2);
mMap.put(name, uids);
}
uids.put(uid, value);
return value;
}
public void remove(String name, int uid) {
SparseArray<E> uids = mMap.get(name);
if (uids != null) {
uids.remove(uid);
if (uids.size() == 0) {
mMap.remove(name);
}
}
}
public ArrayMap<String, SparseArray<E>> getMap() {
return mMap;
}
}
final SparseArray<ProcessRecord> mPidsSelfLocked
mPidsSelfLocked
维护了当前已经成功运行的进程的ProcessRecord.该列表与前文中关于mProcessNames不同地方在于,mProcessNames中的ProcessRecord并未一定用在某个特定进程上。
符合Process.start
第一个参数procClass的基本相求,则需要static main()
由于启动进程时,替换了启动类ActivityThread
,从而无法在新的进程中向AMS发送attach成功的信息,那么一定时间后,attach超时后会将新建的进程kill掉。所以需要进行启动超时的处理。
实现:
step1: 进程启动时,在调用的第二个startProcessLocked(arg1, arg2,arg3)
中,其中存在有如下代码段:
synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(startResult.pid, app);
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, startResult.usingWrapper?
PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
可以看到发出了消息PROC_START_TIMEOUT_MSG
。
step 2: 那么在AMS中对应的Handle对该消息的处理有如下代码:
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case PROC_START_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
nmsg.obj = msg.obj;
mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT);
return;
}
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processStartTimedOutLocked(app);
}
} break;
即首次调用则:mDidDexOpt 赋值为TRUE,并发送一个延时的相同MSG;再次进入时,则可以直接调用processStartTimedOutLocked
,该函数则主要做的就是:从mPidsSelfLocked中移走该ProcessRecord,在输出attach失败的信息,然后kill该 attach超时的进程:killUnneededProcessLocked(app, "start timeout");
如果我们需要防止被kill,则只需要在自己定义的startProcessLocked中取消发送timeout的消息即可。(同时需要在自己定义的startProcessLocked函数中取消remove 超时消息的处理。