【Android】WMS(五)输入事件原理

输入事件原理

安卓输入事件整体流程

Android 系统是由事件驱动的,而 input 是最常见的事件之一,用户的点击、滑动、长按等操作,都属于 input 事件驱动,其中的核心就是 InputReader 和 InputDispatcher。

InputReader 和 InputDispatcher 是跑在 SystemServer进程中的两个 native 循环线程,负责读取和分发 Input 事件。整个处理过程大致流程如下:

在这里插入图片描述

InputManagerService

在SystemServer的startOtherServices函数中,会新建一个InputManagerService对象,然后会作为参数传入到WMS中去。

inputManager = new InputManagerService(context);
......
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
WindowInputEventReceiver输入事件监听

在ViewRootImpl的setView函数中,会先建立一个InputChannel对象。inputChannel是输入事件的信道,它ViewRootImpl和InputManagerService的沟通桥梁。

 InputChannel inputChannel = null;
 if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
      inputChannel = new InputChannel();
 }

inputChannel会通过mWindowSession.addToDisplayAsUser方法传入到WMS侧,并且与当前新建窗口WindowState建立起关系

if  (openInputChannels) {
	//WindowState与InputChannel关联上了
    win.openInputChannel(outInputChannel);
}

之后代码运行到ViewRootImpl侧,这个时候就开始生成监听对象

if (inputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
   					// 输入事件的监听对象
                    mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                            Looper.myLooper());
                }

}
EventHub设备事件监听

它主要是利用Linux的inotify和epoll机制,监听设备事件:包括设备插拔及各种触摸、按钮事件等,可以看做是一个不同设备的集线器,主要面向的是/dev/input目录下的设备节点,比如说/dev/input/event0上的事件就是输入事件:

在这里插入图片描述

InputReader读取事件

InputReader主要是负责不断的从EventHub读取事件,通知派发给InputDispatcher。

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    { 
    ......
    // 读取EventHub中的事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
   ......
   // 通知派发给InputDispatcher
   mQueuedListener->flush();
}
InputDispatcher读取事件
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        ......
        // 被唤醒 ,处理Input消息
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    
       .....
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    //睡眠等到事件
    mLooper->pollOnce(timeoutMillis);
}
寻找目标窗口

Android系统能够同时支持多块屏幕,每块屏幕被抽象成一个DisplayContent对象,内部维护一个WindowList列表对象,用来记录当前屏幕中的所有窗口,包括状态栏、导航栏、应用窗口、子窗口等。对于触摸事件,我们比较关心可见窗口

那么,如何找到触摸事件对应的窗口呢,是状态栏、导航栏还是应用窗口呢,这个时候DisplayContent的WindowList就发挥作用了,DisplayContent握着所有窗口的信息,因此,可以根据触摸事件的位置及窗口的属性来确定将事件发送到哪个窗口,当然其中的细节比一句话复杂的多,跟窗口的状态、透明、分屏等信息都有关系,下面简单瞅一眼,达到主观理解的流程就可以了,然后会调用到InputDispatcher#findTouchedWindowAtLocked

// 遍历所有窗口 
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
        if (windowInfo->displayId == displayId) {
            int32_t flags = windowInfo->layoutParamsFlags;

            if (windowInfo->visible) {
                if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
                    bool isTouchModal = (flags &
                                         (InputWindowInfo::FLAG_NOT_FOCUSABLE |
                                          InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                        int32_t portalToDisplayId = windowInfo->portalToDisplayId;
                        ......
                        // Found window. 找到目标窗口
                        return windowHandle;
                    }
                }
            }
        }

mWindowHandles代表着所有窗口,找到了目标窗口,同时也将事件封装好了,剩下的就是通知目标窗口,可是有个最明显的问题就是,目前所有的逻辑都是在SystemServer进程,而要通知的窗口位于APP端的用户进程,那么如何通知呢?这里面采用的都是Socket的通信方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值