一、概述
是否了解ANR的产生条件是否对Android App的进程运行机制有深入了解是否对Looper的消息机制有深刻的理解
在面试中经常也时常会遇到类似的问题,其实这两个八杆子打不着, 通过这篇文章我们好好捋一捋这两者的概念。
ANR类型
Service Timeout
前台服务 20s后台服务 200s
BroadcaseQueue Timeout
前台广播 10s后台广播 60s
ContentProvider Timeout
10s
InputDispatching Timeout
5s
ANR产生源码
这里以Service启动为例,AMS在调用startService后最终会执行到 ActiveServices的 scheduleServiceTimeoutLocked,这里就挑关键代码讲了
ActiveServices::scheduleServiceTimeoutLocked
static final int SERVICE_TIMEOUT
= 20*1000;
static final int 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
);
}
这里根据前台和后台发送了延迟消息
ActiveServices::serviceDoneExecutingLocked
private void serviceDoneExecutingLocked(ServiceRecord r
, boolean inDestroying
,
boolean finishing
) {
...
mAm
.mHandler
.removeMessages(ActivityManagerService
.SERVICE_TIMEOUT_MSG
, r
.app
);
...
}
这个时候就会把消息remove掉
ActiveServices::realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r
,
ProcessRecord app
, boolean execInFg
) throws RemoteException
{
...
bumpServiceExecutingLocked(r
, execInFg
, "create");
...
app
.thread
.scheduleCreateService(r
, r
.serviceInfo
,
mAm
.compatibilityInfoForPackageLocked(r
.serviceInfo
.applicationInfo
),
app
.repProcState
);
...
serviceDoneExecutingLocked(r
, inDestroying
, inDestroying
);
}
这里我们看下完整的调用链路,首先先发送一个定时消息,在调用Service的onCreate,如果onCreate在规定时间执行完,就会调用serviceDoneExecutingLocked将定时消息移除。
这里我们看看如果触发了ANR会走向哪里,ActivityManagerService.SERVICE_TIMEOUT_MSG我们看下这个消息
ActivityManagerService::MainHandler::handleMessage
final class MainHandler extends Handler {
@Override
public void handleMessage(Message msg
) {
switch (msg
.what
) {
...
case SERVICE_FOREGROUND_TIMEOUT_MSG
: {
mServices
.serviceForegroundTimeout((ServiceRecord
)msg
.obj
);
} break;
...
}}
}
这个mServices 其实是ActiveServices
ActiveServices::serviceForegroundTimeout
void serviceForegroundTimeout(ServiceRecord r
) {
ProcessRecord app
;
synchronized (mAm
) {
if (!r
.fgRequired
|| r
.destroying
) {
return;
}
app
= r
.app
;
if (app
!= null
&& app
.debugging
) {
return;
}
if (DEBUG_BACKGROUND_CHECK
) {
Slog
.i(TAG
, "Service foreground-required timeout for " + r
);
}
r
.fgWaiting
= false;
stopServiceLocked(r
);
}
if (app
!= null
) {
mAm
.mAppErrors
.appNotResponding(app
, null
, null
, false,
"Context.startForegroundService() did not then call Service.startForeground(): "
+ r
);
}
}
AppErrors::appNotResponding
final void appNotResponding(ProcessRecord app
, ActivityRecord activity
,
ActivityRecord parent
, boolean aboveSystem
, final String annotation
) {
...
makeAppNotRespondingLocked(app
,
activity
!= null
? activity
.shortComponentName
: null
,
annotation
!= null
? "ANR " + annotation
: "ANR",
info
.toString());
Message msg
= Message
.obtain();
msg
.what
= ActivityManagerService
.SHOW_NOT_RESPONDING_UI_MSG
;
msg
.obj
= new AppNotRespondingDialog.Data(app
, activity
, aboveSystem
);
mService
.mUiHandler
.sendMessage(msg
);
}
以上是整个ANR产生的源码部分,那我们继续看下Looper又究竟在干什么
主线程究竟在干什么
ActivityThread::main
public static void main(String
[] args
) {
Trace
.traceBegin(Trace
.TRACE_TAG_ACTIVITY_MANAGER
, "ActivityThreadMain");
...
Looper
.prepareMainLooper();
...
ActivityThread thread
= new ActivityThread();
thread
.attach(false, startSeq
);
if (sMainThreadHandler
== null
) {
sMainThreadHandler
= thread
.getHandler();
}
if (false) {
Looper
.myLooper().setMessageLogging(new
LogPrinter(Log
.DEBUG
, "ActivityThread"));
}
Trace
.traceEnd(Trace
.TRACE_TAG_ACTIVITY_MANAGER
);
Looper
.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这个在之前的文章介绍过了很多遍了,开启Looper等待消息过来并处理。
Looper和ANR的关系
理清了两者的概念我们就知道Looper是线程中处理消息的机制,而ANR是执行到某一个环节的时候为开发者在开发时对主线程进行监控。所以Looper不会产生ANR。
那接下来引出下一个问题,为什么Looper死循环不会导致CPU占用率过高呢?
Looper死循环为什么不会导致CPU占用率过高
- Looper
.cpp
struct epoll_event eventItems
[EPOLL_MAX_EVENTS
];
int eventCount
= epoll_wait(mEpollFd
.get(), eventItems
, EPOLL_MAX_EVENTS
, timeoutMillis
);
mEpollFd
.reset(epoll_create1(EPOLL_CLOEXEC
));
int result
= epoll_ctl(mEpollFd
.get(), EPOLL_CTL_ADD
, mWakeEventFd
.get(), &eventItem
);
在消息队列空的时候,会调用 epoll_wait,会等待文件的消息,文件有消息后,才会唤醒。阻塞是不会消耗CPU的时间片,不会导致CPU占用率高。这一点大家要理清楚。
这里留一个问题给大家思考
直接在Activity Sleep 5000ms,再Post一个runable会不会ANR?
深入理解Handler(一) — Android中为什么非UI线程不能更新UI 深入理解Handler(二) — 从源码角度来理解Android线程间消息传递机制 深入理解Handler(三) — Handler发送延时消息实现 深入理解Handler(四) — 主线程的Looper为什么不会导致应用的ANR