Android14 InputManager-ANR原理

目標窗口查找時,作為派發目標的窗口必須已經準備好接收新的輸入事件,否則判定窗口處于未響應狀態,終止事件的派發過程,并在一段時間后再試。倘若5s后窗口仍然未準備好接收輸入事件,將導致ANR。直接引發ANR的原因有很多,

例如Activity生命周期函數調用超時,

服務啟動超時

以及最常見的輸入事件處理超時等。

  1. Service ANR:前臺20s,后臺200s;startForeground超時10s
  2. Broadcast ANR:前臺10s,后臺60s
  3. Input ANR:按鍵或觸摸事件在5s內無響應
  4. ContentProvider ANR:10s,少見

下面從輸入事件超時的角度討論ANR的產生原因與過程

inputANR 分為兩種

無響應anr: 應用連接正常但是未相應事件,并且發生了超時

無焦點窗口anr: 當前有焦點應用,但是無焦點窗口,并且超時

void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LLONG_MAX;{ // acquire lockstd::scoped_lock _l(mLock);mDispatcherIsAlive.notify_all();// 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 (runCommandsLockedInterruptable()) {nextWakeupTime = LLONG_MIN;}// If we are still waiting for ack on some events,// we might have to wake up earlier to check if an app is anr'ing.const nsecs_t nextAnrCheck = processAnrsLocked();nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);// We are about to enter an infinitely long sleep, because we have no commands or// pending or queued eventsif (nextWakeupTime == LLONG_MAX) {mDispatcherEnteredIdle.notify_all();}} // 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);
}
nsecs_t InputDispatcher::processAnrsLocked() {const nsecs_t currentTime = now();nsecs_t nextAnrCheck = LLONG_MAX;// Check if we are waiting for a focused window to appear. Raise ANR if waited too longif (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {if (currentTime >= *mNoFocusedWindowTimeoutTime) {processNoFocusedWindowAnrLocked();// 無焦點anrmAwaitedFocusedApplication.reset();mNoFocusedWindowTimeoutTime = std::nullopt;return LLONG_MIN;} else {// Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.nextAnrCheck = *mNoFocusedWindowTimeoutTime;}}// Check if any connection ANRs are duenextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());if (currentTime < nextAnrCheck) { // most likely scenarioreturn nextAnrCheck;          // everything is normal. Let's check again at nextAnrCheck}// If we reached here, we have an unresponsive connection.std::shared_ptr<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());if (connection == nullptr) {ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());return nextAnrCheck;}connection->responsive = false;// Stop waking up for this unresponsive connectionmAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());onAnrLocked(connection);// 無響應anrreturn LLONG_MIN;
}

mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 的設置是在 InputDispatcher::findFocusedWindowTargetLocked

函數中:

sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,InputEventInjectionResult& outInjectionResult) {std::string reason;outInjectionResult = InputEventInjectionResult::FAILED; // Default resultint32_t displayId = getTargetDisplayId(entry);sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);// If there is no currently focused window and no focused application// then drop the event.if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {ALOGI("Dropping %s event because there is no focused window or focused application in ""display %" PRId32 ".",ftl::enum_string(entry.type).c_str(), displayId);return nullptr;}// Drop key events if requested by input featureif (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {return nullptr;}// Compatibility behavior: raise ANR if there is a focused application, but no focused window.// Only start counting when we have a focused event to dispatch. The ANR is canceled if we// start interacting with another application via touch (app switch). This code can be removed// if the "no focused window ANR" is moved to the policy. Input doesn't know whether// an app is expected to have a focused window.if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {if (!mNoFocusedWindowTimeoutTime.has_value()) {// We just discovered that there's no focused window. Start the ANR timerstd::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);mNoFocusedWindowTimeoutTime = currentTime + timeout.count();mAwaitedFocusedApplication = focusedApplicationHandle;mAwaitedApplicationDisplayId = displayId;ALOGW("Waiting because no window has focus but %s may eventually add a ""window when it finishes starting up. Will wait for %" PRId64 "ms",mAwaitedFocusedApplication->getName().c_str(), millis(timeout));*nextWakeupTime = *mNoFocusedWindowTimeoutTime;outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;} else if (currentTime > *mNoFocusedWindowTimeoutTime) {// Already raised ANR. Drop the eventALOGE("Dropping %s event because there is no focused window",ftl::enum_string(entry.type).c_str());return nullptr;} else {// Still waiting for the focused windowoutInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}}// we have a valid, non-null focused windowresetNoFocusedWindowTimeoutLocked();// Verify targeted injection.if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {ALOGW("Dropping injected event: %s", (*err).c_str());outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;return nullptr;}if (focusedWindowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}// If the event is a key event, then we must wait for all previous events to// complete before delivering it because previous events may have the// side-effect of transferring focus to a different window and we want to// ensure that the following keys are sent to the new window.//// Suppose the user touches a button in a window then immediately presses "A".// If the button causes a pop-up window to appear then we want to ensure that// the "A" key is delivered to the new pop-up window.  This is because users// often anticipate pending UI changes when typing on a keyboard.// To obtain this behavior, we must serialize key events with respect to all// prior input events.if (entry.type == EventEntry::Type::KEY) {if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}}outInjectionResult = InputEventInjectionResult::SUCCEEDED;return focusedWindowHandle;
}

可以看出引發 無焦點ANR 的兩個條件:
1.mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr 并且 currentTime >= *mNoFocusedWindowTimeoutTime

mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 的設置是在 InputDispatcher::findFocusedWindowTargetsLocked 函數中:

當有焦點應用但沒有焦點窗口,設置?mNoFocusedWindowTimeoutTime 的值,開啟 ANR 倒計時。對于當前事件返回 PENDING 。
接著在當前的派發循環中執行 processAnrsLocked 函數判斷是否引發 ANR。

重置?mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 是在 InputDispatcher::resetNoFocusedWindowTimeoutLocked 中。

執行?resetNoFocusedWindowTimeoutLocked 的幾個時機:
(1)InputDispatcher::findFocusedWindowTargetLocked中,當等待有焦點窗口后;

(2)InputDispatcher::setFocusedApplication 中,切換焦點應用后;

(3)InputDispatcher::setInputDispatchMode 中;

(4) InputDispatcher::resetAndDropEverythingLocked

無焦點anr

void InputDispatcher::processNoFocusedWindowAnrLocked() {// Check if the application that we are waiting for is still focused.std::shared_ptr<InputApplicationHandle> focusedApplication =getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);if (focusedApplication == nullptr ||focusedApplication->getApplicationToken() !=mAwaitedFocusedApplication->getApplicationToken()) {// Unexpected because we should have reset the ANR timer when focused application changedALOGE("Waited for a focused window, but focused application has already changed to %s",focusedApplication->getName().c_str());return; // The focused application has changed.}const sp<WindowInfoHandle>& focusedWindowHandle =getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);if (focusedWindowHandle != nullptr) {return; // We now have a focused window. No need for ANR.}onAnrLocked(mAwaitedFocusedApplication);
}
void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {std::string reason =StringPrintf("%s does not have a focused window", application->getName().c_str());updateLastAnrStateLocked(*application, reason);auto command = [this, application = std::move(application)]() REQUIRES(mLock) {scoped_unlock unlock(mLock);mPolicy.notifyNoFocusedWindowAnr(application);};postCommandLocked(std::move(command));
}

?無響應anr


void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) {if (connection == nullptr) {LOG_ALWAYS_FATAL("Caller must check for nullness");}// Since we are allowing the policy to extend the timeout, maybe the waitQueue// is already healthy again. Don't raise ANR in this situationif (connection->waitQueue.empty()) {ALOGI("Not raising ANR because the connection %s has recovered",connection->inputChannel->getName().c_str());return;}/*** The "oldestEntry" is the entry that was first sent to the application. That entry, however,* may not be the one that caused the timeout to occur. One possibility is that window timeout* has changed. This could cause newer entries to time out before the already dispatched* entries. In that situation, the newest entries caused ANR. But in all likelihood, the app* processes the events linearly. So providing information about the oldest entry seems to be* most useful.*/DispatchEntry* oldestEntry = *connection->waitQueue.begin();const nsecs_t currentWait = now() - oldestEntry->deliveryTime;std::string reason =android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",connection->inputChannel->getName().c_str(),ns2ms(currentWait),oldestEntry->eventEntry->getDescription().c_str());sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken();updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);//處理無響應anr
processConnectionUnresponsiveLocked(*connection, std::move(reason));
// cnnel所有事件// Stop waking up for events on this connection, it is already unresponsivecancelEventsForAnrLocked(connection);
}
void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,std::string reason) {const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();std::optional<int32_t> pid;if (connection.monitor) {ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),reason.c_str());pid = findMonitorPidByTokenLocked(connectionToken);} else {// The connection is a windowALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),reason.c_str());const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);if (handle != nullptr) {pid = handle->getInfo()->ownerPid;}}
//發送窗口無響應命令sendWindowUnresponsiveCommandLocked(connectionToken, pid, std::move(reason));
}
//當這個command被執行時,調用的為doNotifyWindowUnresponsiveLockedInterruptible
void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {mLock.unlock();mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);  //調用到jni層,向java層傳遞mLock.lock();
}

ANR 的通知

// Native callback
@SuppressWarnings("unused")
private void notifyWindowUnresponsive(IBinder token, int pid, boolean isPidValid,String reason) {mWindowManagerCallbacks.notifyWindowUnresponsive(token,isPidValid ? OptionalInt.of(pid) : OptionalInt.empty(), reason);
}

調用java層發送彈窗命令, 就不細說了

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/696465.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696465.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696465.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

操作系統學習記錄

系統篇 內核 應用和底層硬件&#xff08;CPU、內存、硬盤等&#xff09;的連接橋梁。 用戶態和內核態 CPU和進程可以在兩種態下運行。 內核態可以直接訪問所有硬件資源&#xff0c;用戶態需要通過“系統調用”陷入到內核態才能否則只能訪問用戶空間地址的內存&#xff08;虛…

P8630 [藍橋杯 2015 國 B] 密文搜索

P8630 [藍橋杯 2015 國 B] 密文搜索 - 洛谷 | 計算機科學教育新生態 (luogu.com.cn)https://www.luogu.com.cn/problem/P8630 題目分析 基本上是hash的板子&#xff0c;但實際上對于密碼串&#xff0c;只要判斷主串中任意連續的八個位置是否存在密碼串即可&#xff1b;那么我們…

PHP學習筆記1——html標簽以及頭部元素頁面布局

html是一種超文本標識符號&#xff0c;用來在網頁中指定顯示頁面格式顯示 基本格式 <!doctype html> <html><head><title></title> </head><body> </body></html> 包含聲明&#xff0c;框架html&#xff0c;頭部head&a…

怎么把公眾號文章鏈接做成二維碼?掃碼查看公眾號推文的方法

公眾號是現在給用戶分享內容的一種方式&#xff0c;通過輸出優質的公眾號文章內容來為關注者提供信息和內容。當我們發布公眾號文章后&#xff0c;有些情況下會需要將公眾號內容生成二維碼之后&#xff0c;印刷到傳單、展板上來顯示&#xff0c;那么如何將公眾號的文章鏈接轉二…

Commonjs 和 Es Module詳解

一 前言 今天我們來深度分析一下 Commonjs 和 Es Module&#xff0c;希望通過本文的學習&#xff0c;能夠讓大家徹底明白 Commonjs 和 Es Module 原理&#xff0c;能夠一次性搞定面試中遇到的大部分有關 Commonjs 和 Es Module 的問題。 帶上疑問開始今天的分析&#xff1a; …

數字化轉型導師堅鵬:城市數字化轉型頂層規劃方法

城市數字化轉型頂層規劃方法 課程背景&#xff1a; 很多城市存在以下問題&#xff1a; 不知道如何系統地開展數字化轉型工作&#xff1f; 不清楚如何科學地制定數字化轉型戰略&#xff1f; 不知道如何高效地實施數字化轉型戰略&#xff1f; 課程特色&#xff1a; 有…

基于SpringBoot的在線拍賣系統設計與實現(源碼+調試+LW+PPT)

項目描述 臨近學期結束&#xff0c;還是畢業設計&#xff0c;你還在做java程序網絡編程&#xff0c;期末作業&#xff0c;老師的作業要求覺得大了嗎?不知道畢業設計該怎么辦?網頁功能的數量是否太多?沒有合適的類型或系統?等等。今天給大家介紹一篇基于SpringBoot的在線拍…

C++編程知識

&#xff08;1&#xff09;把數組中的所有元素賦值為一個值 memset(arr,0x3f,sizeof(arr))//最大值 memset(arr,0,sizeof(distance));//賦值為0&#xff08;2&#xff09;找最大最小值的下標 int zuixiao-1//設置dist-1只是因為方便選取第一個數作為比較對象而已 for(i0;i<…

VSCode將某個字符替換為換行符并換行顯示

不想每次去查了&#xff0c;我自己寫博客記錄一下~~~ 我的需求是一個一行的數據&#xff0c;用逗號分開&#xff0c;我希望豎著看有規律點&#xff0c;類似這樣 快捷鍵其實想 optioncommandf &#xff0c;但是我每次都記不住&#xff0c;大家可以直接在編輯欄找到replace的地方…

vite 快速搭建 Vue3.0項目

一、初始化項目 npm create vite-app <project name>二、進入項目目錄 cd ……三、安裝依賴 npm install四、啟動項目 npm run dev五、配置項目 安裝 typescript npm add typescript -D初始化 tsconfig.json //執行命令 初始化 tsconfig.json npx tsc --init …

Unity Meta XR SDK 快捷配置開發工具【Building Block/Quick Action/OVRCameraRigInteraction】

文章目錄 &#x1f4d5;教程說明&#x1f4d5;Building Block&#x1f4d5;Quick Action&#x1f4d5;OVRCameraRigInteraction 此教程相關的詳細教案&#xff0c;文檔&#xff0c;思維導圖和工程文件會放入 Spatial XR 社區。這是一個高質量 XR 社區&#xff0c;博主目前在內…

【Swift】NSPopUpButton用法和示例

1.簡介 NSPopUpButton 是 macOS 開發中常用的控件之一&#xff0c;它提供了一個彈出菜單&#xff0c;用戶可以從中選擇一個選項。NSPopUpButton 可以包含多個 NSMenuItem 對象&#xff0c;每個 NSMenuItem 表示一個選項。 2.常用方法 下面是一些 NSPopUpButton 的常用功能和…

進程線程的通信-day5

1、將互斥機制的代碼實現重新敲一遍。 #include<myhead.h>//臨界資源 int num520;//1、創建一個互斥鎖變量 pthread_mutex_t mutex;void *task1(void *arg); void *task2(void *arg); int main(int argc, const char *argv[]) {//2、初始化互斥鎖pthread_mutex_init(&am…

Shiro-14-subject 主體

理解Apache Shiro中的主題 毫無疑問&#xff0c;Apache Shiro中最重要的概念是主題。 “主題”只是一個安全術語&#xff0c;它指的是應用程序用戶特定于安全的“視圖”。Shiro主題實例代表了單個應用程序用戶的安全狀態和操作。 這些操作包括: 身份驗證(登錄) 授權(訪問控…

了解JSON的作用及其方法

什么是json JSON&#xff08;JavaScript Object Notation&#xff09;是一種輕量級的數據交換格式采用完全獨立編程語言的文本格式存儲和表示數據&#xff08;就是字符串&#xff09;。它基于JavaScript語法&#xff0c;但可以被多種編程語言使用和解析。JSON以鍵值對的形式存…

高光譜圖像降噪方法(2D Wavelet, 3D Wavelet, FORPDN, HyRes等方法)

近年來&#xff0c;隨著遙感應用的不斷深入&#xff0c;高光譜圖像研究已經成為遙感領域發展最迅速的技術之一。與其他傳統成像技術相比&#xff0c;高光譜圖像具有更多優勢&#xff1a;更豐富的信息量、納米級的光譜分辨率以及范圍更廣且連續的光譜。因此&#xff0c;在農業、…

C#用反射實現兩個類的對象之間相同屬性的值的復制

目錄 1.適用于創建實體的時候從一個實體作為數據源賦值 2.適用于沒有創建實體&#xff0c;兩個實體之間數據的轉換 1.適用于創建實體的時候從一個實體作為數據源賦值 /// <summary> /// 反射實現兩個類的對象之間相同屬性的值的復制 /// 適用于初始化新實體 /// </…

選擇VR全景行業,需要了解哪些內容?

近年來&#xff0c;隨著虛擬現實、增強現實等技術的持續發展&#xff0c;VR全景消費市場得以穩步擴張。其次&#xff0c;元宇宙行業的高速發展&#xff0c;也在進一步拉動VR全景技術的持續進步&#xff0c;帶動VR產業的高質量發展。作為一種戰略性的新興產業&#xff0c;國家和…

OJ_學生信息系統

題干 輸入樣例&#xff1a; 5 zhaoyi 70 80 90 240 qianer 65 32 77 174 sunsan 100 55 68 223 lisi 86 77 90 253 wangwu 100 59 66 225 輸出樣例&#xff1a; *[qianer] 65 32 77 *[sunsan] 100 55 68 *[wangwu] 100 59 66 lisi 86 77 90 zhaoyi 70 80 90 wangwu 100 59 6…

k8s-權限管理

1. 身份認證 我們在目前的k8s集群環境里面&#xff0c;只能在master節點上執行kubectl的一些命令&#xff0c;在其他節點上執行就會報錯 # 看一下是不是 [rootnode1 ~]# kubectl get nodes E0220 12:50:15.695133 6091 memcache.go:238] couldnt get current server API gro…