AMS 如何知道app死亡
参考目录:
进程管理之进程的创建
Android系统中的进程管理:内存的回收
AMS-kill Launcher进程的代码流程
在任何时候,应用进程都可能死亡,例如被OOM Killer或者LowMemoryKiller杀死,自身crash死亡又或者被用户手动杀死。
无论哪种情况,作为应用进程的管理者ActivityManagerService都需要知道。
在应用进程死亡之后,ActivityManagerService需要执行如下工作:
- 执行清理工作 ActivityManagerService内部的ProcessRecord以及可能存在的四大组件的相关结构需要全部清理干净
- 重新计算进程的优先级 上文已经提到过,进程的优先级是有关联性的,有其中一个进程死亡了,可能会连到影响到其他进程的优先级需要调整。
ActivityManagerService是利用Binder提供的死亡通知机制来进行进程的死亡处理的.
简单来说,死亡通知机制就提供了进程间的一种死亡监听的能力:当目标进程死亡的时候,监听回调会执行。
ActivityManagerService中的AppDeathRecipient监听了应用进程的死亡消息,该类代码如下:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private final class AppDeathRecipient implements IBinder.DeathRecipient {final ProcessRecord mApp;final int mPid;final IApplicationThread mAppThread;AppDeathRecipient(ProcessRecord app, int pid,IApplicationThread thread) {if (true) Slog.v(TAG, "New death recipient " + this+ " for thread " + thread.asBinder());mApp = app;mPid = pid;mAppThread = thread;}@Overridepublic void binderDied() {if (true) Slog.v(TAG, "Death received in " + this+ " for thread " + mAppThread.asBinder());synchronized(ActivityManagerService.this) {appDiedLocked(mApp, mPid, mAppThread, true, null);}}}
每一个应用进程在启动之后,都会attach到ActivityManagerService上通知它自己的进程已经启动完成了。
这时ActivityManagerService便会为其创建一个死亡通知的监听器。
在这之后如果进程死亡了,ActivityManagerService便会收到通知。
//attch 贴上,附上
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {... final String processName = app.processName;try {AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);thread.asBinder().linkToDeath(adr, 0);app.deathRecipient = adr;} catch (RemoteException e) {app.resetPackageList(mProcessStats);mProcessList.startProcessLocked(app,new HostingRecord("link fail", processName),ZYGOTE_POLICY_FLAG_EMPTY);return false;}...}
进程死亡之后的处理工作是appDiedLocked这个方法中处理的,可以参考[AMS-kill Launcher进程的代码流程]分析。
思路:利用Log.d(TAG,Log.getStackTraceString(new Throwable()))
打印堆栈.
实际案例
问题: 类似于广告机,主要用于长时间使用/展示客户的某一个app.
其中有些客户遇到app自己crash或者ANR的情况,客户的app没办法重新启动展示。
如果一个一个去解决客户app的问题,那个花费的时间成本过于庞大。
思路一:写个Service,开机后启动,过5~10 min 检测客户app进程是否存活,如果死亡,就重新启动
思路二:利用AMS的AppDeathRecipient[app死亡通知器],在这里做逻辑处理,如同Launcher3和SystemUI,它俩被kill调,就会重新启动
某种情况不会走AppDeathRecipient**
systemui被杀后重启,从启动进程到attachApplicationLocked超过10s 执行processStartTimedOutLocked 导致再次被杀,由于没有执行到attach,不会死亡回调不再重启
03-12 17:33:11.113265 1235 1405 I am_kill : [0,4610,com.android.systemui,-800,Too many Binders sent to SYSTEM]
03-12 17:33:11.357911 1235 1392 I wm_set_keyguard_shown: [1,0,0,setKeyguardShown]
03-12 17:33:21.186292 1235 1405 W ActivityManager: Process ProcessRecord{b9dfae1 29414:com.android.systemui/u0a161} failed to attach
03-12 17:33:21.187587 1235 1405 W ActivityManager: Forcing bringing down service: ServiceRecord{bcb0cfb u0 com.android.systemui/.ImageWallpaper}
03-12 17:33:21.187749 1235 1405 W ActivityManager: Forcing bringing down service: ServiceRecord{bc9e032 u0 com.android.systemui/.dump.SystemUIAuxiliaryDumpService}
03-12 17:33:21.187809 1235 1405 W ActivityManager: Forcing bringing down service: ServiceRecord
//操作者主动移除app 后台日志,这个情况需要另外处理
ActivityManager system_server I Killing 18419:com.android.settings/1000 (adj 1001): remove task
思考Launcher3/SystemUI 为什么被kill后重新启动?
- Launcher
当Launcher在栈顶时[用户可见,可互动],kill它之后会被重新启动--[AMS-kill Launcher进程的代码流程 -- Launcher在栈顶的情况]
如果它不在栈顶,kill 它,他不会被启动。但是如果其它app都退栈了,需要Launcher在栈顶时,它会被重新启动。
- SystemUI
ActivityManager: Start proc 2988:com.android.systemui/u0a129 for restart com.android.systemui 谁打印的?
./frameworks/base/services/core/java/com/android/server/am/ProcessList.javaActivityManagerService mService = null;ProcessList::startProcessLocked() -->ProcessList::handleProcessStartedLocked(){...checkSlow(app.startTime, "startProcess: building log message");StringBuilder buf = mStringBuilder;buf.setLength(0);buf.append("Start proc ");buf.append(pid);buf.append(':');buf.append(app.processName);buf.append('/');UserHandle.formatUid(buf, app.startUid);if (app.isolatedEntryPoint != null) {buf.append(" [");buf.append(app.isolatedEntryPoint);buf.append("]");}buf.append(" for ");buf.append(app.hostingRecord.getType());if (app.hostingRecord.getName() != null) {buf.append(" ");buf.append(app.hostingRecord.getName());}mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);...
}
//应用启动日志,for 后面接的原因,这个时被广播启动的
ActivityManager: Start proc 982:com.android.music/u0a29 for broadcast com.android.music/.MediaButtonIntentReceiver//而kill systemui,它是被它自己重启了
ActivityManager: Start proc 2988:com.android.systemui/u0a129 for restart com.android.systemuiActivityManager: Start proc表示应用进程第一次启动
ActivityTaskManager: Start u0 表示启动已存在堆栈中的应用find ./frameworks/base/services/core/java/com/android/server -name '*.java' | xargs grep 'Start '
正在重启SystemUIService,它带动了systemui
ActivityManager system_process V Death received in com.android.server.am.ActivityManagerService$AppDeathRecipient@fb6af5a for thread android.os.BinderProxy@3d2458b
ActivityManager system_process V Death received in com.android.server.am.ActivityManagerService$AppDeathRecipient@9f93b04 for thread android.os.BinderProxy@83188ed
ActivityManager system_process I Process com.android.systemui (pid 4101) has died: pers PER
ActivityManager system_process W Scheduling restart of crashed service com.android.systemui/.SystemUIService in 0ms for persistent
ActivityManager system_process W Scheduling restart of crashed service com.android.systemui/.ImageWallpaper in 0ms for persistent
ActivityManager system_process W Scheduling restart of crashed service com.android.systemui/.dump.SystemUIAuxiliaryDumpService in 0ms for persistent
ActivityManager system_process W Scheduling restart of crashed service com.android.systemui/.keyguard.KeyguardService in 0ms for persistent
ActivityManager system_process W Re-adding persistent process ProcessRecord{e65ac47 4101:com.android.systemui/u0a129}
ActivityManager system_process I Killing 5302:com.android.packageinstaller/u0a76 (adj 985): empty #17
ActivityManager system_process D java.lang.Throwableat com.android.server.am.ProcessList.handleProcessStartedLocked(ProcessList.java:2501)
从日志上看,...in 0ms for persistent,和清单文件中的persistent属性有关系.
-
在系统刚起来的时候,该App也会被启动起来
-
该App被强制杀掉后,系统会重启该App。这种情况只针对系统内置的App,第三方安装的App不会被重启.
/frameworks/base//services/core/java/com/android/server/am/ActivityManagerService.java
public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {...synchronized (this) {// Only start up encryption-aware persistent apps; once user is// unlocked we'll come back around and start unaware appst.traceBegin("startPersistentApps");startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);t.traceEnd();// Start up initial activity....
}void startPersistentApps(int matchFlags) {if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;synchronized (this) {try {final List<ApplicationInfo> apps = AppGlobals.getPackageManager().getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();for (ApplicationInfo app : apps) {if (!"android".equals(app.packageName)) {addAppLocked(app, null, false, null /* ABI override */,ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);}}} catch (RemoteException ex) {}}final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,boolean disableHiddenApiChecks, boolean disableTestApiChecks,boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) {...//如果是系统App,且persistent属性为true,则异常死亡后会重启,调整app的adj if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {app.setPersistent(true);app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;}//如果App已启动,则不处理,否则调用startProcessLocked方法启动App//启动App是异步的,因此会将正在启动,但还没启动完成的App添加到mPersistentStartingProcesses列表中,当启动完成后再移除if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {mPersistentStartingProcesses.add(app);mProcessList.startProcessLocked(app, new HostingRecord("added application",customProcess != null ? customProcess : app.processName),zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,mountExtStorageFull, abiOverride);}...}
在App启动完成后,会在ActivityThread中调用ActivityManagerService的attachApplicationLocked()方法,
将该App从mPersistentStartingProcesses移除,
并注册一个死亡讣告监听器AppDeathRecipient,用于在App异常被杀后的处理工作.
流程如下:
AppDeathRecipient::binderDied() --> AMS::appDiedLocked()-->AMS::handleAppDiedLocked()-->AMS::cleanUpApplicationRecordLocked()final boolean cleanUpApplicationRecordLocked(ProcessRecord app,boolean restarting, boolean allowRestart, int index, boolean replacingPid) {...// If this is a precede instance of another process instanceallowRestart = true;synchronized (app) {if (app.mSuccessor != null) {// We don't allow restart with this ProcessRecord now,// because we have created a new one already.allowRestart = false;// If it's persistent, add the successor to mPersistentStartingProcessesif (app.isPersistent() && !app.removed) {if (mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {mPersistentStartingProcesses.add(app.mSuccessor);}}// clean up the field so the successor's proc starter could proceed.app.mSuccessor.mPrecedence = null;app.mSuccessor = null;// Notify if anyone is waiting for it.app.notifyAll();}} ...if (!app.isPersistent() || app.isolated) {if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,"Removing non-persistent process during cleanup: " + app);if (!replacingPid) {mProcessList.removeProcessNameLocked(app.processName, app.uid, app);}mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());} 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;}}
}
关于进程保活
谈谈Android中的persistent属性
如果跟service 有关系的对应日志,请看下面:
07-17 09:52:57.674 1022 1037 I ActivityManager: Process com.shan.mvvm (pid 13678) has died: prcp SVC
07-17 09:52:57.675 1022 1037 W ActivityManager: Scheduling restart of crashed service com.shan.mvvm/.MyService in 1000ms for start-requested
Scheduling restart of crashed service解决方案与源码分析
思路一:写个Service...
思路二:利用AMS的AppDeathRecipient...
PS:List的remove()方法陷阱+性能优化**
for循环遍历list(错误)
//通过索引获取元素进行判断后删除
for(int i=0;i<list.size();i++){if(list.get(i).equals("del"))list.remove(i);
}
删除某个元素后,list的大小发生了变化,而索引也在变化,所以会导致在遍历的时候漏掉某些元素,甚至会数组越界。
比如当删除第1个元素,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。(
索引在增加,大小在减小。左增右减同时变化)
因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
使用迭代器方法(正确,推荐)
//正确,并且推荐的方法
Iterator<Integer> itr = list.iterator();
while(itr.hasNext()) {if(itr.next()%2 ==0)itr.remove();
}
迭代器可以正常的循环及删除。但要注意的是,需要使用iterator的remove方法。
如果用list的remove方法同样会报ConcurrentModificationException错误。
使用for循环,倒序进行;(正确)
//正确
for(int i=list.size()-1;i>=0;i--) {if(list.get(i)%2==0) {list.remove(i);}
}
List的remove()方法陷阱+性能优化