ANR 多是你們平時開發過程當中,相對比較少的一類的問題,從考覈過程當中就能看出來,不多有同窗可以回答完善。既然咱們在開發中會遇到,那麼咱們至少須要知道 Framework 層的原理,ANR 怎麼監控?ANR 怎麼分析解決?這三個維度。java
ANR 大體分爲這麼兩種:四大組件啓動超時和 Input 響應超時,它們之間的底層實現原理也是有些差異的,四大組件啓動超時 ANR 是一個埋炸彈拆炸彈的過程,而 Input 響應超時 ANR 是一個排雷的過程。四大組件啓動超時的原理我相信你們都能看懂,但 Input 響應超時可能就未必了,由於它須要必定的 C++ 基礎。markdown
咱們在 Framework 源碼分析篇中詳細介紹過 Service 啓動流程。其中在 Service 進程 attach 到 system_server 進程的過程當中會調用 realStartServiceLocked()
方法來埋下炸彈.app
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
...
// 發送delay消息 (SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
try {
...
// 最終建立 Service 並執行服務的 onCreate() 方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
mAm.appDiedLocked(app);
throw e;
} finally {
...
}
}
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;
}
long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
// 當超時後仍沒有 remove 該 SERVICE_TIMEOUT_MSG 消息,則執行 service Timeout 流程
mAm.mHandler.sendMessageAtTime(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}
複製代碼
該方法的主要工做發送 delay 消息 (SERVICE_TIMEOUT_MSG)。炸彈已埋下, 咱們並不但願炸彈被引爆, 那麼就須要在炸彈爆炸以前拆除炸彈。oop
private void handleCreateService(CreateServiceData data) {
...
// 反射建立 Service 對象
java.lang.ClassLoader cl = packageInfo.getClassLoader();
Service service = (Service) cl.loadClass(data.info.name).newInstance();
...
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();
// 拆除炸彈引線
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (Exception e) {
...
}
}
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);
...
}
...
}
複製代碼
若是整個啓動過程當中一切正常那麼就會 removeMessages ,若是不正常則會引爆炸彈源碼分析
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
...
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
...
}
...
}
}
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.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
mAm.appNotResponding(proc, null, null, false, anrMessage);
}
}
複製代碼
在正式開始分析 Input 事件的 ANR 觸發原理以及觸發場景以前,你們須要先回顧一下 Input 事件的分發流程,這個也在 Framework 源碼分析篇中。post
Input ANR 時間區間即是指當前此次的事件 dispatch 過程當中執行 findFocusedWindowTargetsLocked() 方法到下一次執行 resetANRTimeoutsLocked() 的時間區間。看源碼有 5 個時機會 reset。都位於InputDispatcher.cpp 文件:this
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (! mPendingEvent) {
......
// Get ready to dispatch the event.
resetANRTimeoutsLocked();
}
......
switch (mPendingEvent->type) {
case EventEntry::TYPE_MOTION: {
......
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
......
}
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
int32_t injectionResult;
String8 reason;
// If there is no currently focused window and no focused application
// then drop the event.
if (mFocusedWindowHandle == NULL) {
if (mFocusedApplicationHandle != NULL) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplicationHandle, NULL, nextWakeupTime,
"Waiting because no window has focus but there is a "
"focused application that may eventually add a window "
"when it finishes starting up.");
goto Unresponsive;
}
goto Failed;
}
......
return injectionResult;
}
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime, const char* reason) {
if (applicationHandle == NULL && windowHandle == NULL) {
...
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
if (windowHandle != NULL) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else if (applicationHandle != NULL) {
timeout = applicationHandle->getDispatchingTimeout(
DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else {
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
mInputTargetWaitStartTime = currentTime;
// 這個就是超時時間了
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationHandle.clear();
if (windowHandle != NULL) {
mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
}
if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
mInputTargetWaitApplicationHandle = applicationHandle;
}
}
}
if (mInputTargetWaitTimeoutExpired) {
return INPUT_EVENT_INJECTION_TIMED_OUT;
}
}
複製代碼
視頻連接: pan.baidu.com/s/1bYHPvx3I…
視頻密碼: l5dfspa