ANR代码流程分析.doc_第1页
ANR代码流程分析.doc_第2页
ANR代码流程分析.doc_第3页
ANR代码流程分析.doc_第4页
ANR代码流程分析.doc_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Android ANR代码分析(技术文档)内容目录181 什么是ANR52 ANR代码执行流程52.1 事件分发超时ANR52.2 启动输入分发线程(InputDispatcherThread)52.3 输入事件的分发流程72.4 ANR处理流程132.5 针对Service超时的ANR处理流程172.6 针对Broadcast超时的ANR处理流程182.7 ANR超时时间222.8 序列图241 什么是ANRANR是“Application Not Responding”的缩写,即“应用程序无响应”。在Android中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会监测应用程序的响应时间,如果应用程序主线程(即UI线程)在超时时间内对输入事件没有处理完毕,或者对特定操作没有执行完毕,就会出现ANR。对于输入事件没有处理完毕产生的ANR,Android会显示一个对话框,提示用户当前应用程序没有响应,用户可以选择继续等待或者关闭这个应用程序(也就是杀掉这个应用程序的进程)。2 ANR代码执行流程本文档所有分析均基于Android 4.4原生代码。2.1 事件分发超时ANR响应事件超时的ANR流程大概如下,在系统输入管理服务进程(InputManagerService)中有一个单独线程(InputDispatcherThread)会专门管理输入事件分发,在该线程处理输入事件的过程中,会调用InputDispatcher不断的检测处理过程是否超时,一旦超时,会通过一系列的回调通知WMS的notifyANR函数,最终会触发AMS中mHandler对象里的SHOW_NOT_RESPONDING_MSG这个事件并在AMS中进行相应处理,此时界面上就会显示系统ANR提示对话框。2.2 启动输入分发线程(InputDispatcherThread)InputDispatcherThread作为输入事件分发处理的核心线程,会随系统服务InputManagerService的启动而启动。.frameworkbaseservicesjavacomandroidserverinputInputManagerService.javapublic class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor, DisplayManagerService.InputManagerFuncs public InputManagerService(Context context, Handler handler) . mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue(); public void start() Slog.i(TAG, Starting input manager); nativeStart(mPtr); . InputManagerService在启动时会调用nativeStart方法,其中会启动InputDispatcherThread线程。具体代码在JNI方法nativeInit和nativeStart中实现。.frameworkbaseservicesjnicom_android_server_input_InputManagerService.cppstatic jint nativeInit(JNIEnv* env, jclass clazz, jobject serviceObj, jobject contextObj, jobject messageQueueObj) . NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue-getLooper(); im-incStrong(0); return reinterpret_cast(im);static void nativeStart(JNIEnv* env, jclass clazz, jint ptr) NativeInputManager* im = reinterpret_cast(ptr); status_t result = im-getInputManager()-start(); .在nativeInit中新建InputManager对象,并在nativeStart中调用InutManager的start方法。.frameworkbaseservicesinputInputManager.cppInputManager:InputManager( const sp& eventHub, const sp& readerPolicy, const sp& dispatcherPolicy) mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize();void InputManager:initialize() mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); status_t InputManager:start() status_t result = mDispatcherThread-run(InputDispatcher, PRIORITY_URGENT_DISPLAY); . return OK;在InputManager构造方法中,会新建InputDispatcher对象,该对象会做为一个核心对象来处理所有输入事件分发逻辑。在initialize方法中,分发线程对象被建立,并在start方法中启动InputDispatcherThread线程。2.3 输入事件的分发流程输入事件的分发逻辑主要是在InputDispatcher类中实现的。先看一下InputDispatcherThread线程类:.frameworkbaseservicesinputInputDispatcher.cppInputDispatcherThread:InputDispatcherThread(const sp& dispatcher) : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) bool InputDispatcherThread:threadLoop() mDispatcher-dispatchOnce(); return true;该线程的loop方法中,只执行InputDispatcher类的dispatchOnce方法。下面集中讨论一下InputDispatcher类:void InputDispatcher:dispatchOnce() nsecs_t nextWakeupTime = LONG_LONG_MAX; / acquire lock AutoMutex _l(mLock); mDispatcherIsAliveCondition.broadcast(); / Run a dispatch loop if there are no pending commands. / The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked() dispatchOnceInnerLocked(&nextWakeupTime); / Run all pending commands if there are any. / If any commands were run then force the next poll to wake up immediately. if (runCommandsLockedInterruptible() nextWakeupTime = LONG_LONG_MIN; / release lock / Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t currentTime = now(); int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); mLooper-pollOnce(timeoutMillis);InputDispatcher类维护了一个command队列,在dispatchOnce方法中会检查该队列中是否有command项,没有的话会调用dispatchOnceInnerLocked方法做进一步处理,有的话则依序处理队列中所有command,并设置等待时间触发下一次轮询。DispatchOnceInnerLocked方法主要是检查是否有pending event,有的话就进行分发处理,下面具体关注与ANR相关的部分代码。void InputDispatcher:dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) nsecs_t currentTime = now(); . switch (mPendingEvent-type) . case EventEntry:TYPE_KEY: KeyEntry* typedEntry = static_cast(mPendingEvent); . done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; case EventEntry:TYPE_MOTION: MotionEntry* typedEntry = static_cast(mPendingEvent); . done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); break; default: ALOG_ASSERT(false); break; 根据输入事件的类型进行分发处理,和ANR相关的有两种类型:按键和触摸。先看按键事件处理:bool InputDispatcher:dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) . / Identify targets. Vector inputTargets; int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); if (injectionResult = INPUT_EVENT_INJECTION_PENDING) return false; setInjectionResultLocked(entry, injectionResult); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) return true; addMonitoringTargetsLocked(inputTargets); / Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true;.int32_t InputDispatcher:findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, Vector& inputTargets, nsecs_t* nextWakeupTime) int32_t injectionResult; / 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; ALOGI(Dropping event because there is no focused window or focused application.); injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; . / If the currently focused window is paused then keep waiting. if (mFocusedWindowHandle-getInfo()-paused) injectionResult = handleTargetsNotReadyLocked(currentTime, entry, mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, Waiting because the focused window is paused.); goto Unresponsive; / If the currently focused window is still working on previous events then keep waiting. if (!isWindowReadyForMoreInputLocked(currentTime, mFocusedWindowHandle, entry) injectionResult = handleTargetsNotReadyLocked(currentTime, entry, mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, Waiting because the focused window has not finished processing the input events that were previously delivered to it.); goto Unresponsive; .在该处理过程中,有一步是获取响应该事件的window target,主要是调用findFocusedWindowTargetsLocked完成。对此有三种情况,系统会认为没有ready的target:1)没有找到focused window和focused application; 2) focused window当前正处于pause状态; 3)focused window还没有处理完之前的事件。这些时候,handleTargetsNotReadyLocked方法就会被调用来处理这些异常情况。/ Default input dispatching timeout if there is no focused application or paused window/ from which to determine an appropriate dispatching timeout.const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; / 5 32_t InputDispatcher:handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp& applicationHandle, const sp& windowHandle, nsecs_t* nextWakeupTime, const char* reason) . if (applicationHandle = NULL & windowHandle = NULL) if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; mInputTargetWaitStartTime = currentTime; mInputTargetWaitTimeoutTime = LONG_LONG_MAX; mInputTargetWaitTimeoutExpired = false; mInputTargetWaitApplicationHandle.clear(); else if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) nsecs_t timeout; 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 (currentTime = mInputTargetWaitTimeoutTime) onANRLocked(currentTime, applicationHandle, windowHandle, entry-eventTime, mInputTargetWaitStartTime, reason); / Force poll loop to wake up immediately on next iteration once we get the / ANR response back from the policy. *nextWakeupTime = LONG_LONG_MIN; return INPUT_EVENT_INJECTION_PENDING; else / Force poll loop to wake up when timeout is due. if (mInputTargetWaitTimeoutTime *nextWakeupTime) *nextWakeupTime = mInputTargetWaitTimeoutTime; return INPUT_EVENT_INJECTION_PENDING; 当等待时间超时之后,就会触发ANR事件,并调用onANRLocked方法来做进一步处理。再看触摸事件的流程:bool InputDispatcher:dispatchMotionLocked( nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) Vector inputTargets; bool conflictingPointerActions = false; int32_t injectionResult; if (isPointerEvent) / Pointer event. (eg. touchscreen) injectionResult = findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime, &conflictingPointerActions); else / Non touch event. (eg. trackball) injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); .int32_t InputDispatcher:findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, Vector& inputTargets, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) . / If there is an error window but it is not taking focus (typically because / it is invisible) then wait for it. Any other focused window may in / fact be in ANR state. if (topErrorWindowHandle != NULL & newTouchedWindowHandle != topErrorWindowHandle) injectionResult = handleTargetsNotReadyLocked(currentTime, entry, NULL, NULL, nextWakeupTime, Waiting because a system error window is about to be displayed.); injectionPermission = INJECTION_PERMISSION_UNKNOWN; goto Unresponsive; . / Ensure all touched foreground windows are ready for new input. for (size_t i = 0; i getInfo()-paused) injectionResult = handleTargetsNotReadyLocked(currentTime, entry, NULL, touchedWindow.windowHandle, nextWakeupTime, Waiting because the touched window is paused.); goto Unresponsive; / If the touched window is still working on previous events then keep waiting. if (!isWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, entry) injectionResult = handleTargetsNotReadyLocked(currentTime, entry, NULL, touchedWindow.windowHandle, nextWakeupTime, Waiting because the touched window has not finished processing the input events that were previously delivered to it.); goto Unresponsive; 该流程ANR相关部分与按键处理流程类似,对于异常情况最终也调用handleTargetsNotReadyLocked方法来处理。2.4 ANR处理流程void InputDispatcher:onANRLocked( nsecs_t currentTime, const sp& applicationHandle, const sp& windowHandle, nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) . CommandEntry* commandEntry = postCommandLocked( & InputDispatcher:doNotifyANRLockedInterruptible); commandEntry-inputApplicationHandle = applicationHandle; commandEntry-inputWindowHandle = windowHandle; commandEntry-reason = reason;InputDispatcher类的onANRLocked方法,会在command队列中post一个command,在处理该command时会调用关联的doNotifyANRLockedInterruptible方法。void InputDispatcher:doNotifyANRLockedInterruptible( CommandEntry* commandEntry) . nsecs_t newTimeout = mPolicy-notifyANR( commandEntry-inputApplicationHandle, commandEntry-inputWindowHandle, commandEntry-reason); .该方法会调用policy接口的notifyANR方法,其中通过com_android_server_input_InputManagerService.cpp中的JNI接口调用InputManagerService的notifyANR方法,该方法也是一个wrapper method,最终实现是在InputMonitor类的notifyANR方法中。.frameworkbaseservicesjavacomandroidserverinputInputManagerService.java private long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) return mWindowManagerCallbacks.notifyANR( inputApplicationHandle, inputWindowHandle, reason); 通过一系列调用,最终由ActivityManagerService弹出ANR对话框向用户提示“应用无响应”信息。.frameworkbaseservicesjavacomandroidserverwmInputMonitor.java public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) . if (appWindowToken != null & appWindowToken.appToken != null) try / Notify the activity manager about the timeout and let it decide whether / to abort dispatching or keep waiting. boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason); if (! abort) / The activity manager declined to abort dispatching. / Wait a bit longer and timeout again later. return appWindowToken.inputDispatchingTimeoutNanos; catch (RemoteException ex) else if (windowState != null) try / Notify the activity manager about the timeout and let it decide whether / to abort dispatching or keep waiting. long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut( windowState.mSession.mPid, aboveSystem, reason); if (timeout = 0) / The activity manager declined to abort dispatching. / Wait a bit longer and timeout again later. return timeout; catch (RemoteException ex) return 0; / abort dispatching .frameworkbaseservicesjavacomandroidserveramActivityManagerService.java public boolean inputDispatchingTimedOut(final ProcessRecord proc, final ActivityRecord activity, final ActivityRecord parent, final boolean aboveSystem, String reason) . mHandler.post(new Runnable

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论