InputManagerService分发输入事件给应用程序(下)

本文详细剖析了Android系统中InputManagerService如何分发输入事件到应用程序的过程,从InputDispatcherThread的threadLoop()方法开始,探讨了事件的获取、处理、分发到应用程序窗口的整个流程,涉及InputDispatcher、InputChannel、Looper和NativeInputEventReceiver等多个关键组件的工作原理,揭示了Android系统中输入事件处理的底层机制。
摘要由CSDN通过智能技术生成

InputManagerService分发输入事件给应用程序(下)

1.简介

在InputManagerService分发输入事件消息给应用程序(上)中介绍了InputReader从EventHub中获取输入事件,并对输入事件进行加工处理,然后将输入事件放入到InputDispatcher的mInboundQueue队列,并通过Looper->wake()方法唤醒InputDispatcherThread线程。本篇文章将从InputDispatcherThread线程开始分析,InputDispatcher如何将输入事件分发到应用程序激活的Activity窗口。

2.源码分析

在InputDispatcherThread线程中,会循环调用其threadLoop()方法。

2.1 InputDispatcherThread.threadLoop()

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

当threadLoop()方法返回true时,代表将循环调用loopOnce()方法。当threadLoop()方法返回false时,代表退出循环。

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        if (!haveCommandsLocked()) {//当mCommandQueue队列为空时,则说明当前没有等待执行任务,则可以进行分发任务。
            dispatchOnceInnerLocked(&nextWakeupTime);//分发输入事件,见2.2
        }

        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);//进入epoll_wait等待状态
}

在调用InputDispatcher的dispatchOnce()方法时,如果没有输入事件需要分发,则会调用Looper的pollOnce()方法,进入epoll_wait等待状态。
当发送以下3种情况时,会退出epoll_wait等待状态:

  • callback:通过回调方法唤醒;
  • timeout:超时时间timeoutMillis唤醒;
  • wake:主动通过Looper->wake()方法唤醒;

当InputDispatcher退出epoll_wait等待状态,会返回到dispatchOnce()方法,再返回到threadLoop()方法,因为返回值为true,所以接着又循环调用threadLoop()方法,重新进入dispatchOnce()方法。因为此时已经有输入事件需要处理了,所以会调用dispatchOnceInnerLocked()方法进行处理。

2.2 InputDispatcher.dispatchOnceInnerLocked()

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();//获取当前时间
    //分发禁用了,则重置key重复操作;
    if (!mDispatchEnabled) {
        resetKeyRepeatLocked();
    }
    //如果分发冻结了,则不再处理超时和分发事件的工作,直接返回
    if (mDispatchFrozen) {
        return;
    }
    // 优化App切换延迟,当切换超时,则抢占分发,丢弃其他所有即将要处理的事件。
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }

    // 开始准备启动一个新的事件,如果我们还没有准备好待处理的事件,则先获取一个待处理的事件
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {//如果mInboundQueue队列为空,则说明没有输入事件需要处理
            if (isAppSwitchDue) {
                resetPendingAppSwitchLocked(false);// mInboundQueue队列为空,说明我们等待APP切换key不会到来,所以停止等待。
                isAppSwitchDue = false;
            }

            if (mKeyRepeatState.lastKeyEntry) {
                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                } else {
                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                    }
                }
            }

            if (!mPendingEvent) {//没有待处理的事件,直接返回
                return;
            }
        } else {
            mPendingEvent = mInboundQueue.dequeueAtHead();//mInboundQueue队列不为空,从mInboundQueue队列中获取输入事件
            traceInboundQueueLengthLocked();
        }

        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(mPendingEvent);
        }
        //开始准备分发事件
        resetANRTimeoutsLocked();//重置ANR信息,见2.3
    }

    ALOG_ASSERT(mPendingEvent != NULL);
    bool done = false;
    DropReason dropReason = DROP_REASON_NOT_DROPPED;//丢弃的原因
    if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DROP_REASON_POLICY;//丢弃原因为策略原因
    } else if (!mDispatchEnabled) {
        dropReason = DROP_REASON_DISABLED;//丢弃原因为禁用
    }

    if (mNextUnblockedEvent == mPendingEvent) {
        mNextUnblockedEvent = NULL;
    }

    ......
    case EventEntry::TYPE_KEY: {//按键输入事件
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        if (isAppSwitchDue) {
            if (isAppSwitchKeyEventLocked(typedEntry)) {
                resetPendingAppSwitchLocked(true);
                isAppSwitchDue = false;
            } else if (dropReason == DROP_REASON_NOT_DROPPED) {
                dropReason = DROP_REASON_APP_SWITCH;//丢弃原因为APP切换
            }
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);//分发按键输入事件,见2.4
        break;
    }

    case EventEntry::TYPE_MOTION: {//触摸事件
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
            && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }

    default:
        ALOG_ASSERT(false);
        break;
    }

    //分发操作完成,则进入该分支
    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);//丢弃事件,见
        }
        mLastDropReason = dropReason;

        releasePendingEventLocked();//释放待处理的输入事件,见
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }   
}

在enqueueInboundEventLocked()的过程中已设置mAppSwitchDueTime等于eventTime加上500ms:

mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;

该方法的主要功能有:

  1. mDispatchFrozen用于决定是否冻结事件分发工作是否继续执行;
  2. 当事件分发的时间点距离该事件加入mInboundQueue的时间超过了500ms,则认为APP切换超时,即isAppSwitchDue为true;
  3. 当mInboundQueue不为空时,则取出头部的事件,放入mPendingEvent中,并重置ANR时间;
  4. 根据输入事件的类型,分别调用不同的处理函数,例如按键事件,则调用dispatchKeyLocked方法进行处理;
  5. 当事件处理完成(done)了,根据dropReason(默认NOT_DROPPED)来决定是否丢弃事件dropInboundEventLocked;
  6. 释放当前正在处理的事件mPendingEvent,即调用releasePendingEventLocked;

2.3 InputDispatcher.resetANRTimeoutsLocked()

void InputDispatcher::resetANRTimeoutsLocked() {
    //清空超时等待cause和handle,将等待原因改为INPUT_TARGET_WAIT_CAUSE_NONE
    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
    mInputTargetWaitApplicationHandle.clear();
}

2.4 InputDispatcher.dispatchKeyLocked()

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
    DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // 预处理
    if (! entry->dispatchInProgress) {
        if (entry->repeatCount == 0
            && entry->action == AKEY_EVENT_ACTION_DOWN
            && (entry->policyFlags & POLICY_FLAG_TRUSTED)
            && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
            if (mKeyRepeatState.lastKeyEntry
                && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
                // 产生了重复按键,记录下重复按下的次数,并重置重复按键时间为最大
                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // 
            } else {
                // 不是重复按键,保存下按键状态,以防后面有重复按键
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
            }
            mKeyRepeatState.lastKeyEntry = entry;//保存最近按键事件为当前处理的按键事件
            entry->refCount += 1;//将按键事件的引用次数加1
        } else if (! entry->syntheticRepeat) {
            resetKeyRepeatLocked();
        }

        if (entry->repeatCount == 1) {
            entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;//长按标志
        } else {
            entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
        }

        entry->dispatchInProgress = true;

        logOutboundKeyDetailsLocked("dispatchKey - ", entry);
    }

    // 处理拦截策略请求我们再次尝试处理
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        // 当前时间小于拦截唤醒时间
        if (currentTime < entry->interceptKeyWakeupTime) {
            if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                *nextWakeupTime = entry->interceptKeyWakeupTime;//更新下一次唤醒时间
            }
            return false; // wait until next wakeup
        }
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
        entry->interceptKeyWakeupTime = 0;
    }

    // 让policy尝试拦截这个按键事件
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {//传递给用户
            CommandEntry* commandEntry = postCommandLocked(
                & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);//将命令放入队列,见2.5
            // 如果获取焦点的窗口不为空,则将其作为输入窗口
            if (mFocusedWindowHandle != NULL) {
                commandEntry->inputWindowHandle = mFocusedWindowHandle;
            }
            commandEntry->keyEntry = entry;
            entry->refCount += 1;
            return false; //等待命令运行
        } else {
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {//跳过
        if (*dropReason == DROP_REASON_NOT_DROPPED) {
            *dropReason = DROP_REASON_POLICY;//丢弃原因为Policy
        }
    }

    // 如果输入事件丢弃原因不是DROP_REASON_NOT_DROPPED,则清空丢弃的输入事件
    if (*dropReason != DROP_REASON_NOT_DROPPED) {
        setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
            ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
        return true;
    }

    // 验证输入目标
    Vector<InputTarget> inputTargets;
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
        entry, inputTargets, nextWakeupTime);//找出获取焦点的窗口,见2.6
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

    setInjectionResultLocked(entry, injectionResult);
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }

    addMonitoringTargetsLocked(inputTargets);

    dispatchEventLocked(currentTime, entry, inputTargets);//分发输入事件,见2.9
    return true;

}

在分发事件前,会做一些预处理工作,如果出现以下情况,则直接返回,跳过事件分发:

  1. 当前时间小于拦截唤醒时间的情况,返回false;
  2. 输入事件被Policy拦截了,返回false;
  3. 输入事件需要被丢弃的情况,返回true;
  4. 寻找聚焦窗口,聚焦窗口还未准备好,返回false;
  5. 寻找聚焦窗口失败,则返回true;

2.5 InputDispatcher.postCommandLocked()

InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command);//创建新的CommandEntry
    mCommandQueue.enqueueAtTail(commandEntry);//将CommandEntry放入到队列的尾部
    return commandEntry;//返回CommandEntry
}

InputDispatcher::CommandEntry::CommandEntry(Command command) :
    command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0),
    seq(0), handled(false) {
}

// Command类型定义
typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);

postCommandLocked()方法的主要作用是将创建一个CommandEntry,并把CommandEntry放入到命令队列尾部,然后等待执行,最后返回一个CommandEntry。

void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
    CommandEntry* commandEntry) {
    KeyEntry* entry = commandEntry->keyEntry;

    KeyEvent event;
    initializeKeyEvent(&event, entry);

    mLock.unlock();

    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
        &event, entry->policyFlags);//调用到InputManagerService的interceptKeyBeforeDispatching()方法。

    mLock.lock();

    if (delay < 0) {// delay小于0
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;//跳过拦截
    } else if (!delay) {//delay等于0
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;//继续执行
    } else {//delay大于0
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;//下次再尝试
        entry->interceptKeyWakeupTime = now() + delay;//拦截唤醒事件
    }
    entry->release();
}

doInterceptKeyBeforeDispatchingLockedInterruptible方法最终会调用到Ja

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值