Android ANR产生过程解析

Android2.2及此前,输入事件处理都在Java层,Android2.3及之后,为提高处理效率,输入事件处理移到了Native 层。
为了简化分析,本文的分析是基于Android2.2的源码。

1、ANR产生机制:
    硬件收集到的输入事件都会插入到WindowManagerService.mQueue(KeyQ类)中,同时 WindowManagerService.InputDispatcherThread这个线程运行在系统进程中,它循环地处理 mQueue内的消息。InputDispatcherThread在处理事件的过程中,会不断的检测处理过程是否超时,一旦超时,会通过一 系列的回调通知AMS,由AMS控制弹出ANR对话框。
    超时检测处理过程实际上是一个wait()/notify()的过程。首先,如果一个输入事件进入检测流程时,发现当前正在处理输入事件,就会调用 wait()阻塞输入事件处理线程,wait的时间为10s。在一个输入事件处理完成时,会调用notify()让输入事件处理线程继续执 行。继续执行时就会计算整个过程一共花了多长时间来判断是否是ANR。

2、流程解析:

a)输入事件1到达,发现KeyWaitter的mFinished(事件已经处理完成)为true,无需等待,直接异步将事件1移交 ViewRoot 进行处理,并把mFinished置为false。
b)ViewRoot处理事件1时,会同步地进行事件分发处理,分发完成后才执行KeyWaitter.finishKey()方法。 注:finishKey()方法会把mFinished置为false,并调用notify()唤醒 InputDispatcherThread线程。
c)在事件1的分发过程中发现它是点击事件,回调到View.onClick()方法,于是同步执行了耗时任务,导致ViewRoot卡在这 个分发流程。
d)事件2到达,发现KeyWaitter的mFinished是false,调用wait()方法将 InputDispatcherThread线程置为等待状态,且wait()的时间为10s。
e)10内,由于在c中没有执行KeyWaitter.finishKey()方法,所以InputDispatcherThread一直处 于等待状态。
f)10s后,InputDispatcherThread线程继续执行,计算时间后发现已经超时,随后通知AMS弹出ANR对话框。

 

3、时序图:


4、相关代码:
1、输入事件的检测流程:
WindowManagerService.java

.................KeyWaitter.waitForNextEventTarget()方 法.................

 1 .................
 2 long startTime = SystemClock.uptimeMillis();
 3 long keyDispatchingTimeout = 5 * 1000;
 4 .................
 5 while (true) {
 6     .................
 7     long curTimeout = keyDispatchingTimeout;
 8     .................
 9     try {
10                         // after that continue
11                         // processing keys, so we don't get stuck.
12                         if (DEBUG_INPUT) Slog.v(
13                                 TAG, "Waiting for key dispatch: " + curTimeout);
14                         wait(curTimeout);
15                         if (DEBUG_INPUT) Slog.v(TAG, "Finished waiting @"
16                                 + SystemClock.uptimeMillis() + " startTime="
17                                 + startTime + " switchTime=" + mTimeToSwitch
18                                 + " target=" + targetWin + " mLW=" + mLastWin
19                                 + " mLB=" + mLastBinder + " fin=" + mFinished
20                                 + " mCurrentFocus=" + mCurrentFocus);
21                     } catch (InterruptedException e) {
22                     }
23     .................
24      if (mWasFrozen) {
25                     waitedFor = 0;
26                     mWasFrozen = false;
27                 } else {
28                     waitedFor = SystemClock.uptimeMillis() - startTime;
29                 }
30 
31                 if (waitedFor >= keyDispatchingTimeout && mTimeToSwitch == 0) {
32             .................
33             if (at != null) {
34                            try {
35                             long timeout = at.getKeyDispatchingTimeout();
36                             if (timeout > waitedFor) {
37                                 // we did not wait the proper amount of time for this application.
38                                 // set the timeout to be the real timeout and wait again.
39                                 keyDispatchingTimeout = timeout - waitedFor;
40                                 continue;
41                             } else {
42                                 abort = at.keyDispatchingTimedOut();
43                             }
44                         } catch (RemoteException ex) {
45                         }
46                     }
47         .................
48     .................
49 .................

2、处理完输入事件时的操作:
ViewRoot.java
..........handleMessage()方法...........

 1 .....................
 2 case DISPATCH_POINTER: {
 3 try {
 4     .....................
 5      handled = mView.dispatchTouchEvent(event);
 6     .....................
 7 }finally {
 8                 if (callWhenDone) {
 9                     try {
10                         sWindowSession.finishKey(mWindow);
11                     } catch (RemoteException e) {
12                     }
13                 }
14         .....................
15 }

WindowManagerService.java
..........KeyWaitter.finishedKey()方法..........

1 .....................
2     notifyAll();
3 .....................

3、弹出ANR对话框:
ActivityManagerService.java

 1 .................
 2 final Handler mHandler = new Handler() {
 3         public void handleMessage(Message msg) {
 4 .................
 5 case SHOW_NOT_RESPONDING_MSG: {
 6                 synchronized (ActivityManagerService.this) {
 7                     HashMap data = (HashMap) msg.obj;
 8                     ProcessRecord proc = (ProcessRecord)data.get("app");
 9                     if (proc != null && proc.anrDialog != null) {
10                         Slog.e(TAG, "App already has anr dialog: " + proc);
11                         return;
12                     }
13                     
14                     broadcastIntentLocked(null, null, new Intent("android.intent.action.ANR"),
15                             null, null, 0, null, null, null,
16                             false, false, MY_PID, Process.SYSTEM_UID);
17 
18                     Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
19                             mContext, proc, (HistoryRecord)data.get("activity"));
20                     d.show();
21                     proc.anrDialog = d;
22                 }
23                 
24                 ensureBootCompleted();
25             }
26 .........

 

posted @ 2014-01-02 16:26  Li.CK  阅读(2703)  评论(0)    收藏  举报