Android输入管理InputManager之派送给Window


InputDispatcher#dispatchMotionLocked分发处理事件解析

三个步骤

  • 判断事件是否触摸事件
  • 寻找触屏Window
  • 事件派发

InputDispatcher#dispatchMotionLocked方法

bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, 
                DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ....
    //判定事件源为手指触屏
    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
 
    Vector<InputTarget> inputTargets;
    if (isPointerEvent) {// 触屏事件
        //初始化Window目标到inputTargets数组,返回事件注入的结果
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        ...//非触屏事件
    }

    //派发
    dispatchEventLocked(currentTime, entry, inputTargets);
}

injectionResult 成功找到Window目标,标志是0。

INPUT_EVENT_INJECTION_SUCCEEDED = 0
其他
INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,//失败 无权限
INPUT_EVENT_INJECTION_FAILED = 2,//失败 无目标
INPUT_EVENT_INJECTION_TIMED_OUT = 3//超时
  • findTouchedWindowTargetsLocked解析

初始化inputTargets数组
Vector<InputTarget>& inputTargets
InputWindowHandle类型的数组,确定触屏windowHandle
Vector<sp<InputWindowHandle> > mWindowHandles;

遍历InputWindowHandle数组元素,寻找触屏InputWindowHandle。

findTouchedWindowTargetsLocked方法代码段:

int32_t pointerIndex = getMotionEventActionPointerIndex(action);
//触摸坐标(x,y)
int32_t x = int32_t(entry->pointerCoords[pointerIndex].
                getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry->pointerCoords[pointerIndex].
                getAxisValue(AMOTION_EVENT_AXIS_Y));
size_t numWindows = mWindowHandles.size();
for (size_t i = 0; i < numWindows; i++) {
    sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
    const InputWindowInfo* windowInfo = windowHandle->getInfo();
    ...
    int32_t flags = windowInfo->layoutParamsFlags;
    if (windowInfo->visible) {//信息说明窗体可见
        //flags信息说明窗体支持可触摸
        if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
            isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
            if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                newTouchedWindowHandle = windowHandle;
                break; //发现触屏Window,退出遍历
            }
        }

        if (maskedAction == AMOTION_EVENT_ACTION_DOWN
                        && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
            int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
            if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
                outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
            }
            mTempTouchState.addOrUpdateWindow(
                            windowHandle, outsideTargetFlags, BitSet32(0));
        }
     }
}
...
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);

touchableRegionContainsPoint判断是否包含触摸坐标(x,y),如果包含说明触摸点在该窗体上。
根据找到的newTouchedWindowHandle更新或者增加mTempTouchState结构体中windows元素内容
windows是保存mTempTouchState(TouchState)结构体中TouchedWindow类型的数组,遍历windows,若TouchedWindow的windowHandle与newTouchedWindowHandle相等,更新TouchedWindow中的targetFlags与pointerIds值。

TouchState#addOrUpdateWindow方法
void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& 
            windowHandle,int32_t targetFlags, BitSet32 pointerIds){
    ...
    //遍历TouchState中windows的每个TouchedWindow
    //找到与入参InputWindowHandle相同的TouchedWindow
    //更新其中的值
    for (size_t i = 0; i < windows.size(); i++) {
        TouchedWindow& touchedWindow = windows.editItemAt(i);
        if (touchedWindow.windowHandle == windowHandle) {
            touchedWindow.targetFlags |= targetFlags;
            if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
                touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
            }
            touchedWindow.pointerIds.value |= pointerIds.value;
            return;
        }
    }
    
    //如果没有找到,压入栈顶新元素,创建一个TouchedWindow
    windows.push();

    TouchedWindow& touchedWindow = windows.editTop();//操作栈顶
    //新TouchedWindow赋值 
    touchedWindow.windowHandle = windowHandle;
    touchedWindow.targetFlags = targetFlags;
    touchedWindow.pointerIds = pointerIds;
}

确保TouchState的TouchedWindow列表中有一个是保存触屏Window信息。

最后初始化InputTargets数组,通过addWindowTargetLocked方法完成InputTargets数组初始化。

findTouchedWindowTargetsLocked方法代码段:

// Success!  Output targets.返回注入成功结果
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
//遍历TouchedWindow数组
for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
    const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
    addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                touchedWindow.pointerIds, inputTargets);
}

这时TouchState的TouchedWindow数组中应该有一个TouchedWindow的windowHandle对应触摸的Window。一般触摸的情况下TouchedWindow应该只有一个元素吧。
用每个TouchedWindow中的InputWindowHandle信息初始化inputTargets。

addWindowTargetLocked方法

void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
        int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
    inputTargets.push();//将一项入栈,该项默认的构造方法构建

    const InputWindowInfo* windowInfo = windowHandle->getInfo();
    InputTarget& target = inputTargets.editTop(); //取出栈顶,授予对栈顶的访问
    target.inputChannel = windowInfo->inputChannel;
    target.flags = targetFlags;
    target.xOffset = - windowInfo->frameLeft;
    target.yOffset = - windowInfo->frameTop;
    target.scaleFactor = windowInfo->scaleFactor;
    target.pointerIds = pointerIds;
}

最后一个被赋值的InputTarget位于栈顶。

InputTarget主要赋值内容:inputChannel,targetFlags,pointerIds。
TouchedWindow#InputWindowHandle中的windowInfo信息赋值InputTarget。
包括inputChannel,它属于WindowState中保存的的服务端InputChannel。
于是InputTargets数组有元素就包含了服务端inputChannel。

  • dispatchEventLocked方法解析

1.寻找Connection

Connection代表通向窗体的一条数据链路,获得Connection意味着拿到可写入socket套接字,便可以将事件发布给窗体。
Connection是InputDispatcher内部类,在InputChannel通道注册时创建。

Connection封装了三个重要对象

  • InputChannel :传输通道,Socket套接字通信。
  • InputWindowHandle:Window相关对象。
  • InputPublisher :封装事件的Message,事件发布。
sp<InputChannel> inputChannel; 
sp<InputWindowHandle> inputWindowHandle; 
InputPublisher inputPublisher;

在Connection构造方法中,传入的InputChannel同时交给InputPublisher内部,所以InputPublisher内也包含inputChannel。
Connection构造方法代码片段:inputPublisher(inputChannel)

遍历inputTargets,其中inputTargets中有一个InputTarget包触屏通道
内部包含sp<InputChannel> inputChannel
寻找Connection

for (size_t i = 0; i < inputTargets.size(); i++) {
  const InputTarget& inputTarget = inputTargets.itemAt(i);
  ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
  if (connectionIndex >= 0) {//说明找到Connection的位置
    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
    prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
  }
}

getConnectionIndexLocked获取connectionIndex,connectionIndex是mConnectionsByFd中Fd的索引值,根据connectionIndex获取Connection。

ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
    ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
    if (connectionIndex >= 0) {
        sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
        if (connection->inputChannel.get() == inputChannel.get()) {
            return connectionIndex;
        }
    }
    return -1;
}

根据InputChannel的Fd在mConnectionsByFd中查找Connection,Connection也保存了当时传入的服务端InputChannel,而InputTarget的InputChannel是InputWindowHandle交给的,保证这两个通道相同,才能通过正确的链路传给窗体。

mConnectionsByFd保存socket句柄与Connection的Map
KeyedVector<int, sp<Connection> > mConnectionsByFd;

系统创建每一个窗体,均会为其注册一个服务端InputChannel,创建链路Connection并建立InputChannel的Fd与Connection的关系存储Map表。

依次走到3个方法派送

  • prepareDispatchCycleLocked
  • enqueueDispatchEntriesLocked
  • startDispatchCycleLocked
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection, 
            EventEntry* eventEntry, const InputTarget* inputTarget) {
    ...
    //
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection, 
            EventEntry* eventEntry, const InputTarget* inputTarget) {
    bool wasEmpty = connection->outboundQueue.isEmpty();

    // If the outbound queue was previously empty, start the dispatch cycle going.
    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}
2.发布事件

在startDispatchCycleLocked中,开始真正的事件发布。

1.Connection的inputPublisher对象是发布者,实现在InputTransport.cpp文件中,可处理发布不同事件,有触摸publishMotionEvent、Key事件publishKeyEvent等。
2.将传入的将事件信息(例motionEntry->deviceId)进一步封装,产生InputMessage对象。
3.依赖服务端InputChannel派送。

startDispatchCycleLocked方法片段

...
status = connection->inputPublisher.publishMotionEvent(dispatchEntry
            ->seq,motionEntry->deviceId, motionEntry->source,..);
...

InputPublisher#publishMotionEvent方法,主要工作是封装InputMessage,利用InputPublisher的InputChannel发送。

status_t InputPublisher::publishMotionEvent(
        uint32_t seq,
        int32_t deviceId,
        ....
        const PointerProperties* pointerProperties,
        const PointerCoords* pointerCoords) {
    ....
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_MOTION;
    msg.body.motion.seq = seq;
    msg.body.motion.deviceId = deviceId;
    msg.body.motion.source = source;
    msg.body.motion.action = action;
    msg.body.motion.flags = flags;
    ...
    msg.body.motion.downTime = downTime;
    msg.body.motion.eventTime = eventTime;
    msg.body.motion.pointerCount = pointerCount;

    return mChannel->sendMessage(&msg);
}

发送对象是InputMessage类型

3.发送

将InputMessage对象写入InputChannel的socket套接字
将数据写入到套接字mFd发送缓冲区。

status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
        //调用Linux send方法
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
}

最终向服务端InputChannel的套接字写入了事件信息InputMessage。

Java层IMS服务创建与注册InputChannel通道

WMS#addWindow方法调用IMS服务提供的InputChannel创建与注册方法。

addWindow代码段:

public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
    .....
    WindowState win = new WindowState(this, session, client, token, 
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, 
                    displayContent);
    ...
    if (outInputChannel != null && (attrs.inputFeatures
                    & LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
        String name = win.makeInputChannelName();
        //创建Java层InputChannel数组
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        //服务端InputChannel
        win.setInputChannel(inputChannels[0]);
        //客户端InputChannel
        inputChannels[1].transferTo(outInputChannel);    
        mInputManager.registerInputChannel(win.mInputChannel, 
                      win.mInputWindowHandle);
    }
    .....
}

服务端InputChannel交给WindowState注册到InputDispatcher。在WindowState中同时交给内部InputWindowHandle。

WindowState#setInputChannel方法
void setInputChannel(InputChannel inputChannel) {
    mInputChannel = inputChannel;
    mInputWindowHandle.inputChannel = inputChannel;
}

客户端InputChannel交给了outInputChannel。outInputChannel是addWindow的入参,即通过mWindowSession.addToDisplay方法传入的mInputChannel,最终保存在ViewRootImpl中。

因此,InputChannel数组的一对InputChannel,一个注册给了InputDispatcher,另一个交给应用程序ViewRootImpl。

在WMS#addWindow方法中

IMS服务提供Java层InputChannel的创建与注册
创建:InputChannel静态方法
InputChannel.openInputChannelPair(inputChannelName)
注册:IMS方法
registerInputChannel(InputChannel ,InputWindowHandle)

  • InputChannel的创建

包括Java层与Native层的InputChannel,各一对,保存数组中。
JNI#nativeOpenInputChannelPair方法

public static InputChannel[] openInputChannelPair(String name) {
    ...
    return nativeOpenInputChannelPair(name)
}

C++代码创建Java层InputChannel对象

  • 构造一个Java层jobjectArray数组对象channelPair,数组元素的类型是gInputChannelClassInfo的clazz。
    初始化值为"android/view/InputChannel",所以创建的Java对象为InputChannel。
  • 创建Java层的InputChannel对象jobject ,将创建的Native层InputChannel封装成NativeInputChannel,指针赋值给Java层mPtr变量。
  • 最后将jobject对象为jobjectArray赋值, 数组channelPair,将其返回给Java层,即InputChannel[]。
  • Java层的InputChannel对象引用Native层NativeInputChannel指针。
JNI#nativeOpenInputChannelPair代码段:

sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
//Native层InputChannel的创建
status_t result = InputChannel::openInputChannelPair(name, serverChannel, 
                clientChannel);

jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
//Java层InputChannel的创建
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));
//两个InputChannel对象设置到数组
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);

return channelPair;//返回Java数组

InputChannel[0]代表服务端通道serverChannelObj。InputChannel[1]代表客户端通道clientChannelObj。

InputChanne#openInputChannelPair方法

status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    //socketpair创建一对套接字描述符,保存在sockets数组中
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        ...失败处理
    }

    //setsockopt设置套接字
    int bufferSize = SOCKET_BUFFER_SIZE;
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    //创建服务InputChannel(C++层)
    String8 serverChannelName = name;
    serverChannelName.append(" (server)");
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    //创建客户端InputChannel(C++层)
    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

C++代码创建Native层InputChannel对象

  • InputChannel代表一个通道,每个InputChannel内部均有一个socket句柄,用于进行socket通信。
  • 创建完毕后,openInputChannelPair的入参serverChannel与clientChannel指针就指向了Native层InputChannel对象
  • InputChannel的注册

IMS服务registerInputChannel负责注册
本质是通过InputDispatcher进行注册,告诉InputDispatcher注册这个通道
JNI#nativeRegisterInputChannel方法

服务端InputChannel事件接收架构图如下:

InputChannel服务端事件接收架构图解.png

服务端InputChannel注册到InputDispatcher

IMS#registerInputChannel方法
public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
  nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}

JNI#nativeRegisterInputChannel方法

nativeRegisterInputChannel代码段:
static void nativeRegisterInputChannel(JNIEnv* env, jclass jlong ptr, jobject inputChannelObj, 
            jobject inputWindowHandleObj, jboolean monitor) {
    //根据ptr指针获取本地NativeInputManager
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    //根据Java层入参InputWindowHandle对象和InputChannel对象,找到对应Native对象
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    ...
    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
    ...
    //调用NativeInputManager的注册通道方法
    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
}

NativeInputManager#registerInputChannel注册方法,实质是触发InputDispatcher的注册方法

mInputManager->getDispatcher()->registerInputChannel(inputChannel, 
                inputWindowHandle, monitor)

InputDispatcher注册registerInputChannel输入通道,主要功能:创建Connection,保存Fd与Connection的Map,保存Fd与Connection的关系表,增加对Fd监听。

InputDispatcher#registerInputChannel代码

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    { //上锁
        AutoMutex _l(mLock);
        .......
        sp<Connection> connection = new Connection(inputChannel, 
                    inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        ....
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } //释放锁

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

InputDispatcher的registerInputChannel注册方法,Looper是InputDispatcher构造方法创建,线程在服务进程中,服务创建过程在Android输入管理InputManager之服务启动文章中参考。handleReceiveCallback是接收事件回调方法,最后唤醒Looper。

注册完成后,InputDispatcher增加一条派发通道。


ViewRootImpl创建窗体输入事件监听器WindowInputEventReceiver

监听的本质:
在客户端应用程序,借助Native层Looper将InputChannel通道的套接字Fd交给底层epoll进行事件流监视,将监视的事件流派发给ViewRootImpl中的树视图。

客户端ViewRootImpl监听触摸事件接收器结构图如下

客户端ViewRootImpl事件接收器架构图解.png

以下代码段是在ViewRootImpl#setView方法中初始化监听器的过程。

//窗体注册到WMS,初始化通道mInputChannel
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
...
//ViewRootImpl中注册事件监听接收器WindowInputEventReceiver
if (mInputChannel != null) {
    if (mInputQueueCallback != null) {
        mInputQueue = new InputQueue();
        mInputQueueCallback.onInputQueueCreated(mInputQueue);
     }
    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
}

WindowInputEventReceiver类继承InputEventReceiver抽象类
InputEventReceiver构造方法

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    //InputChannel与Looper为空异常判断
    ....
    mInputChannel = inputChannel;
    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);
}

JNI#nativeInit负责初始化底层,创建底层NativeInputEventReceiver接收器,mReceiverPtr保存接收器对象指针。

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    //根据java层InputChannel找到对应Native层InputChannel
    sp<InputChannel> inputChannel = android_view_InputChannel_
                    getInputChannel(env,inputChannelObj);
    //根据java层MessageQueue找到对应Native层消息队列
    sp<MessageQueue> messageQueue = android_os_MessageQueue_
                    getMessageQueue(env, messageQueueObj);
    //创建Native层接收器,inputChannel交给mInputConsumer(inputChannel)
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();//初始化
    //返回给Java层指针
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); 
    return reinterpret_cast<jlong>(receiver.get());
}

NativeInputEventReceiver构造方法初始化mInputConsumer,mMessageQueue。InputConsumer封装了InputChannel。
NativeInputEventReceiver#initialize接收器初始化,设置fd事件监听。

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

从InputConsumer的InputChannel中拿到Fd。

//InputChannel的Fd
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

请求Looper提供对fd的监听支持addFd(fd, 0, events, this, NULL)
参数fd代表监听句柄,参数events代表事件类型,值是ALOOPER_EVENT_INPUT即1。这两个参数会写入结构体struct epoll_event eventItem,作为epoll_ctl的入参。
初始化eventItem,events是addFd传入的值ALOOPER_EVENT_INPUT。

int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;//EVENT_INPUT是1
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;//EPOLLOUT是2
memset(eventItem, 0, sizeof(epoll_event)); 
eventItem->events = epollEvents;
eventItem->data.fd = fd;

参数this代表回调对象LooperCallback,是处理Looper事件的回调类,NativeInputEventReceiver继承LooperCallback。
NativeInputEventReceiver实现了底层消息的回调handleEvent方法,当监听的句柄fd发生事件,触发NativeInputEventReceiver#handleEvent方法。
处理事件方法,负责构建Java层的事件实体对象,并回调Java方法。
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data)

NativeInputEventReceiver#handleEvent处理事件消息

事件处理流程图如下所示

NativeInputEventReceiver事件处理流程图.png
  • 事件处理流程

NativeInputEventReceiver#handleEvent方法代码段:
if (events & ALOOPER_EVENT_INPUT) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
    mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
    return status == OK || status == NO_MEMORY ? 1 : 0;
}

NativeInputEventReceiver#consumeEvents消费事件方法
这里分析只针对触摸事件,根据Native层MotionEvent,构造Java层的MotionEvent对象。
Java层对象是在android/view/MotionEvent

NativeInputEventReceiver#consumeEvents代码段:
case AINPUT_EVENT_TYPE_MOTION: {              
    MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
    f ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
        *outConsumedBatch = true;
    }
    //inputEventObj是Java层MotionEvent对象
    inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
    break;
}
...
if (inputEventObj) {
    ...               
    env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent,
                        seq, inputEventObj);
    ...         
} 

通过env的CallVoidMethod方法调Java层方法,gInputEventReceiverClassInfo.clazz对应的类是
android/view/InputEventReceiver。
CallVoidMethod触发Java层InputEventReceiver#dispatchInputEvent方法。入参seq和inputEventObj,inputEventObj是Java层MotionEvent对象。

InputEventReceiver#dispatchInputEvent方法
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event);
}

dispatchInputEvent中调用onInputEvent,WindowInputEventReceiver重写onInputEvent。

WindowInputEventReceiver重写的onInputEvent
@Override
public void onInputEvent(InputEvent event) {
    enqueueInputEvent(event, this, 0, true);
}

因此WindowInputEventReceiver最终调用的方法是ViewRootImpl的enqueueInputEvent,从接收器进入ViewRootImpl,开始View事件责任链处理以及后续View事件传递。
ViewRootImpl#enqueueInputEvent方法

void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
    ....
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
     
    if (processImmediately) {
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}

1:obtainQueuedInputEvent方法获取QueuedInputEvent,mQueuedInputEventPool是QueuedInputEvent对象池的首指针,从mQueuedInputEventPool链表中获取QueuedInputEvent,不存在就新建对象。将MotionEvent、InputEventReceiver和flag设置到QueuedInputEvent中。
2:mPendingInputEventTail是待处理的最后一个节点,如果不是空,将刚刚获取的节点放到最后,如果是空,说明没有待处理的节点,mPendingInputEventHead与mPendingInputEventTail指向当前QueuedInputEvent节点,只有一个节点需要处理。
3:doProcessInputEvents处理,循环遍历QueuedInputEvent链表,mPendingInputEventHead开头,直到mNext指向空。

void doProcessInputEvents() { 
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;//拿到节点
        mPendingInputEventHead = q.mNext;//指向下一个
        if (mPendingInputEventHead == null) {
            mPendingInputEventTail = null;
        }
        q.mNext = null//每个处理节点的mNext置空,mPendingInputEventHead已经指向下一个。
        mPendingInputEventCount -= 1;
        long eventTime = q.mEvent.getEventTimeNano();
        long oldestEventTime = eventTime;
        if (q.mEvent instanceof MotionEvent) {
                MotionEvent me = (MotionEvent)q.mEvent;
                if (me.getHistorySize() > 0) {
                    oldestEventTime = me.getHistoricalEventTimeNano(0);
                }
        }
        mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);

        deliverInputEvent(q);//发送事件
    }
    ....
}

while循环会处理完所有mPendingInputEventHead链表节点,最后mPendingInputEventHead与mPendingInputEventTail均变为null。

deliverInputEvent方法处理一个QueuedInputEvent节点,QueuedInputEvent节点封装了MotionEvent对象。
InputStage责任链处理QueuedInputEvent节点。

InputStage处理链如下图所示

InputStage处理链 .png
  • InputStage分析

责任链设计
链表派送与处理的对象:QueuedInputEvent事件对象

deliver派送事件入口

public final void deliver(QueuedInputEvent q) {
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        forward(q);//有了结束标志就一直向后传
    } else if (shouldDropInputEvent(q)) {//是否放弃事件
        finish(q, false);//false代表未处理状态下的结束,增加结束标志,然后向后传
    } else {
        apply(q, onProcess(q));
    }
}

1:forward(QueuedInputEvent q)方法,触发onDeliverToNext派送给下一个对象,执行链表next节点的deliver派送方法,如果next为空,链式处理结束ViewRootImpl#finishInputEvent方法。
2:QueuedInputEvent有结束标志FLAG_FINISHED时,forward方法派送下一个对象。放弃事件时,finish(QueuedInputEvent q, boolean handled)结束方法,QueuedInputEvent加上FLAG_FINISHED标志,forward方法派送下一个对象。
3:以上都不满足时进入apply,首先onProcess处理,结果传给apply。:

InputStage子类重写onProcess处理方法。
onProcess结果:FORWARD、FINISH_HANDLED、FINISH_NOT_HANDLED

apply(QueuedInputEvent q, int result)方法,根据onProcess结果选择forward方法还是finish方法。

protected void apply(QueuedInputEvent q, int result) {
    if (result == FORWARD) {//自己未处理,向后传
        forward(q);
    } else if (result == FINISH_HANDLED) {//自己节点处理过,加上结束标志,向后传
        finish(q, true);
    } else if (result == FINISH_NOT_HANDLED) {//自己节点未处理成功,加上结束标志,向后传
        finish(q, false);
    } else {
        throw new IllegalArgumentException("Invalid result: " + result);
    }
}

EarlyPostImeInputStage节点处理键盘事件,根据事件类型及其输入源处理,触摸事件派送到方法processPointerEvent(QueuedInputEvent q)中,返回FORWARD,继续传给下一个节点。
ViewPostImeInputStage节点,onProcess方法,根据输入源类型判断,如果是Touch事件派送到processPointerEvent处理,发送MotionEvet到View层次结构,若成功返回FINISH_HANDLED,这样apply时QueuedInputEvent加上结束标志,后续节点便不再处理。

private int processPointerEvent(QueuedInputEvent q) {
    final MotionEvent event = (MotionEvent)q.mEvent;

    mAttachInfo.mUnbufferedDispatchRequested = false;
    boolean handled = mView.dispatchPointerEvent(event);
    .....
    return handled ? FINISH_HANDLED : FORWARD;
}

View#dispatchPointerEvent方法就是Touch事件进入View树形结构的入口方法。


Happy
End
^^

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容