Android触摸事件的传递(四-1)--输入系统-InputReader

了解更多,移步Android触摸事件传递机制系列详解

在Android触摸事件的传递(二)--输入系统InputManagerService,介绍IMS服务的启动过程会创建两个native线程,分别是InputReader,InputDispatcher. 接下来从InputReader线程的执行过程从threadLoop为起点开始分析

1 threadLoop

[-> InputReader.cpp]

bool InputReaderThread::threadLoop() {
    mReader->loopOnce(); //【见小节1.2】
    return true;
}
  • threadLoop返回值true代表的是会不断地循环调用loopOnce()。另外,如果当返回值为false则会 退出循环。
  • 整个过程是不断循环的地调用InputReaderloopOnce()方法。

2 loopOnce();

  1. 查看InputReader配置是否修改,如界面大小、方向、键盘布局重新加载、指针速度改变等
  2. 从EventHub读取事件,其中EVENT_BUFFER_SIZE = 256
  3. 处理事件
  4. 将事件传到InputDispatcher

注:通过EventHub->getEvents(),获取输入事件和设备增删事件,count为读取的事件数量,mEventBuffer存储着事件。由上一篇文章可知getEvents()是阻塞的,只有当有事件或者被wake才会被唤醒向下执行。

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector inputDevices;
    { // acquire lock
        AutoMutex _l(mLock);
 
        oldGeneration = mGeneration;
        timeoutMillis = -1;
    //查看InputReader配置是否修改,如界面大小、方向、键盘布局重新加载、指针速度改变等
        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes); //刷新配置
         } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock
    ////从EventHub读取事件,其中EVENT_BUFFER_SIZE = 256【见小节2.1】
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
 
    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();
 
        if (count) {//处理事件【见小节3.1】
            processEventsLocked(mEventBuffer, count);
        }
 
        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }
 
        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock
 
    // 发送一个消息,该消息描述已更改的输入设备。
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    mQueuedListener->flush();  //将事件传到InputDispatcher.
}

2.1 EventHub->getEvents(),获取输入事件和设备增删事件

通过EventHub->getEvents(),获取输入事件和设备增删事件,count为读取的事件数量,mEventBuffer存储着事件。由上一篇文章可知getEvents()是阻塞的,只有当有事件或者被wake才会被唤醒向下执行。
Android触摸事件的传递(三)--输入系统EventHub

2.2 processEventsLocked 处理事件

  • 接着判断count的值,如果有读到事件(count大于0),则调用
    processEventsLocked(mEventBuffer, count)处理事件。
  • processEventsLocked()函数中会遍历所有的事件,分别进行处理。其处理的事件类型分为四种:原始输入事件、设备加载事件、设备卸载事件及FINISHED_DEVICE_SCAN事件。
  • 这里我们只关心对于设备自身产生的事件。也就是触摸屏相关的事件。也就是processEventsForDeviceLocked函数中所进行的操作。

[-> 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; //同一设备的事件打包处理
            }
            //数据事件的处理【见小节3.3】
            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);//不会发生
                break;
            }
        }
        count -= batchSize;  //count减少已处理事件个数,表示剩余事件个数
        rawEvent += batchSize;  //rawEvent指针向后移动batchSize个RawEvent对象,也就是指到该处理的事件上。
    }
}

2.2.1事件派发到Device

根据事件获得相应的设备类型,然后将事件交给相应的设备处理。判断是否忽略该事件,如果不是忽略该事件,则会调用相应设备的process方法进行处理。

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        return;
    }

    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        return;//可忽略则直接返回
    }

    device->process(rawEvents, count);
}

2.2.3 事件派发到InputMapper

这里的事件又交给了InputMapper来处理

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
     ....
    for (size_t i = 0; i < numMappers; i++) {
           InputMapper* mapper = mMappers[i];
            //调用具体mapper来处理
           mapper->process(rawEvent);
    }
    ....
}
Android触摸事件的传递(四-1)--输入系统-InputReader_第1张图片
160fe0a1c21df76b.png
  • InputMapper对应了很多的子类,这里根据事件的类型进行相应的派发,处理。
  • 事件到了这里之后,如何传递到应用层,这里mapper->process进行了那些处理。
  • 这里来看一下对于触摸屏事件的处理函数。
void TouchInputMapper::process(const RawEvent* rawEvent) {
    mCursorButtonAccumulator.process(rawEvent);
    mCursorScrollAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);

    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        sync(rawEvent->when);
    }
}

追踪数据流向
通过process函数处理,我们继续追踪函数的数据流向。对于相关的事件处理,这里最终执行的是将

void TouchInputMapper::sync(nsecs_t when) {
    .....
    processRawTouches(false /*timeout*/);
}

void TouchInputMapper::processRawTouches(bool timeout) {
    if (mDeviceMode == DEVICE_MODE_DISABLED) {
        // Drop all input if the device is disabled.
        mCurrentRawState.clear();
        mRawStatesPending.clear();
        return;
    }

    // Drain any pending touch states. The invariant here is that the mCurrentRawState is always
    // valid and must go through the full cook and dispatch cycle. This ensures that anything
    // touching the current state will only observe the events that have been dispatched to the
    // rest of the pipeline.
    const size_t N = mRawStatesPending.size();
    size_t count;
    for(count = 0; count < N; count++) {
        const RawState& next = mRawStatesPending[count];

        // A failure to assign the stylus id means that we're waiting on stylus data
        // and so should defer the rest of the pipeline.
        if (assignExternalStylusId(next, timeout)) {
            break;
        }

        // All ready to go.
        clearStylusDataPendingFlags();
        mCurrentRawState.copyFrom(next);
        if (mCurrentRawState.when < mLastRawState.when) {
            mCurrentRawState.when = mLastRawState.when;
        }
        cookAndDispatch(mCurrentRawState.when);
    }
    if (count != 0) {
        mRawStatesPending.removeItemsAt(0, count);
    }

    if (mExternalStylusDataPending) {
        if (timeout) {
            nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
            clearStylusDataPendingFlags();
            mCurrentRawState.copyFrom(mLastRawState);
            cookAndDispatch(when);
        } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
            mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
        }
    }
}
void TouchInputMapper::cookAndDispatch(nsecs_t when) {
    // Always start with a clean state.
    mCurrentCookedState.clear();

    // Apply stylus buttons to current raw state.
    applyExternalStylusButtonState(when);

    // Handle policy on initial down or hover events.
    bool initialDown = mLastRawState.rawPointerData.pointerCount == 0
            && mCurrentRawState.rawPointerData.pointerCount != 0;

    uint32_t policyFlags = 0;
    bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
    if (initialDown || buttonsPressed) {
        // If this is a touch screen, hide the pointer on an initial down.
        if (mDeviceMode == DEVICE_MODE_DIRECT) {
            getContext()->fadePointer();
        }

        if (mParameters.wake) {
            policyFlags |= POLICY_FLAG_WAKE;
        }
    }

    // Consume raw off-screen touches before cooking pointer data.
    // If touches are consumed, subsequent code will not receive any pointer data.
    if (consumeRawTouches(when, policyFlags)) {
        mCurrentRawState.rawPointerData.clear();
    }

    // Cook pointer data.  This call populates the mCurrentCookedState.cookedPointerData structure
    // with cooked pointer data that has the same ids and indices as the raw data.
    // The following code can use either the raw or cooked data, as needed.
    cookPointerData();

    // Apply stylus pressure to current cooked state.
    applyExternalStylusTouchState(when);

    // Synthesize key down from raw buttons if needed.
    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
            policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);

    // Dispatch the touches either directly or by translation through a pointer on screen.
    if (mDeviceMode == DEVICE_MODE_POINTER) {
        for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);
                !idBits.isEmpty(); ) {
            uint32_t id = idBits.clearFirstMarkedBit();
            const RawPointerData::Pointer& pointer =
                    mCurrentRawState.rawPointerData.pointerForId(id);
            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
                mCurrentCookedState.stylusIdBits.markBit(id);
            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER
                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
                mCurrentCookedState.fingerIdBits.markBit(id);
            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {
                mCurrentCookedState.mouseIdBits.markBit(id);
            }
        }
        for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);
                !idBits.isEmpty(); ) {
            uint32_t id = idBits.clearFirstMarkedBit();
            const RawPointerData::Pointer& pointer =
                    mCurrentRawState.rawPointerData.pointerForId(id);
            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS
                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
                mCurrentCookedState.stylusIdBits.markBit(id);
            }
        }

        // Stylus takes precedence over all tools, then mouse, then finger.
        PointerUsage pointerUsage = mPointerUsage;
        if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
            mCurrentCookedState.mouseIdBits.clear();
            mCurrentCookedState.fingerIdBits.clear();
            pointerUsage = POINTER_USAGE_STYLUS;
        } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {
            mCurrentCookedState.fingerIdBits.clear();
            pointerUsage = POINTER_USAGE_MOUSE;
        } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||
                isPointerDown(mCurrentRawState.buttonState)) {
            pointerUsage = POINTER_USAGE_GESTURES;
        }

        dispatchPointerUsage(when, policyFlags, pointerUsage);
    } else {
        if (mDeviceMode == DEVICE_MODE_DIRECT
                && mConfig.showTouches && mPointerController != NULL) {
            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);

            mPointerController->setButtonState(mCurrentRawState.buttonState);
            mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
                    mCurrentCookedState.cookedPointerData.idToIndex,
                    mCurrentCookedState.cookedPointerData.touchingIdBits);
        }

        if (!mCurrentMotionAborted) {
            dispatchButtonRelease(when, policyFlags);
            dispatchHoverExit(when, policyFlags);
            dispatchTouches(when, policyFlags);
            dispatchHoverEnterAndMove(when, policyFlags);
            dispatchButtonPress(when, policyFlags);
        }

        if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
            mCurrentMotionAborted = false;
        }
    }

    // Synthesize key up from raw buttons if needed.
    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
            policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);

    // Clear some transient state.
    mCurrentRawState.rawVScroll = 0;
    mCurrentRawState.rawHScroll = 0;

    // Copy current touch to last touch in preparation for the next cycle.
    mLastRawState.copyFrom(mCurrentRawState);
    mLastCookedState.copyFrom(mCurrentCookedState);
}

void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
      ....
    dispatchMotion();
      ....
}
  • 在相关的函数调用之后,最终调用了dispatchTouches
  • 对于dispatchTouches中,会根据记录的上一次的触摸位置,对事件的类型进行判断,然后做相应的分发,事件类型有抬起,下落,移动等,然后对相应的事件进行分发。无论是对于何种类型的事件派发,最终被调用到的都是dispatchMotion()方法。
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
      ....
    dispatchMotion();
      ....
}
void TouchInputMapper::dispatchMotion() {
   ....
   NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
            action, actionButton, flags, metaState, buttonState, edgeFlags,
            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
            xPrecision, yPrecision, downTime);
    getListener()->notifyMotion(&args);
}

getListener函数

InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

notifyMotion函数实现

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

我们将触摸相关的事件进行包装之后,将其加入到一个ArgsQueue队列,到此,我们已经将数据加入到参数队列中

InputReader的loopOnce过程, 可知当执行完processEventsLocked()过程, 然后便开始执行mQueuedListener->flush()过程

2.3 QueuedInputListener的flush方法

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

遍历整个mArgsQueue数组, 在input架构中NotifyArgs的实现子类主要有以下几类:

  • NotifyConfigurationChangedArgs
  • NotifyKeyArgs
  • NotifyMotionArgs
  • NotifySwitchArgs
  • NotifyDeviceResetArgs

NotifyArgs的notify函数实现

void NotifyMotionArgs::notify(const sp& listener) const {
    listener->notifyMotion(this);
}

对于这个listener的创建来自于InputReader构建的时候。

mQueuedListener = new QueuedInputListener(listener);
 mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

InputDispatcher
而这里的Listener则是InputDispatcherInputDispatchernotifyMotion实现源码。

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    .....
   MotionEvent event;
   event.initialize(args->deviceId, args->source, args->action, args->actionButton,
                    args->flags, args->edgeFlags, args->metaState, args->buttonState,
                    0, 0, args->xPrecision, args->yPrecision,
                    args->downTime, args->eventTime,
                    args->pointerCount, args->pointerProperties, args->pointerCoords);
    ....
  //构造MotionEntry,然后将其加入到enqueueInboundEventLocked之中
  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);
    ....
   if (needWake) {
      mLooper->wake();//唤醒其中的looper
   }
}

在该函数中,所做的事情是对于所传递的参数,构造MotionEntry,然后将其加入到enqueueInboundEventLocked之中。然后唤醒其中的looper

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    mInboundQueue.enqueueAtTail(entry);
    ...
    //进行一些事件和窗口相关的判断处理
}

总结

Android触摸事件的传递(四-1)--输入系统-InputReader_第2张图片
触摸事件流程2.png

补充--设备增加

1. addDeviceLocked

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {
        return; //已添加的相同设备则不再添加
    }

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
   
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);
    mDevices.add(deviceId, device); //添加设备到mDevices
    ...
}

2 createDeviceLocked

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    //创建InputDevice对象
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);
    ...

    //获取键盘源类型
    uint32_t keyboardSource = 0;
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
    }
    if (classes & INPUT_DEVICE_CLASS_DPAD) {
        keyboardSource |= AINPUT_SOURCE_DPAD;
    }
    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
    }

    //添加键盘类设备InputMapper
    if (keyboardSource != 0) {
        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
    }

    //添加鼠标类设备InputMapper
    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
        device->addMapper(new CursorInputMapper(device));
    }

    //添加触摸屏设备InputMapper
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        device->addMapper(new MultiTouchInputMapper(device));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        device->addMapper(new SingleTouchInputMapper(device));
    }
    ...
    return device;
}

该方法主要功能:

  • 创建InputDevice对象,将InputReadermContext赋给InputDevice对象所对应的变量
  • 根据设备类型来创建并添加相对应的InputMapper,同时设置mContext.

input设备类型有很多种,以上代码只列举部分常见的设备以及相应的InputMapper:

  • 键盘类设备:KeyboardInputMapper
  • 触摸屏设备:MultiTouchInputMapper或SingleTouchInputMapper
  • 鼠标类设备:CursorInputMapper

参考

Android系统源码剖析-事件分发
Android 输入系统(三)InputReader

你可能感兴趣的:(Android触摸事件的传递(四-1)--输入系统-InputReader)