Android按键超时的ANR原理小结
来源:互联网 发布:excel数据验证限制 编辑:程序博客网 时间:2024/05/29 03:19
1) 按键处理主要的功臣是WindowManagerService. mInputManager
@WindowManagerService.java
mInputManager = new InputManager(context, this);
@InputManager.java
public InputManager(Context context, WindowManagerService windowManagerService) { this.mContext = context; this.mWindowManagerService = windowManagerService; this.mCallbacks = new Callbacks(); Looper looper = windowManagerService.mH.getLooper(); Slog.i(TAG, "Initializing input manager"); nativeInit(mContext, mCallbacks, looper.getQueue()); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); }
@com_android_server_InputManager.cpp
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz, jobject contextObj, jobject callbacksObj, jobject messageQueueObj) { if (gNativeInputManager == NULL) { sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper); } else { LOGE("Input manager already initialized."); jniThrowRuntimeException(env, "Input manager already initialized."); }}NativeInputManager::NativeInputManager(jobject contextObj, jobject callbacksObj, const sp<Looper>& looper) : mLooper(looper) { JNIEnv* env = jniEnv(); mContextObj = env->NewGlobalRef(contextObj); mCallbacksObj = env->NewGlobalRef(callbacksObj); { AutoMutex _l(mLock); mLocked.displayWidth = -1; mLocked.displayHeight = -1; mLocked.displayExternalWidth = -1; mLocked.displayExternalHeight = -1; mLocked.displayOrientation = DISPLAY_ORIENTATION_0; mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; mLocked.pointerSpeed = 0; mLocked.pointerGesturesEnabled = true; mLocked.showTouches = false; } sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this);}
@inputManager.cpp
InputManager::InputManager( const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize();}
2) 接下来,重点来了,inputDispatcher负责事件(key、touch)的分发,而事件处理延时的ANR也在它这里。InputReader主要负责读取底层传上来的事件,这里就不介绍了。
@inputDispatcher.cpp
// If the currently focused window is still working on previous events then keep waiting. if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindowHandle)) {#if DEBUG_FOCUS LOGD("Waiting because focused window still processing previous input.");#endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime); goto Unresponsive; }...int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime) {... if (currentTime >= mInputTargetWaitTimeoutTime) { onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, mInputTargetWaitStartTime); // 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; }}void InputDispatcher::onANRLocked( nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime, nsecs_t waitStartTime) { LOGI("Application is not responding: %s. " "%01.1fms since event, %01.1fms since wait started", getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(), (currentTime - eventTime) / 1000000.0, (currentTime - waitStartTime) / 1000000.0); CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doNotifyANRLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; commandEntry->inputWindowHandle = windowHandle;}void InputDispatcher::doNotifyANRLockedInterruptible( CommandEntry* commandEntry) { mLock.unlock(); nsecs_t newTimeout = mPolicy->notifyANR( commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle); mLock.lock(); resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputWindowHandle != NULL ? commandEntry->inputWindowHandle->getInputChannel() : NULL);}
这里的mPlicy是在构造的时候初始化的。追溯前面的inputManager.cpp,com_android_server_InputManager.cpp,很快
发现mPlicy其实就是NativeInputManager。
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : mPolicy(policy),
@ com_android_server_InputManager.cpp
nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, const sp<InputWindowHandle>& inputWindowHandle) {#if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyANR");#endif JNIEnv* env = jniEnv(); jobject inputApplicationHandleObj = getInputApplicationHandleObjLocalRef(env, inputApplicationHandle); jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle); jlong newTimeout = env->CallLongMethod(mCallbacksObj, gCallbacksClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj); if (checkAndClearExceptionFromCallback(env, "notifyANR")) { newTimeout = 0; // abort dispatch } else { assert(newTimeout >= 0); } env->DeleteLocalRef(inputWindowHandleObj); env->DeleteLocalRef(inputApplicationHandleObj); return newTimeout;}
3) 仔细回忆下前面从java层到native层的inputManger和inputDispatcher的构造,不难发现mPlicy的notifyANR()最终回到inputManager.java中对应Callbacks的notifyANR()。(JNI:不仅java层访问本地方法,同样在本地方法中也可以访问java层方法)
@inputManager.java
public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle) { return mWindowManagerService.mInputMonitor.notifyANR( inputApplicationHandle, inputWindowHandle); }
@WindowManagerService.java
final InputMonitor mInputMonitor = new InputMonitor(this);
@InputMonitor.java
public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle) { AppWindowToken appWindowToken = null; if (inputWindowHandle != null) { synchronized (mService.mWindowMap) { WindowState windowState = (WindowState) inputWindowHandle.windowState; if (windowState != null) { Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to " + windowState.mAttrs.getTitle()); appWindowToken = windowState.mAppToken; } } } if (appWindowToken == null && inputApplicationHandle != null) { appWindowToken = inputApplicationHandle.appWindowToken; if (appWindowToken != null) { Slog.i(WindowManagerService.TAG, "Input event dispatching timed out sending to application " + appWindowToken.stringName); } } 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(); if (! abort) { // The activity manager declined to abort dispatching. // Wait a bit longer and timeout again later. return appWindowToken.inputDispatchingTimeoutNanos; } } catch (RemoteException ex) { } } return 0; // abort dispatching }class AppWindowToken extends WindowToken { // Non-null only for application tokens. final IApplicationToken appToken;
其实这里的appToken实质上是一个ActivityRecord的“代理”。(需了解binder)
@ActivityRecord.java
final class ActivityRecord { final ActivityManagerService service; // owner final ActivityStack stack; // owner final IApplicationToken.Stub appToken; // window manager tokenpublic boolean keyDispatchingTimedOut() { ActivityRecord r; ProcessRecord anrApp = null; synchronized(service) { r = getWaitingHistoryRecordLocked(); if (r != null && r.app != null) { if (r.app.debugging) { return false; } if (service.mDidDexOpt) { // Give more time since we were dexopting. service.mDidDexOpt = false; return false; } if (r.app.instrumentationClass == null) { anrApp = r.app; } else { Bundle info = new Bundle(); info.putString("shortMsg", "keyDispatchingTimedOut"); info.putString("longMsg", "Timed out while dispatching key event"); service.finishInstrumentationLocked( r.app, Activity.RESULT_CANCELED, info); } } } if (anrApp != null) { service.appNotResponding(anrApp, r, this, "keyDispatchingTimedOut"); } return true; }
接下去就是ActivityManagerService的appNotResponding,我们平常所看到的ANR对话框正出自这里。
- Android按键超时的ANR原理小结
- android ANR小结
- 解决android应用点击按键过快导致的ANR
- ANR超时时间的定义
- ANR超时时间的定义
- ANR超时时间的定义
- android anr 输出原理
- ANR 出现广播超时的分析
- Android 按键事件小结
- android 按键事件小结
- Android ANR源码原理分析
- android应用程序中对于ANR主线程超时不响应导致强制关闭的解决方案
- ANR小结
- android 的ANR问题
- Android的ANR分析
- Android的ANR详解
- Android的ANR处理
- android-ANR的解析
- linux bash shell之变量替换::=句法、=句法、:-句法、-句法、=?句法、?句法、:+句法、+句法
- 模仿360android手机卫士 仿w8界面,右边滑出按钮条
- 字典树 C++
- 我指间的刺青是对你的誓言
- 人事档案管理
- Android按键超时的ANR原理小结
- CSDN开博前的一些话
- ActionContext和ServletActionContext小结
- 断想
- android注册页面
- sed 非常实用的几种用法!
- 为了忘却的纪念,我的天龙游戏生涯
- 彻底搞定C指针-函数名与函数指针
- 第二章:Samba服务全攻略