Android系统源码阅读(13):Input消息的分发过程

Android系统源码阅读(13):Input消息的分发过程

请对照AOSP版本:6.0.1_r50。学校电脑好渣,看源码时卡半天


先回顾一下前两篇文章。在设备没有事件输入的时候,InputReader和InputDispatcher都处于睡眠状态。当输入事件发生,InputReader首先被激活,然后发送读取消息,激活Dispatcher。Dispatcher被激活以后,将消息发送给当前激活窗口的主线程,然后睡眠等待主线程处理完这个事件。主线程被激活后,会处理相应的消息,处理完毕后反馈给Dispatcher,从而Dispatcher可以继续发送消息。

1. InputReader获取事件

回顾一下第11章4.2中,InputReader线程在获取事件以后,会调用processEventsLocked(mEventBuffer, count);处理事件。

这里写图片描述

1.1

这里先根据event的种类进行分门别类的处理。

frameworks/native/services/inputflinger/InputReader.cpp :

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;

        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            //如果这里获得是合成事件
            //这里一次要将该输入设备中的一组事件都获取出来
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            //处理这些事件
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            //这些是一些设备状况的事件,没必要将这些消息发送出去,留给自己处理就可以了
            switch (rawEvent->type) {
            case EventHubInterface::DEVICE_ADDED:
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::DEVICE_REMOVED:
                removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                break;
            case EventHubInterface::FINISHED_DEVICE_SCAN:
                handleConfigurationChangedLocked(rawEvent->when);
                break;
            default:
                ALOG_ASSERT(false); // can't happen
                break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

1.2

准备将事件交给设备进行处理。

frameworks/native/services/inputflinger/InputReader.cpp :

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {  
    //判断设备是否是已知的
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }
    //获得设备
    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
    //交给设备进行处理
    device->process(rawEvents, count);
}

1.3

用device中的mapper去映射传入的事件,然后再处理。

frameworks/native/services/inputflinger/InputReader.cpp :

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.
    //一个设备可能有多种类型的event,所有有多个mapper,但是需要保持event的顺序性
    //所以这里采用先循环event,再循环mapper的方式
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
                //..
            } else {
                //..
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            //..
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                //用每一种mapper尝试处理event
                mapper->process(rawEvent);
            }
        }
    }
}

1.4

这里InputMapper种类很多,每个事件处理方法各不相同,所以在这里不再详述。其中有如下的InputMapper:

SwitchInputMapper, VibratorInputMapper, KeyboardInputMapper, CursorInputMapper, TouchInputMapper, SingleTouchInputMapper, MultiTouchInputMapper, JoystickInputMapper

这些方法处理到最后,会调用getListener()->notifyXXX(&args),让Dispatcher进行分发,XXX根据不同的Mapper有相应的名字。在创建InputReader时,将InputDispatcher作为参数传入,同时建立了QueuedInputListener来存放这个InputDispatcher,估计是准备将来处理多个InputDispatcher。所以这里getListener获取的就是当初建立的InputDispatcher对象。

1.5

这里虽然已经开始调用InputDispatcher的函数,但是还是在InputReader线程中。这里开始向InputDispatcher的队列中插入事件,并且把InputDispatcher唤醒了。因为notifyXXX函数同样是针对不同的输入有着不同的处理,所以不再详述,截取一段MotionEvent的代码片段。

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

   //针对每种event,都会想将其封装成一个EventEntry
   // Just enqueue a new motion event.
   MotionEntry* newEntry = new MotionEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->displayId,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
  //然后加入队列
  needWake = enqueueInboundEventLocked(newEntry);

  //...
  //唤醒Looper线程
  if (needWake) {
        mLooper->wake();
    }

将一个事件放入队列之后,会根据needWake参数决定是否要唤醒线程。如果要唤醒,则调用Looper的wake函数就可以了,和原来道理一样。在有些时候,有事件添加进去,不一定要唤醒线程,比如线程正在等待应用反馈事件处理完毕的消息。

1.6

实实在在的将这个EventEntry放入队列mInboundQueue中了。

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

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    //如果队列空了,需要唤醒
    bool needWake = mInboundQueue.isEmpty();
    //将事件加入队列
    mInboundQueue.enqueueAtTail(entry);
    traceInboundQueueLengthLocked();

    switch (entry->type) {
    //这里会优化App切换的事件,如果上一个App还有事件没处理完,也没反馈事件处理完毕消息
    //则清空之前的事件,切换下一个应用
    case EventEntry::TYPE_KEY: {
        // Optimize app switch latency.
        // If the application takes too long to catch up then we drop all events preceding
        // the app switch key.
        KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
        if (isAppSwitchKeyEventLocked(keyEntry)) {
            if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
                mAppSwitchSawKeyDown = true;
            } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
                if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
                    ALOGD("App switch is pending!");
#endif
                    mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
                    mAppSwitchSawKeyDown = false;
                    needWake = true;
                }
            }
        }
        break;
    }

    //当一个非当前激活app的点击事件发生,会清空之前的事件
    //从这个新的点击事件开始
    case EventEntry::TYPE_MOTION: {
        // Optimize case where the current application is unresponsive and the user
        // decides to touch a window in a different application.
        // If the application takes too long to catch up then we drop all events preceding
        // the touch into the other window.
        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
        if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
                && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
                && mInputTargetWaitApplicationHandle != NULL) {
            int32_t displayId = motionEntry->displayId;
            int32_t x = int32_t(motionEntry->pointerCoords[0].
                    getAxisValue(AMOTION_EVENT_AXIS_X));
            int32_t y = int32_t(motionEntry->pointerCoords[0].
                    getAxisValue(AMOTION_EVENT_AXIS_Y));
            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
            if (touchedWindowHandle != NULL
                    && touchedWindowHandle->inputApplicationHandle
                            != mInputTargetWaitApplicationHandle) {
                // User touched a different application than the one we are waiting on.
                // Flag the event, and start pruning the input queue.
                mNextUnblockedEvent = motionEntry;
                needWake = true;
            }
        }
        break;
    }
    }
    return needWake;
}

这里做了两种优化,主要是在当前App窗口处理事件过慢,同时你又触发其他App的事件时,Dispatcher就会丢弃先前的事件,从这个开始唤醒Dispatcher。这样做很合情合理,用户在使用时,会遇到App由于开发者水平有限导致处理事件过慢情况,这时用户等的不耐烦,则应该让用户轻松的切换到其它App,而不是阻塞在那。所以,事件无法响应只会发生在App内部,而不会影响应用的切换,从而提升用户体验。App的质量问题不会影响系统的运转,Android在这点上做的很人性。

2. InputDispatcher分发事件

在第11章中3.2中,Dispatcher调用函数dispatchOnceInnerLocked(&nextWakeupTime);来分配队列中的事件。

这里写图片描述

2.1

从队列中获取event,然后准备处理。

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

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();

    // Reset the key repeat timer whenever normal dispatch is suspended while the
    // device is in a non-interactive state.  This is to ensure that we abort a key
    // repeat if the device is just coming out of sleep.
    if
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值