[实践] Android5.1.1源码 - App进程保活之persistent法
@(Android研究)[App进程保活]
[TOC]
前言
使用本文中的原理,在Android5.1.1系统中使用下面的方法结束App进程后App会立即透明复活
:
- 在代码中用Android系统提供的接口清理内存。
- 长按HOME的方法键清理App。
- 在shell中调用kill -9 <pid>命令强制终止App进程。
原理简介
App保活关键点
App进程在启动的时候会创建并关联一个ProcessRecord对象,这个对象中保存了App的进程信息,在ProcessRecord类中有一个成员变量persistent
,对于系统App这个成员变量的值为true,对于普通App它为false。当persistent为true时,Android系统将保证这个App进程一直存在。所谓一直存在是指,当App进程被结束掉后,Android系统会将这个App重启,这个重启是透明的。
App进程复活点
当App进程结束时会进入ActivityManagerService.cleanUpApplicationRecordLocked方法,这个方法的源码在文件"frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java"中,下面是它的源码:
/**
* Main code for cleaning up a process when it has gone away. This is
* called both as a result of the process dying, or directly when stopping
* a process when running in single process mode.
*
* @return Returns true if the given process has been restarted, so the
* app that was passed in must remain on the process lists.
*/
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index) {
......
if (!app.persistent || app.isolated) {
......
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
// and start a new process for it.
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
......
if (restart && !app.isolated) {
// We have components that still need to be running in the
// process, so re-launch it.
if (index < 0) {
ProcessList.remove(app.pid);
}
mProcessNames.put(app.processName, app.uid, app);
startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
boolean removed;
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
app.setPid(0);
}
return false;
}
通过看源码中对这个方法的注释就可以知道:在单进程模式下当进程正在结束或进程被直接停止时会调用这个方法。
参数"app"是被结束App所关联的ProcessRecord对象。
通过阅读上面源码可以发现:当app.persistent为true且app.isolated为false且app.removed为false且mPersistentStartingProcesses中不存在app时将会把restart设置为true。
app.isolated、app.removed和mPersistentStartingProcesses的由来不展开来讲,只需知道:
- 只有App进程uid与App包的uid不同时,app.isolated才为true。而通常情况下App进程的uid与App包的uid相同所以这个值通常为false。
- 当App包从设备上删除时,app.removed才为true。
- 当App启动的时候会调用到attachApplicationLocked方法,在这个方法中将当前应用关联的ProcessRecord对象从mPersistentStartingProcesses里面删除了,所以通常来说"mPersistentStartingProcesses.indexOf(app)"的返回值小于0。
现在进行一个小结: 在
通常
情况下只要app.persistent的值为true,那么restart就会被赋值为true。 当restart为true且app.isolated为false时会执行下面的代码将App进程重启(这段代码在ActivityManagerService.cleanUpApplicationRecordLocked方法中):
mProcessNames.put(app.processName, app.uid, app);
startProcessLocked(app, "restart", app.processName);
return true;
验证
通过修改Android5.1.1的源码进行验证
。
设定,要保活的App包名:com.example.myapp
设置persistent为true
当App启动时会调用ActivityManagerService.startProcessLocked方法,这个方法的源码在文件"frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java"中,在这个方法中创建/获得了App关联的ProcessRecord对象。
我在这个方法中添加了下面的代码:
if ("com.example.myapp".equals(info.packageName)) {
app.persistent = true;
}
下面是对这个方法进行修改后的代码,只列出了添加的代码所在的上下文:
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
......
if (app == null) {
checkTime(startTime, "startProcess: creating new process record");
app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
if (app == null) {
Slog.w(TAG, "Failed making new process record for "
+ processName + "/" + info.uid + " isolated=" + isolated);
return null;
}
app.crashHandler = crashHandler;
mProcessNames.put(processName, app.uid, app);
if (isolated) {
mIsolatedProcesses.put(app.uid, app);
}
checkTime(startTime, "startProcess: done creating new process record");
} else {
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName, info.versionCode, mProcessStats);
checkTime(startTime, "startProcess: added package to existing proc");
}
if ("com.example.myapp".equals(info.packageName)) {
app.persistent = true;
}
......
return (app.pid != 0) ? app : null;
}
log辅助验证
为了验证"原理简介"一节中的理论,我在ActivityManagerService.cleanUpApplicationRecordLocked方法中也添加了代码。
下面是添加的代码,这段代码用于log输出:
Log.i("debug", "restart app, packageName=" + app.info.packageName + ", pid=" + app.pid);
if ("com.example.myapp".equals(app.info.packageName)) {
try {
throw new Exception("com.example.myapp restart");
} catch(Exception e) {
e.printStackTrace();
}
}
下面是对这个方法进行修改后的代码,只列出了添加的代码所在的上下文:
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index) {
......
if (restart && !app.isolated) {
// We have components that still need to be running in the
// process, so re-launch it.
if (index < 0) {
ProcessList.remove(app.pid);
}
Log.i("debug", "restart app, packageName=" + app.info.packageName + ", pid=" + app.pid);
if ("com.example.myapp".equals(app.info.packageName)) {
try {
throw new Exception("com.example.myapp restart");
} catch(Exception e) {
e.printStackTrace();
}
}
mProcessNames.put(app.processName, app.uid, app);
startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
......
}
return false;
}
结果
按照上文中修改完Android5.1.1源码后,进行编译打包然后刷到Nexus5中。写一个包名为com.example.myapp
的App并安装到新系统中,当使用前言一节中列出的结束App进程的方法结束包名为com.example.myapp
的进程时,包名
为com.example.myapp的进程(包名相同即可,如包名相同的服务进程)会立即透明复活
。
下面是当包名为com.example.myapp的进程复活时输出的log(上文在ActivityManagerService.cleanUpApplicationRecordLocked方法中添加的代码就是为了输出log进行验证):
05-19 14:53:27.217: I/ActivityManager(776): Process com.example.myapp (pid 3998) has died
05-19 14:53:27.219: I/debug(776): restart app, packageName=com.example.myapp, pid=3998
05-19 14:53:27.219: W/System.err(776): java.lang.Exception: com.example.myapp restart
05-19 14:53:27.219: W/System.err(776): at com.android.server.am.ActivityManagerService.cleanUpApplicationRecordLocked(ActivityManagerService.java:15332)
05-19 14:53:27.219: W/System.err(776): at com.android.server.am.ActivityManagerService.handleAppDiedLocked(ActivityManagerService.java:4702)
05-19 14:53:27.219: W/System.err(776): at com.android.server.am.ActivityManagerService.appDiedLocked(ActivityManagerService.java:4866)
05-19 14:53:27.219: W/System.err(776): at com.android.server.am.ActivityManagerService$AppDeathRecipient.binderDied(ActivityManagerService.java:1230)
05-19 14:53:27.219: W/System.err(776): at android.os.BinderProxy.sendDeathNotice(Binder.java:551)
后记
我在Android的1.6、2.0.1、2.1、2.2.3、2.3.7、4.0.4、4.1.1、4.2、4.3、4.4这些系统版本的源码中均找到了ProcessRecord类,并且在ProcessRecord类中均有成员变量persistent,也在ActivityManagerService.cleanUpApplicationRecordLocked方法中找到了与App重启相关的语句:
startProcessLocked(app, "restart", app.processName);
我没有在这些Android系统上进行验证,我在这里武断
的认为在这些Andriod系统上当一个App关联的ProcessRecord对象中的成员变量persistent为true时,只要这个App进程被结束就会被Android系统重启。