ANR 之 触发原理解析

程序员Android

共 17009字,需浏览 35分钟

 ·

2021-02-22 08:53

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一 概述

ANR (Application Not responding),是指应用程序未响应,Android 系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成 ANR。一般地,这时往往会弹出一个提示框,告知用户当前 xxx 未响应,用户可选择继续等待或者Force Close。

那么哪些场景会造成 ANR 呢?

  • Service Timeout:前台服务在 20s 内未执行完成

  • BroadcastQueue Timeout:前台广播在 10s 内未执行完成

  • ContentProvider Timeout:内容提供者,超时 10s

  • InputDispatching Timeout:输入事件分发超时 5s,包括按键和触摸事件

触发 ANR 的过程可分为三个步骤:埋炸弹,拆炸弹,引爆炸弹

二 Service

Service Timeout 是位于 ”ActivityManager” 线程中的 AMS.MainHandler 收到 SERVICE_TIMEOUT_MSG 消息时触发。

对于 Service 有两类:

  • 对于前台服务,则超时为 SERVICE_TIMEOUT = 20s

  • 对于后台服务,则超时为 SERVICE_BACKGROUND_TIMEOUT = 200s

由变量 ProcessRecord.execServicesFg 来决定是否前台启动

2.1 埋炸弹

文章Android四大组件系列4 Service启动流程详细介绍了 Service 的启动流程。其中在 Service 进程 attach 到 system_server 进程的过程中会调用 realStartServiceLocked() 方法来埋下炸弹。

2.1.1 ActiveServices.realStartServiceLocked

ActiveServices.java

private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
......
//发送delay消息(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
try {
......
//最终执行服务的onCreate()方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(
r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
......
} finally {
......
}
......
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

2.1.2 ActiveServices.bumpServiceExecutingLocked

ActiveServices.java

private final void bumpServiceExecutingLocked(ServiceRecord r,
boolean fg, String why) {
......
scheduleServiceTimeoutLocked(r.app);
......
}

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;
//当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) :
(now+ SERVICE_BACKGROUND_TIMEOUT));
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

该方法的主要工作是发送 delay 消息 (SERVICE_TIMEOUT_MSG)。炸弹已埋下,我们并不希望炸弹被引爆,那么就需要在炸弹爆炸之前拆除炸弹。

2.2 拆炸弹

在 system_server 进程 ActiveServices.realStartServiceLocked() 调用的过程会埋下一颗炸弹,超时没有启动完成则会爆炸。那么什么时候会拆除这颗炸弹的引线呢?我们来看经过 Binder 等进程间通信层层调用进入目标进程的主线程 handleCreateService() 函数。

2.2.1 ActivityThread.handleCreateService

ActivityThread.java

private void handleCreateService(CreateServiceData data) {
......
java.lang.ClassLoader cl = packageInfo.getClassLoader();
Service service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
......
try {
//创建ContextImpl对象
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
//创建Application对象
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//调用服务onCreate()方法
service.onCreate();
mServices.put(data.token, service);

//拆除炸弹引线
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (Exception e) {
......
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

在这个过程会创建目标服务对象,并回调其 onCreate() 方法,紧接着再次经过 IPC 调用回到 system_server 来执行 serviceDoneExecuting。

2.2.2 ActiveServices.serviceDoneExecutingLocked

ActiveServices.java

private void serviceDoneExecutingLocked(ServiceRecord r,
boolean inDestroying, boolean finishing) {
......
if (r.executeNesting <= 0) {
if (r.app != null) {
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
//当前服务所在进程中没有正在执行的service
mAm.mHandler.removeMessages(
ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
......
}
......
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

该方法的主要工作是当 service 启动完成,则移除服务超时消息 SERVICE_TIMEOUT_MSG。

2.3 引爆炸弹

前面介绍了埋炸弹和拆炸弹的过程,如果在炸弹倒计时结束之前成功拆卸炸弹,那么就没有爆炸的机会,但是世事难料,总有些极端情况下无法即时拆除炸弹,导致炸弹爆炸,其结果就是 App 发生 ANR。接下来,分析下炸弹爆炸的现场。

在 system_server 进程中有一个 Handler 线程,名叫 ”ActivityManager”。当倒计时结束便会向该 Handler 线程发送一条消息 SERVICE_TIMEOUT_MSG。

2.3.1 MainHandler.handleMessage

ActivityManagerService.java ::MainHandler

final class MainHandler extends Handler {
......
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
......
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
......
}
......
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

2.3.2 ActiveServices.serviceTimeout

ActiveServices.java

void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
final long now = SystemClock.uptimeMillis();
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT :
SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
for (int i=proc.executingServices.size()-1; i>=0; i--) {
ServiceRecord sr = proc.executingServices.valueAt(i);
if (sr.executingStart < maxTime) {
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
if (timeout != null &&
mAm.mProcessList.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(timeout);
timeout.dump(pw, " ");
pw.close();
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer,
LAST_ANR_LIFETIME_DURATION_MSECS);
anrMessage = "executing service " + timeout.shortName;
}
}

if (anrMessage != null) {
//当存在timeout的service,则执行appNotResponding
proc.appNotResponding(null, null, null, null, false, anrMessage);
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

其中 anrMessage 的内容为 ”executing service [发送超时 serviceRecord 信息]”。

三 BroadcastReceiver

BroadcastReceiver Timeout 是位于 ”ActivityManager” 线程中的 BroadcastQueue.BroadcastHandler 收到 BROADCAST_TIMEOUT_MSG 消息时触发。

对于广播队列在 AMS 中有两个:mFgBroadcastQueue 队列和 mBgBroadcastQueue 队列:

  • 对于前台广播,则超时为 BROADCAST_FG_TIMEOUT = 10s

  • 对于后台广播,则超时为 BROADCAST_BG_TIMEOUT = 60s

3.1 埋炸弹

我们之前已经分析过广播的注册和发送流程,通过调用 processNextBroadcast 来处理广播。其流程为先处理并行广播,再处理当前有序广播,最后获取并处理下条有序广播。

3.1.1 processNextBroadcast

final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
processNextBroadcastLocked(fromMsg, false);
}
}


final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
......
//part 2: 处理当前有序广播
do {
final long now = SystemClock.uptimeMillis();
r = mDispatcher.getNextBroadcastLocked(now);
......
//获取所有该广播所有的接收者
int numReceivers =
(r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && !r.timeoutExempt &&
r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mConstants.TIMEOUT*numReceivers))) {
//当广播处理时间超时,则强制结束这条广播
broadcastTimeoutLocked(false);
......
}
}
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
if (r.resultTo != null) {
//处理广播消息消息
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
r.resultTo = null;
}
//拆炸弹
cancelBroadcastTimeoutLocked();
}
} while (r == null);
......
//part 3: 获取下条有序广播
r.receiverTime = SystemClock.uptimeMillis();
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
//埋炸弹
setBroadcastTimeoutLocked(timeoutTime);
}
......
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

对于广播超时处理时机:

1.首先在 part3 的过程中 setBroadcastTimeoutLocked(timeoutTime) 设置超时广播消息
2.然后在 part2 根据广播处理情况来处理:

  • 当广播接收者等待时间过长,则调用 broadcastTimeoutLocked(false)

  • 当执行完广播,则调用 cancelBroadcastTimeoutLocked

3.1.2 setBroadcastTimeoutLocked

final void setBroadcastTimeoutLocked(long timeoutTime) {
if (!mPendingBroadcastTimeoutMessage) {
Message msg =
mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

设置定时广播 BROADCAST_TIMEOUT_MSG,即当前往后推 mTimeout 时间广播还没处理完毕,则进入广播超时流程。

3.2 拆炸弹

broadcast 跟 service 超时机制大体上相同,但有一个非常隐蔽的技能点,那就是通过静态注册的广播超时会受 SharedPreferences 的影响。

3.2.1 sendFinished

关于广播是否考虑 SP 的情况取决于如下代码:

BroadcastReceiver.PendingResult.finish

public final void finish() {
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManager.getService();
if (QueuedWork.hasPendingWork()) {
//当SP有未同步到磁盘的工作,则需等待其完成,才告知系统已完成该广播
QueuedWork.queue(new Runnable() {
@Override public void run() {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast after work to component " + mToken);
sendFinished(mgr);
}
}, false);
} else {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to component " + mToken);
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to " + mToken);
final IActivityManager mgr = ActivityManager.getService();
sendFinished(mgr);
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

可见,只有 XML 静态注册的广播超时检测过程会考虑是否有 SP 尚未完成,动态广播并不受其影响。

3.2.2 cancelBroadcastTimeoutLocked

final void cancelBroadcastTimeoutLocked() {
if (mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
mPendingBroadcastTimeoutMessage = false;
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

移除广播超时消息 BROADCAST_TIMEOUT_MSG。

3.3 引爆炸弹

3.3.1 BroadcastHandler.handleMessage

BroadcastQueue.java ::BroadcastHandler

private final class BroadcastHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
+ mQueueName + "]");
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
}
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

3.3.2 broadcastTimeoutLocked

BroadcastQueue.java

final void broadcastTimeoutLocked(boolean fromMsg) {
if (fromMsg) {//fromMsg = true
mPendingBroadcastTimeoutMessage = false;
}
if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked()
== null) {
return;
}
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
if (fromMsg) {
//当系统还没有准备就绪时,广播处理流程中不存在广播超时
if (!mService.mProcessesReady) {
return;
}
......
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (timeoutTime > now) {
//如果当前正在执行的receiver没有超时,则重新设置广播超时
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
if (r.state == BroadcastRecord.WAITING_SERVICES) {
//广播已经处理完成,但需要等待已启动service执行完成。当等待足够时间,则处理下一条广播。
r.curComponent = null;
r.state = BroadcastRecord.IDLE;
processNextBroadcast(false);
return;
}
r.receiverTime = now;
if (!debugging) {
r.anrCount++;//当前BroadcastRecord的anr次数执行加1操作
}
Object curReceiver;
if (r.nextReceiver > 0) {
curReceiver = r.receivers.get(r.nextReceiver-1);
r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
} else {
curReceiver = r.curReceiver;
}
//查询App进程
if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter)curReceiver;
if (bf.receiverList.pid != 0
&& bf.receiverList.pid != ActivityManagerService.MY_PID) {
synchronized (mService.mPidsSelfLocked) {
app = mService.mPidsSelfLocked.get(
bf.receiverList.pid);
}
}
} else {
app = r.curApp;
}
if (app != null) {
anrMessage = "Broadcast of " + r.intent.toString();
}

if (mPendingBroadcast == r) {
mPendingBroadcast = null;
}
//继续移动到下一个广播接收者
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();

if (!debugging && anrMessage != null) {
mHandler.post(new AppNotResponding(app, anrMessage));
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

3.3.3 AppNotResponding

BroadcastQueue.java

private final class AppNotResponding implements Runnable {
private final ProcessRecord mApp;
private final String mAnnotation;

public AppNotResponding(ProcessRecord app, String annotation) {
mApp = app;
mAnnotation = annotation;
}

@Override
public void run() {
// 进入ANR处理流程
mApp.appNotResponding(null, null, null, null, false, mAnnotation);
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

四 ContentProvider

ContentProvider Timeout 是位于 ”ActivityManager” 线程中的 AMS.MainHandler 收到 CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG 消息时触发。

ContentProvider 超时为 CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s。这个跟前面的 Service 和 BroadcastQueue 完全不同,由 Provider 进程启动过程相关。

4.1 埋炸弹

我们之前详细介绍了 Provider 启动流程。埋炸弹的过程其实是在进程创建的过程,进程创建后会调用 attachApplicationLocked() 进入 system_server 进程。

4.1.1 AMS.attachApplicationLocked

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
ProcessRecord app;
long startTime = SystemClock.uptimeMillis();
long bindApplicationTimeMillis;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid); // 根据pid获取ProcessRecord
}
}
......
//系统处于ready状态或者该app为FLAG_PERSISTENT进程则为true
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ?
generateApplicationProvidersLocked(app) : null;

//app进程存在正在启动中的provider
// 则超时10s后发送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(
CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
......
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

4.2 拆炸弹

当 provider 成功 publish 之后,便会拆除该炸弹。

4.2.1 AMS.publishContentProviders

public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
......
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);

final int N = providers.size();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
......
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
ComponentName comp =
new ComponentName(dst.info.packageName, dst.info.name);
//将该provider添加到mProviderMap
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}

int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
//将该provider移除mLaunchingProviders队列
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
//成功pubish则移除该消息
if (wasInLaunchingProviders) {
mHandler.removeMessages(
CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
//唤醒客户端的wait等待方法
dst.notifyAll();
}
......
}
}
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

4.3 引爆炸弹

在 system_server 进程中有一个 Handler 线程, 名 叫”ActivityManager”。当倒计时结束便会向该 Handler 线程发送 一条信息 CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG

4.3.1 MainHandler.handleMessage

ActivityManagerService.java ::MainHandler

final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
......
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processContentProviderPublishTimedOutLocked(app);
}
} break;
......
}
......
}
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

4.3.2 AMS.processContentProviderPublishTimedOutLocked

private final void processContentProviderPublishTimedOutLocked(
ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
mProcessList.removeProcessLocked(app, false, true,
"timeout publishing content providers");
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

4.3.3 AMS.cleanupAppInLaunchingProvidersLocked

boolean cleanupAppInLaunchingProvidersLocked(
ProcessRecord app, boolean alwaysBad) {
boolean restart = false;
for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.launchingApp == app) {
if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
restart = true;
} else {
//移除死亡的provider
removeDyingProviderLocked(app, cpr, true);
}
}
}
return restart;
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

removeDyingProviderLocked() 的功能跟进程的存活息息相关

  • 对于 stable 类型的 provider (即 conn.stableCount > 0),则会杀掉所有跟该 provider 建立 stable 连接的非persistent 进程

  • 对于 unstable 类的 provider (即 conn.unstableCount > 0),并不会导致 client 进程被级联所杀

4.3.4 ProcessList.removeProcessLocked

boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
final int uid = app.uid;
......
//移除mProcessNames中的相应对象
removeProcessNameLocked(name, uid);
mService.mAtmInternal.clearHeavyWeightProcessIfEquals(
app.getWindowProcessController());

boolean needRestart = false;
if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) ||
(app.pid == 0 && app.pendingStart)) {
int pid = app.pid;
if (pid > 0) {
......
}
boolean willRestart = false;
if (app.isPersistent() && !app.isolated) {
if (!callerWillRestart) {
willRestart = true;
} else {
needRestart = true;
}
}
app.kill(reason, true);//杀进程
mService.handleAppDiedLocked(app, willRestart, allowRestart);
if (willRestart) {
removeLruProcessLocked(app);
mService.addAppLocked(app.info, null, false, null /* ABI override */);
}
} else {
mRemovedProcesses.add(app);
}

return needRestart;
}
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

五 总结

当出现 ANR 时,都是调用到 AMS.appNotResponding() 方法,详细过程见文章理解Android ANR的信息收集过程. 当然这里介绍的 provider 例外。

5.1 Timeout时长

  • 对于前台服务,则超时为 SERVICE_TIMEOUT = 20s

  • 对于后台服务,则超时为 SERVICE_BACKGROUND_TIMEOUT = 200s

  • 对于前台广播,则超时为 BROADCAST_FG_TIMEOUT = 10s

  • 对于后台广播,则超时为 BROADCAST_BG_TIMEOUT = 60s

  • ContentProvider 超时为 CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s

5.2 超时检测

Service 超时检测机制:

  • 超过一定时间没有执行完相应操作来触发移除延时消息,则会触发 anr

BroadcastReceiver 超时检测机制:

  • 有序广播的总执行时间超过 2* receiver 个数 * timeout 时长,则会触发 anr

  • 有序广播的某一个 receiver 执行过程超过 timeout 时长,则会触发 anr

另外:

  • 对于 Service,Broadcast,Input 发生 ANR 之后,最终都会调用 AMS.appNotResponding

  • 对于 provider,在其进程启动时 publish 过程可能会出现 ANR,则会直接杀进程以及清理相应信息,而不会弹出 ANR 的对话框。appNotRespondingViaProvider() 过程会走 appNotResponding(),这个就不介绍了,很少使用,由用户自定义超时时间.

 原文链接:https://blog.csdn.net/liuwg1226/article/details/113834833

友情推荐:

Android 开发干货集锦

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

点个在看,方便您使用时快速查找!

浏览 31
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报