Android Framework-Input-8 ANR相关

开局叠甲,可能理解有误,请大佬们斧正。

其实Input 相关的ANR 一边表现为咱们常说的卡住,然后等待一段时间,熟悉的ANR弹窗就出来了。

我们看下Input相关ANR的原因。

C++ 复制代码
//native/services/inputflinger/dispatcher/InputDispatcher.cpp

void InputDispatcher::dispatchOnce() {
  //省略
  //如果没有commands 则进行事件分发
  if (!haveCommandsLocked()) {
 dispatchOnceInnerLocked(&nextWakeupTime);
        }

   if (runCommandsLockedInterruptible()) { // 执行commands
            nextWakeupTime = LONG_LONG_MIN; // 立即进行下一轮的派发
        }
     const nsecs_t nextAnrCheck = processAnrsLocked();
  //获取最小的唤醒时间 
    nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
   if (nextWakeupTime == LONG_LONG_MAX) {
            mDispatcherEnteredIdle.notify_all();
        }
   //省略
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

processAnrsLocked方法里, 如果有在等待出现焦点窗口的操作,判断是否有超时,如果出现超时,则会调用 onAnrLocked, 通知出现ANR。 对所有的connection检查是否有ANR出现,出现则调用onAnrLocked 通知出现ANR。 简单来说,一种是连着,WQ里炸弹没移除掉。第二种,找不到要给焦点的对象(Window)。

c++ 复制代码
nsecs_t InputDispatcher::processAnrsLocked() {
    const nsecs_t currentTime = now();
    nsecs_t nextAnrCheck = LONG_LONG_MAX;
  
  //  // 等待焦点应用的focused 窗口出现 mNoFocusedWindowTimeoutTime 会在 findFocusedWindowTargetsLocked 里赋值 也就是一个超时时间
    if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
        if (currentTime >= *mNoFocusedWindowTimeoutTime) {//场景1: 触发noFocusedWindow的anr
            processNoFocusedWindowAnrLocked();// 1.1
            mAwaitedFocusedApplication.reset();//重置
            mNoFocusedWindowTimeoutTime = std::nullopt;
            return LONG_LONG_MIN;//直接返回最小值 下一个判断
        } else {
         
            nextAnrCheck = *mNoFocusedWindowTimeoutTime;//下次的时间
        }
    }

     // 检查是否有任何连接 ANR 到期,mAnrTracker 中保存所有已分发事件(未被确认消费的事件)的超时时间
    nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
    if (currentTime < nextAnrCheck) { //一切正常,在 nextAnrCheck 再检查一次
        return nextAnrCheck;          
    }

   
   // 如果我们到达这里,则连接无响应。
    sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
    if (connection == nullptr) {
        return nextAnrCheck;
    }

    connection->responsive = false;//没响应了
   // 停止为此无响应的连接唤醒
    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());// // 移除此token对应的记录
 // 场景2: 触发ANR 1.2
    onAnrLocked(connection);// 通知ANR发生, 注意此处的参数是 Connection
    return LONG_LONG_MIN;//立即进行下一轮循环
}
  • onAnrLocked(app)
c 复制代码
//找不到focus的window,但是找到当前获取input事件的应用。
void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {
    std::string reason =
            StringPrintf("%s does not have a focused window", application->getName().c_str());
  //收集anr的window、reason信息
    updateLastAnrStateLocked(*application, reason);

    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
            &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible);
    commandEntry->inputApplicationHandle = std::move(application);
  //将anr的命令添加到 mCommandQueue 中 回调 doNotifyNoFocusedWindowAnrLockedInterruptible
    postCommandLocked(std::move(commandEntry));
}

//会调用 doNotifyNoFocusedWindowAnrLockedInterruptible


void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
    mLock.unlock();

    mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle);

    mLock.lock();
}
  • onAnrLocked(connection)
c 复制代码
void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
 
  //如果消息等待队列是null 那就不用检查了 没等待消息怎么anr
    if (connection->waitQueue.empty()) {
       
        return;
    }

    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();
  // 记录ANR信息     
    updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);

    processConnectionUnresponsiveLocked(*connection, std::move(reason));
    cancelEventsForAnrLocked(connection);
}


void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,
                                                          std::string reason) {
    const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();
    if (connection.monitor) {
      
        std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);
        if (!pid.has_value()) {
           
            return;
        }
        sendMonitorUnresponsiveCommandLocked(pid.value(), std::move(reason));
        return;
    }
   
    
    sendWindowUnresponsiveCommandLocked(connectionToken, std::move(reason));
}



void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,
                                                          std::string reason) {
    std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(
            &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);
    windowUnresponsiveCommand->connectionToken = std::move(connectionToken);
    windowUnresponsiveCommand->reason = std::move(reason);
    postCommandLocked(std::move(windowUnresponsiveCommand));
}

//会调用doNotifyWindowUnresponsiveLockedInterruptible

void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
    mLock.unlock();

    mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);

    mLock.lock();
}
  • mPolicy的由来
js 复制代码
    sp<InputDispatcherPolicyInterface> mPolicy;

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
      : mPolicy(policy),

而我们在InputManager.cpp的初始化过程中,

可以看出 其实传入的policy 其实就是 this,也就说其实会回调到com_android_server_input_InputManagerService

c 复制代码
//base/services/core/jni/com_android_server_input_InputManagerService.cpp


NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiLightsOut = false;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
        mLocked.pointerCapture = false;
        mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
    }
    mInteractive = true;
   
    InputManager* im = new InputManager(this, this);
    mInputManager = im;
    defaultServiceManager()->addService(String16("inputflinger"), im);
}


InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    mReader = createInputReader(readerPolicy, mClassifier);
}

sp<InputDispatcherInterface> createInputDispatcher(
        const sp<InputDispatcherPolicyInterface>& policy) {
    return new android::inputdispatcher::InputDispatcher(policy);
}

我们看下JNI对应的方法

c 复制代码
void NativeInputManager::notifyWindowUnresponsive(const sp<IBinder>& token,
                                                  const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
    ALOGD("notifyWindowUnresponsive");
#endif
    ATRACE_CALL();

    JNIEnv* env = jniEnv();
    ScopedLocalFrame localFrame(env);

    jobject tokenObj = javaObjectForIBinder(env, token);
    ScopedLocalRef<jstring> reasonObj(env, env->NewStringUTF(reason.c_str()));

    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyWindowUnresponsive, tokenObj,
                        reasonObj.get());
    checkAndClearExceptionFromCallback(env, "notifyWindowUnresponsive");
}

void NativeInputManager::notifyNoFocusedWindowAnr(
        const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {

    ATRACE_CALL();

    JNIEnv* env = jniEnv();
    ScopedLocalFrame localFrame(env);

    jobject inputApplicationHandleObj =
            getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);

    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyNoFocusedWindowAnr,
                        inputApplicationHandleObj);
    checkAndClearExceptionFromCallback(env, "notifyNoFocusedWindowAnr");
}

会反向调用到InputManagerService.java

java 复制代码
//base/services/core/java/com/android/server/input/InputManagerService.java
private void notifyNoFocusedWindowAnr(InputApplicationHandle inputApplicationHandle) {
        mWindowManagerCallbacks.notifyNoFocusedWindowAnr(inputApplicationHandle);
    }
    
     // Native callback
    private void notifyWindowUnresponsive(IBinder token, String reason) {
        mWindowManagerCallbacks.notifyWindowUnresponsive(token, reason);
    }

然后一路回调下去 ,最后调用到

java 复制代码
com.android.server.am.AppErrors

 void handleShowAnrUi(Message msg) {
        List<VersionedPackage> packageList = null;
        boolean doKill = false;
        AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
        final ProcessRecord proc = data.proc;
        if (proc == null) {
            Slog.e(TAG, "handleShowAnrUi: proc is null");
            return;
        }
        synchronized (mProcLock) {
            final ProcessErrorStateRecord errState = proc.mErrorState;
            if (!proc.isPersistent()) {
                packageList = proc.getPackageListWithVersionCode();
            }
            if (errState.getDialogController().hasAnrDialogs()) {
                Slog.e(TAG, "App already has anr dialog: " + proc);
                MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                        AppNotRespondingDialog.ALREADY_SHOWING);
                return;
            }

            boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                    Settings.Secure.ANR_SHOW_BACKGROUND, 0,
                    mService.mUserController.getCurrentUserId()) != 0;
            if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
                AnrController anrController = errState.getDialogController().getAnrController();
                if (anrController == null) {
                    errState.getDialogController().showAnrDialogs(data);
                } else {
                    String packageName = proc.info.packageName;
                    int uid = proc.info.uid;
                    boolean showDialog = anrController.onAnrDelayCompleted(packageName, uid);

                    if (showDialog) {
                        Slog.d(TAG, "ANR delay completed. Showing ANR dialog for package: "
                                + packageName);
                        errState.getDialogController().showAnrDialogs(data);
                    } else {
                        Slog.d(TAG, "ANR delay completed. Cancelling ANR dialog for package: "
                                + packageName);
                        errState.setNotResponding(false);
                        errState.setNotRespondingReport(null);
                        errState.getDialogController().clearAnrDialogs();
                    }
                }
            } else {
                MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                        AppNotRespondingDialog.CANT_SHOW);
                // Just kill the app if there is no dialog to be shown.
                doKill = true;
            }
        }
        if (doKill) {
            mService.killAppAtUsersRequest(proc);
        }
        // Notify PackageWatchdog without the lock held
        if (packageList != null) {
            mPackageWatchdog.onPackageFailure(packageList,
                    PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
        }
    }
相关推荐
法欧特斯卡雷特2 小时前
Kotlin 2.2.20 现已发布!下个版本的特性抢先看!
android·前端·后端
人生游戏牛马NPC1号2 小时前
学习 Android (二十一) 学习 OpenCV (六)
android·opencv·学习
用户2018792831672 小时前
Native 层 Handler 机制与 Java 层共用 MessageQueue 的设计逻辑
android
lichong9513 小时前
【混合开发】vue+Android、iPhone、鸿蒙、win、macOS、Linux之android 把assert里的dist.zip 包解压到sd卡里
android·vue.js·iphone
·云扬·3 小时前
MySQL 日志全解析:Binlog/Redo/Undo 等 5 类关键日志的配置、作用与最佳实践
android·mysql·adb
Kapaseker3 小时前
如果你的 View 不支持 Compose 怎么办
android·kotlin
珹洺4 小时前
Java-Spring入门指南(五)Spring自动装配
android·java·spring
sylvia_08154 小时前
react native 初次使用Android Studio 打包
android·react native·android studio
前行的小黑炭4 小时前
Android:在项目当中可能会遇到的ANR,应该如何解决?
android·java·kotlin