基础知识:
=====
ANR 的四种场景:
-
Service TimeOut: service 未在规定时间执行完成:前台服务 20s,后台 200s
-
BroadCastQueue TimeOut: 未在规定时间内未处理完广播:前台广播 10s 内, 后台 60s 内
-
ContentProvider TimeOut: publish 在 10s 内没有完成
-
Input Dispatching timeout: 5s 内未响应键盘输入、触摸屏幕等事件
ANR 的根本原因是:应用未在规定的时间内处理 AMS 指定的任务才会 ANR。
所以 Service 未在指定时间内执行完成而且非主进程的 Service 仍然需要通过 AMS 进行通信。这也能说明一定会产生 ANR 的。
实验:
===
理论上是会,那我们就写个小 demo 来试一下。
写一个 Service:
class NoZuoNoDieService: Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
// 小睡会模拟耗时
Thread.sleep(21_000)
}
}
然后在 AndroidManifest 里声明在独立进程
<service
android:name=“.service.NoZuoNoDieService”
android:process=“:whatever”
/>
最后我们把这个 Service 调起来作死
startService(Intent(this, NoZuoNoDieService::class.java))
应用并没有 ANR 弹窗,不过 logcat 有 ANR 相关信息,看一下 trace 文件:
----- pid 14608 at 2021-03-23 19:56:20 -----
Cmd line: demo.com.sam.demofactory:whatever
… 省略无关信息
“main” prio=5 tid=1 Sleeping
| group=“main” sCount=1 dsCount=0 flags=1 obj=0x73f13a80 self=0x78e7cc2a00
| sysTid=14608 nice=0 cgrp=default sched=0/0 handle=0x796c96d9a8
| state=S schedstat=( 57816925 3067496 80 ) utm=2 stm=3 core=4 HZ=100
| stack=0x7fe1d03000-0x7fe1d05000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep(Native method)
- sleeping on <0x0a71f3e8> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:373)
- locked <0x0a71f3e8> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:314)
at demo.com.sam.demofactory.service.NoZuoNoDieService.onCreate(NoZuoNoDieService.kt:15)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3426)
at android.app.ActivityThread.-wrap4(ActivityThread.java:-1)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1744)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:171)
at android.app.ActivityThread.main(ActivityThread.java:6611)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
ANR 弹窗的显示原理
===========
我们来看一下这个没有弹窗的 ANR 是怎么发生的
首先来复习一下 说一说 Service 的启动流程
AMS 真正去启动 Service 调用的是 ActiveServices.realStartServiceLocked 方法:
// API 29 com.android.server.am.ActiveServices
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
…
bumpServiceExecutingLocked(r, execInFg, “create”);
}
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
…
scheduleServiceTimeoutLocked(r.app);
}
// 通过 Handler 延时发一条消息,延时时间则为 Service 触发的 ANR 时长
// SERVICE_TIMEOUT = 20*1000
// SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
如果 Service 在规定时间内启动完成,则这个消息会被 remove 掉,我们今天要看一下超时之后,收到这个消息是怎么处理的。
// com.android.server.am.ActivityManagerService.MainHandler
final class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
}
}
mServices 是 ActiveServices:
// com.android.server.am.ActiveServices#serviceTimeout
void serviceTimeout(ProcessRecord proc) {
…
if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
…
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
anrMessage = "executing service " + timeout.shortInstanceName;
}
…
if (anrMessage != null) {
proc.appNotResponding(null, null, null, null, false, anrMessage);
}
}
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
7596)]
[外链图片转存中…(img-W5775CWD-1715555537597)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!