关于进程的死亡处理流程

news/2025/3/9 12:39:13/文章来源:https://www.cnblogs.com/kato-T/p/18757713

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()方法陷阱+性能优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/895087.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Node.js 路由

我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。 因此,我们需要查看 HTTP 请求,从中提取出请求的 URL 以及 GET/POST 参数。这一功能应当属于路由还是服务器(甚至作为一个模块自身的功能)? 我们需要的所有数据都会…

SageMath 9.3软件下载与安装教程

1、安装包 扫描下方二维码关注「软知社」,后台回复【051】三位数字即可免费获取分享链接,无广告拒绝套路; 2、安装教程(建议关闭杀毒软件)双击解压安装包安装,弹窗安装对话框点击下一步选择I accept,点击下一步默认,点击下一步默认文件夹,点击下一步安装路径选择D盘,点…

GoLand 2024软件下载与安装教程

1、安装包 扫描下方二维码关注「软知社」,后台回复【048】三位数字即可免费获取分享链接,无广告拒绝套路; 2、安装教程(建议关闭杀毒软件)下载并解压压缩包,双击exe文件安装,弹窗安装对话框点击下一步创建桌面快捷方式,点击下一步默认,点击安装等待安装过程运行软件,点…

VisualStudio快速更改方法签名

右键——快速操作和重构 更改方法签名 更改签名中,即可进行添加或删除 *****有道无术,术尚可求;有术无道,止于术。*****

eclipse xml Indent using spaces not work /eclipse xml 使用空格缩进功能未生效问题解决

eclipse xml Indent using spaces not work eclipse xml 使用空格缩进功能未生效问题解决 设置xml格式化配置Line width: 设置每行宽度Line width设定为80到100个字符。 Split multiple attributes each on a new line: 标签的每个属性都单独一行显示 Preserve whitespace in t…

【多线程】AQS详解

AQS是什么 AbstractQueuedSynchronizer是一个抽象的队列同步器,AQS利用模板方法模式解决了开发者在实现同步器时的复杂问题,提供了一个通用的加锁解锁框架。 AQS执行原理 AQS为实现的同步器提供了通用的执行框架,定义了对资源state的获取和释放流程。AQS核心思想是在CLH锁的…

.NET 8 AOT

AOT是个防止反编译的好办法 都来看看怎么用吧~ 1.需要安装C++的桌面开发2.新建项目,选择控制台3.确认 <PublishAot>true</PublishAot> 是否为true4.发布release版本优点: 1. AOT 会生成一个自包含的应用程序,并且已提前 (AOT) 编译为本机代码。原生 AOT 应…

京准电钟:GPS北斗时间服务器的作用与应用

京准电钟:GPS北斗时间服务器的作用与应用京准电钟:GPS北斗时间服务器的作用与应用 京准电钟:GPS北斗时间服务器的作用与应用 京准电钟官微——ahjzsz GPS北斗时间服务器是一种利用全球卫星导航系统(GPS为美国系统,北斗为中国系统)提供高精度时间同步服务的设备,其核心作…

制造未来:大型车厂焊接站从PROFIBUS跃迁至PROFINET引爆智能革命

某大型生产现场焊接站的协议改造PROFIBUS从转PROFINET从网关一、背景介绍 某大型汽车制造厂拥有多个生产线,用于车身焊接、喷涂及装配等工序。其中,一条关键的焊接生产线长期采用PROFIBUS协议进行数据传输和设备控制。随着生产自动化程度的提升和工业4.0战略的推进,厂方决定…

信创数据库在金融行业的五大应用场景

信创产业作为国家信息技术创新发展的重要支撑,近年来取得了显著进展。其中,信创数据库在金融行业的应用日益广泛,为金融机构的数字化转型和安全稳定运行提供了有力保障。信创数据库凭借其自主可控、安全可靠等优势,在金融行业多个关键领域发挥着重要作用,不仅提升了金融机…

信创国产化适配方案在金融行业的应用实践

信创国产化适配方案在金融行业的应用实践 随着信息技术的飞速发展,金融行业对信息技术的依赖程度日益加深。在当前复杂的国际形势下,实现信息技术的自主可控成为金融行业发展的重要战略方向。信创国产化适配方案应运而生,旨在通过采用国产信息技术产品和解决方案,降低对国外…

纷享销客vs销售易:制造行业CRM选型深度解析

在当今竞争激烈的制造行业中,企业对于客户关系管理(CRM)系统的需求日益增强,高效、智能的CRM系统已成为推动企业业务增长、优化客户体验的关键。在众多CRM解决方案中,纷享销客凭借其卓越的功能性、高度的定制化能力以及出色的市场表现脱颖而出,成为众多中大型企业首选的C…