Android和焦点相关的ANR

Android和焦点相关的ANR

在android设备上和焦点相关的anr我认为有两种

  1. no focus anr,这是一种常见的anr,简单来讲是由 分发key事件时找不到对应屏幕上的焦点窗口引起.
  2. FocusEvent 处理超时anr,这个后面后详细讲讲.

一. no focus anr

1. 埋雷阶段

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    .....
    // Identify targets.
    std::vector<InputTarget> inputTargets;
    // 寻找该事件应该被派发的窗口,一般对应屏幕上的焦点窗口( no focus anr的雷就在该阶段埋下)
    InputEventInjectionResult injectionResult =
            findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
    if (injectionResult == InputEventInjectionResult::PENDING) {
        return false;
    }

    setInjectionResult(*entry, injectionResult);
    if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
        return true;
    }
    .....
    // Dispatch the key. 正式开始分发该key事件
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

1.获取该屏幕上对应的焦点窗口,获取该屏幕上对应的焦点应用(对应ActivityRecord)
2.如果焦点窗口为null且焦点应用也为null,则丢弃该事件,同时返回InputEventInjectionResult::FAILED
3.如果焦点窗口为null,但焦点应用不为null,就开始做no focus anr的检查了,如果mNoFocusedWindowTimeoutTime没有值,则说明之前没有开始无焦点的anr的触发计时,如果mNoFocusedWindowTimeoutTime没有值,则说明之前没有开始无焦点的anr的触发计时,获取焦点应用配置的时间分发超时的时长,设置触发anr的时间 = 当前时间 + 焦点应用配置的时间分发超时时间,设置inputDispatchar自己唤起的时间为触发该次无焦点anr的时间,埋下 no focus anr的雷
4. 如果一切正常就重置无焦点anr的计时

frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
        nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
        nsecs_t* nextWakeupTime) {
    std::string reason;
    // 获取该key事件对应的屏幕id
    int32_t displayId = getTargetDisplayId(entry);
    // 获取该屏幕上对应的焦点窗口
    sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
    // 获取该屏幕上对应的焦点应用(对应ActivityRecord)
    std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
    // 如果焦点窗口为null且焦点应用也为null,则丢弃该事件,同时返回InputEventInjectionResult::FAILED
    if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
        ALOGI("Dropping %s event because there is no focused window or focused application in "
              "display %" PRId32 ".",
              NamedEnum::string(entry.type).c_str(), displayId);
        return InputEventInjectionResult::FAILED;
    }
    // 如果焦点窗口为null,且焦点应用不为null
    if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
        // 如果mNoFocusedWindowTimeoutTime没有值,则说明之前没有开始无焦点的anr的触发计时
        if (!mNoFocusedWindowTimeoutTime.has_value()) {
            // We just discovered that there's no focused window. Start the ANR timer
            // 获取焦点应用配置的时间分发超时的时长
            std::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(
                    DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            // 设置触发anr的时间 = 当前时间 + 焦点应用配置的时间分发超时时间
            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));
            // 设置inputDispatchar自己唤起的时间为触发该次无焦点anr的时间
            *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
            // 返回InputEventInjectionResult::PENDING
            return InputEventInjectionResult::PENDING;
        } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
            // Already raised ANR. Drop the event
            ALOGE("Dropping %s event because there is no focused window",
                  NamedEnum::string(entry.type).c_str());
            return InputEventInjectionResult::FAILED;
        } else {
            // Still waiting for the focused window
            return InputEventInjectionResult::PENDING;
        }
    }
    // 一切ok,重置无焦点anr的计时
    // we have a valid, non-null focused window
    resetNoFocusedWindowTimeoutLocked();

    // Check permissions.
    if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
        return InputEventInjectionResult::PERMISSION_DENIED;
    }

    if (focusedWindowHandle->getInfo()->paused) {
        ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
        return InputEventInjectionResult::PENDING;
    }

    if (entry.type == EventEntry::Type::KEY) {
        if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
            *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
            return InputEventInjectionResult::PENDING;
        }
    }

    // Success!  Output targets.
    addWindowTargetLocked(focusedWindowHandle,
                          InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
                          BitSet32(0), inputTargets);

    // Done.
    // 一切ok ,返回InputEventInjectionResult::SUCCEEDED;
    return InputEventInjectionResult::SUCCEEDED;
}

2. no focus anr的触发阶段

如上,当分发keyevent时,当focusedApplicationHandle不为null,且focusedWindowHandle为null时,就设置了一个无焦点的anr触发时间。
接下来就分析该类型的anr如何真正触发的:


在事件的分发函数dispatchOnce中会对anr进行处理
```cpp
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

void InputDispatcher::dispatchOnce() {
    // 先把下次的唤醒事件是设置为无穷大
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    // 在当前版本上 一个时间处理时间 超时引起的anr 不用等下一个时间来触发
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();

        // 分发事件
        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;
        }
        
        // 处理anr 不仅仅处理了anr, 还会 算出 最近一次anr唤醒的超时时间,时间到了  就唤醒.
        // 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 events
        if (nextWakeupTime == LONG_LONG_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);
}

如下:
1.mNoFocusedWindowTimeoutTime 已经有值,说明之前有埋下 no focus anr的雷,那就做相关的检查了
2.如果当前的时间大于之前设置的anr触发的时间, 则触发无焦点类型的anr,随后上报无焦点anr,重置相关值
3.如果没有超过,则 nextAnrCheck = *mNoFocusedWindowTimeoutTime,这一轮里面不触发无焦点anr,nextAnrCheck为下一次唤醒时间

/**
 * Check if any of the connections' wait queues have events that are too old.
 * If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
 * Return the time at which we should wake up next.
 */
nsecs_t InputDispatcher::processAnrsLocked() {
    const nsecs_t currentTime = now();
    nsecs_t nextAnrCheck = LONG_LONG_MAX;
    // Check if we are waiting for a focused window to appear. Raise ANR if waited too long
    // 上文中 mNoFocusedWindowTimeoutTime 已经有值
    if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
        // 如果当前的时间大于之前设置的anr触发的时间, 则触发无焦点类型的anr
        if (currentTime >= *mNoFocusedWindowTimeoutTime) {
            // 上报无焦点anr
            processNoFocusedWindowAnrLocked();
            // 重置相关值
            mAwaitedFocusedApplication.reset();
            mNoFocusedWindowTimeoutTime = std::nullopt;
            return LONG_LONG_MIN;
        } else {
            // 如果没有超过,则 nextAnrCheck = *mNoFocusedWindowTimeoutTime,这一轮里面不触发无焦点anr
            // Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.
            nextAnrCheck = *mNoFocusedWindowTimeoutTime;
        }
    }
    
    // 下来讲会处理分发给窗口的事件没有及时处理的anr
    // Check if any connection ANRs are due
    nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
    if (currentTime < nextAnrCheck) { // most likely scenario
        return nextAnrCheck;          // everything is normal. Let's check again at nextAnrCheck
    }

    // If we reached here, we have an unresponsive connection.
    sp<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 connection
    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
    onAnrLocked(connection);
    return LONG_LONG_MIN;
}

上报无焦点anr:

frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

/**
 * Raise ANR if there is no focused window.
 * Before the ANR is raised, do a final state check:
 * 1. The currently focused application must be the same one we are waiting for.
 * 2. Ensure we still don't have a focused window.
 */
void InputDispatcher::processNoFocusedWindowAnrLocked() {
    // Check if the application that we are waiting for is still focused.
    std::shared_ptr<InputApplicationHandle> focusedApplication =
            getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
    // 如果期间的焦点app发生了变化,则放弃上报此次的无焦点anr
    if (focusedApplication == nullptr ||
        focusedApplication->getApplicationToken() !=
                mAwaitedFocusedApplication->getApplicationToken()) {
        // Unexpected because we should have reset the ANR timer when focused application changed
        ALOGE("Waited for a focused window, but focused application has already changed to %s",
              focusedApplication->getName().c_str());
        return; // The focused application has changed.
    }
    // 检查现在的焦点窗口是否存在,存在则放弃此次无焦点anr的上报
    const sp<InputWindowHandle>& focusedWindowHandle =
            getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
    if (focusedWindowHandle != nullptr) {
        return; // We now have a focused window. No need for ANR.
    } 
    // 触发anr
    onAnrLocked(mAwaitedFocusedApplication);
}

随后,该anr将抛给了上层来处理。

二、FocusEvent 处理超时anr

除此之外,还有一种看起来和焦点窗口相关的的anr,常见的打印如下:
Subject: Input dispatching timed out (fd239fd lenovo.vcl.crashdemo/lenovo.vcl.crashdemo.MainActivity (server) is not responding. Waited 5001ms for FocusEvent(hasFocus=false))
最重要的打印是····for FocusEvent(hasFocus=false)),该log的打印虽然包含Focus字段,但实际上它不是无焦点anr,上一篇文章分析节点切换流程的时候,提到过当inputdispatcaher在切换焦点是分别会给焦点进入的窗口和焦点切出的窗口发送一个Focusevent。该事件想普通的时间一样,处理完需要再和inputdispatcaher,辨明该事件已经处理完毕,否则,5s过后,inputdispatcaher分发时间再次唤醒时,触发一个分发超时的anr。

再回到贴出的log,很明显这是一个焦点撤出的时间处理超时的anr

这种anr一般发生在应用的关闭阶段,处理这种anr重点关注关闭的activity的onPaused 或者 onstopped函数中有没有用耗时的操作,同时结合anr的堆栈打印进行分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值