Android 输入系统之InputDispatcher2ViewRootImpl篇----终

本来没打算写这一篇的,因为input event从InputDispatcher到ViewRootImpl涉及到activity的启动流程,这个过程的复杂度不会比input流程简单,但是不分析事件是如何被送到ViewRootImpl实在是不完整。OK,废话不多说,开始分析。

activity的启动流程没准备分析了,这个不是这个系列文章的重点,以这篇文章的分析作为参考:http://www.tuicool.com/articles/yQRrUv

我们注意到这一段:

if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mInputChannel);
这里创建的mInputChannel就是作为一个参数罢了,后面会被另外一个InputChannel覆盖,后面会说到。

很明显,如果没有设置INPUT_FEATURE_NO_INPUT_CHANNEL属性则会创建一个InputChannel,然后调用mWindowSession.addToDisplay()

我们注意到这个方法实现在Session.java中,他直接调用WindowManagerService的addWindow()函数。

addWindow()内容比较长,我们只贴我们需要的代码:

if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                /** M: [ALPS00044207] @{ */
                 try {
                    String name = win.makeInputChannelName();
//如果没有设置INPUT_FEATURE_NO_INPUT_CHANNEL和INPUT_FEATURE_NO_INPUT_CHANNEL属性,则会创建一对InputChannel,为什么是一对呢,其实InputChannel内部就是Linux的socketpair,再说通俗点,就是一对全双工的管道,在任何一端写,另外一端都可以读到,当然和INotify一样,也是需要主动读的。Linux中跨进程通信这个还是比较好用的
 //setup 1                  
 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                    win.setInputChannel(inputChannels[0]);
                    inputChannels[1].transferTo(outInputChannel);
//setup 2
                    mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
                } catch (RuntimeException e) {
                    Slog.w(TAG,"handle Input channel erorr", e);
                    return WindowManagerGlobal.ADD_INPUTCHANNEL_NOT_ALLOWED;
                }
                /** @} */
            }

.............
.............
//setup 3
mInputMonitor.updateInputWindowsLw(false /*force*/);

 setup 1,2,3是我们需要关注的。 

先看看openInputChannelPair()实现:

public static InputChannel[] openInputChannelPair(String name) {
        if (name == null) {
            throw new IllegalArgumentException("name must not be null");
        }

        if (DEBUG) {
            Slog.d(TAG, "Opening input channel pair '" + name + "'");
        }
        return nativeOpenInputChannelPair(name);
    }
直接调用 nativeOpenInputChannelPair(name),看看其实现:

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    String8 name(nameChars);
    env->ReleaseStringUTFChars(nameObj, nameChars);

    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    if (result) {
        String8 message;
        message.appendFormat("Could not open input channel pair.  status=%d", result);
        jniThrowRuntimeException(env, message.string());
        return NULL;
    }

    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}
继续看看 InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.string(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }

    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));

    String8 serverChannelName = name;
    serverChannelName.append(" (server)");
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}
到这里就印证了之前的说法,InputChannel对其实就是一对 socketpair,用来跨进程通信的,后面还设置了socketpair的缓冲区大小,然后将socketpair两端的 sockets[2]分别封装成两个 InputChannel,一个server,一个client,对于socketpair而言这两端是完全对等的,因为前面已经说过,在任何一端写入数据,另外一端都能读到。
native成生成一对InputChannel后返回给Java了。

win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
然后一端给到WindowState,另外一端给到app端,覆盖之前app传过来的mInputChannel。
现在看setup 2:
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
其实现在InputManagerService.java:

 
public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null.");
        }
        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    }
把服务端的InputChannel传入 nativeRegisterInputChannel()中,我们看看其实现:
 
static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
        jint ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        throwInputChannelNotInitialized(env);
        return;
    }

    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);

    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
    if (status) {
        String8 message;
        message.appendFormat("Failed to register input channel.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return;
    }

    if (! monitor) {
        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                handleInputChannelDisposed, im);
    }
}
我们只要关注 status_t status = im->registerInputChannel( env, inputChannel, inputWindowHandle, monitor);就行了,其他部分都是对象在Java层和native层都相互转化和错误处理,不影响我们分析。
 
im是NativeInputManager:
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
        const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);
}
最终是调用InputDispatcher的同名函数:
 
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATION
    ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
            toString(monitor));
#endif

    { // acquire lock
        AutoMutex _l(mLock);
//如果该inputchannel已经被保存,则返回
        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().string());
            return BAD_VALUE;
        }
//将inputchannel封装成Connection,其中保存了InputChannel,InputPublisher(用来往InputChannel中写数据的),inputWondwHandle(用来保存窗口信息,比如说是否有焦点)看到这里,大家可能已经开始迷糊了,封装太多了,如果看到这里忘记了前面的内容,很简单,再返回去看就好了,多看几遍就明白了,跟着别人的思路走就容易迷失自己的思路,看别人的代码分析一定要一边看一边想,形成自己的思路
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
//其实InputChannel中最重要的东西也就是这个fd了,要传送数据直接写它就行了。
        int fd = inputChannel->getFd();
//添加到mConnectionsByFd中
        mConnectionsByFd.add(fd, connection);
        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
//将server端的fd加入looper中监听,一旦client端有写入数据,handleReceiveCallback就会被回调,关于addFd后面会经常用到,第四个参数是用来传回调函数的,大家注意一下
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
//说明窗口情况有变化,需要唤醒InputDispatcher马上工作
    mLooper->wake();
    return OK;
}
走完setup 2,服务端的管道已经成功的传递到InputDispatcher中了,InputDispatcher只要向对应的fd中写东西,app端的InputChannel就可以成功读取到数据,跨进程通信已经搭建好了。
 
继续看setup 3:mInputMonitor.updateInputWindowsLw(false /*force*/);
final int numDisplays = mService.mDisplayContents.size();
        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
            WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                final WindowState child = windows.get(winNdx);
                final InputChannel inputChannel = child.mInputChannel;
                final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
                if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
                    // Skip this window because it cannot possibly receive input.
                    continue;
                }

                final int flags = child.mAttrs.flags;
                final int privateFlags = child.mAttrs.privateFlags;
                final int type = child.mAttrs.type;

                final boolean hasFocus = (child == mInputFocus);
                final boolean isVisible = child.isVisibleLw();
                final boolean hasWallpaper = (child == mService.mWallpaperTarget)
                        && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
                final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);

                // If there's a drag in progress and 'child' is a potential drop target,
                // make sure it's been told about the drag
                if (inDrag && isVisible && onDefaultDisplay) {
                    mService.mDragState.sendDragStartedIfNeededLw(child);
                }

                if (universeBackground != null && !addedUniverse
                        && child.mBaseLayer < aboveUniverseLayer && onDefaultDisplay) {
                    final WindowState u = universeBackground.mWin;
                    if (u.mInputChannel != null && u.mInputWindowHandle != null) {
                        addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags,
                                u.mAttrs.privateFlags, u.mAttrs.type,
                                true, u == mInputFocus, false);
                    }
                    addedUniverse = true;
                }

                if (child.mWinAnimator != universeBackground) {
                    addInputWindowHandleLw(inputWindowHandle, child, flags, privateFlags, type,
                            isVisible, hasFocus, hasWallpaper);
                }
            }
        }

        // Send windows to native code.
        mService.mInputManager.setInputWindows(mInputWindowHandles);

        // Clear the list in preparation for the next round.
        clearInputWindowHandlesLw();
这里做的工作也比较简单,将所有的窗口的 InputWindowHandle添加到 mInputWindowHandles中,然后调用 mService.mInputManager.setInputWindows(mInputWindowHandles);
 
继续关注>mService.mInputManager.setInputWindows(mInputWindowHandles);</span>
直接调用InputManagerService.java的nativeSetInputWindows(mPtr, windowHandles);</span>
nativeSetInputWindows(mPtr, windowHandles);实现:
static void nativeSetInputWindows(JNIEnv* env, jclass clazz,
        jint ptr, jobjectArray windowHandleObjArray) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    im->setInputWindows(env, windowHandleObjArray);
}
顺便提一下传进来的mPtr,大家可以自行去看其实如何初始化的,它保存在Java层,大家可以认为代表的native层NativeInputManager对象,通过reinterpret_cast方式,<pre name="code" class="java">后面很多分析都有XXXPtr的,原理都是一模一样,<span style="font-family: Arial, Helvetica, sans-serif;">貌似这玩意比较厉害,什么东西都可以转化为int形式保存,看起来比Java层的序列化还要厉害,各位知道其原理的可以留言告诉我。</span>
 
和前面很类似im->setInputWindows(env, windowHandleObjArray);核心也是调用InputDispatcher的同名函数:
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
#if DEBUG_FOCUS
    ALOGD("setInputWindows");
#endif
    { // acquire lock
        AutoMutex _l(mLock);

        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
        mWindowHandles = inputWindowHandles;

        sp<InputWindowHandle> newFocusedWindowHandle;
        bool foundHoveredWindow = false;
        for (size_t i = 0; i < mWindowHandles.size(); i++) {
            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
                mWindowHandles.removeAt(i--);
                continue;
            }
            if (windowHandle->getInfo()->hasFocus) {
                newFocusedWindowHandle = windowHandle;
            }
            if (windowHandle == mLastHoverWindowHandle) {
                foundHoveredWindow = true;
            }
        }

        if (!foundHoveredWindow) {
            mLastHoverWindowHandle = NULL;
        }

        if (mFocusedWindowHandle != newFocusedWindowHandle) {
            if (mFocusedWindowHandle != NULL) {
#if DEBUG_FOCUS
                ALOGD("Focus left window: %s",
                        mFocusedWindowHandle->getName().string());
#endif
                sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
                if (focusedInputChannel != NULL) {
                    CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                            "focus left window");
                    synthesizeCancelationEventsForInputChannelLocked(
                            focusedInputChannel, options);
                }
            }
            if (newFocusedWindowHandle != NULL) {
#if DEBUG_FOCUS
                ALOGD("Focus entered window: %s",
                        newFocusedWindowHandle->getName().string());
#endif
            }
            mFocusedWindowHandle = newFocusedWindowHandle;
        }

        for (size_t i = 0; i < mTouchState.windows.size(); i++) {
            TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i);
            if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
#if DEBUG_FOCUS
                ALOGD("Touched window was removed: %s",
                        touchedWindow.windowHandle->getName().string());
#endif
                sp<InputChannel> touchedInputChannel =
                        touchedWindow.windowHandle->getInputChannel();
                if (touchedInputChannel != NULL) {
                    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                            "touched window was removed");
                    synthesizeCancelationEventsForInputChannelLocked(
                            touchedInputChannel, options);
                }
                mTouchState.windows.removeAt(i--);
            }
        }

        // Release information for windows that are no longer present.
        // This ensures that unused input channels are released promptly.
        // Otherwise, they might stick around until the window handle is destroyed
        // which might not happen until the next GC.
        for (size_t i = 0; i < oldWindowHandles.size(); i++) {
            const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
            if (!hasWindowHandleLocked(oldWindowHandle)) {
#if DEBUG_FOCUS
                ALOGD("Window went away: %s", oldWindowHandle->getName().string());
#endif
                oldWindowHandle->releaseInfo();
            }
        }
    } // release lock

    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}
这段函数主要工作就是需找焦点窗口,保存在mFocusedWindowHandle中,这里为后面InputDispatcher寻找焦点窗口提供了依据。
 
不过这里我有点不明白,最后为什么还要去遍历oldWindowHandles,删除其中更新掉的元素,同样有知道的留言告诉我。
OK,经过这三个步骤,一切准备就绪了,我们接着看上一篇最后的函数。dispatchEventLocked()
分析了这么多,我不想再一步步分析下去,代码模式都差不多,最终dispatchEventLocked实现,最终其会调用:
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ startDispatchCycle",
            connection->getInputChannelName());
#endif

    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
        dispatchEntry->deliveryTime = currentTime;

        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

            // Publish the key event.
//通过inputPublisher.publishKeyEvent将key event发送出去,其最终调用mChannel->sendMessage(&msg),其又调用nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);看到这里应该很明白了,就是向fd中写数据了,这些就是socketpair的工作了,大家看到这里,很多流程可以自己分析了,中间我省略了很多细节部分,大家感兴趣的自己去了解细节部分。
            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
            break;
        }

        case EventEntry::TYPE_MOTION: {
            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);

            PointerCoords scaledCoords[MAX_POINTERS];
            const PointerCoords* usingCoords = motionEntry->pointerCoords;

            // Set the X and Y offset depending on the input source.
            float xOffset, yOffset, scaleFactor;
            if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                    && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
                scaleFactor = dispatchEntry->scaleFactor;
                xOffset = dispatchEntry->xOffset * scaleFactor;
                yOffset = dispatchEntry->yOffset * scaleFactor;
                if (scaleFactor != 1.0f) {
                    for (size_t i = 0; i < motionEntry->pointerCount; i++) {
                        scaledCoords[i] = motionEntry->pointerCoords[i];
                        scaledCoords[i].scale(scaleFactor);
                    }
                    usingCoords = scaledCoords;
                }
            } else {
                xOffset = 0.0f;
                yOffset = 0.0f;
                scaleFactor = 1.0f;

                // We don't want the dispatch target to know.
                if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
                    for (size_t i = 0; i < motionEntry->pointerCount; i++) {
                        scaledCoords[i].clear();
                    }
                    usingCoords = scaledCoords;
                }
            }

            // Publish the motion event.
            status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
                    motionEntry->deviceId, motionEntry->source,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
                    xOffset, yOffset,
                    motionEntry->xPrecision, motionEntry->yPrecision,
                    motionEntry->downTime, motionEntry->eventTime,
                    motionEntry->pointerCount, motionEntry->pointerProperties,
                    usingCoords);
            break;
        }

        default:
            ALOG_ASSERT(false);
            return;
        }

        // Check the result.
        if (status) {
            if (status == WOULD_BLOCK) {
                if (connection->waitQueue.isEmpty()) {
                    ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
                            "This is unexpected because the wait queue is empty, so the pipe "
                            "should be empty and we shouldn't have any problems writing an "
                            "event to it, status=%d", connection->getInputChannelName(), status);
                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                } else {
                    // Pipe is full and we are waiting for the app to finish process some events
                    // before sending more events to it.
#if DEBUG_DISPATCH_CYCLE
                    ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
                            "waiting for the application to catch up",
                            connection->getInputChannelName());
#endif
                    connection->inputPublisherBlocked = true;
                }
            } else {
                ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
                        "status=%d", connection->getInputChannelName(), status);
                abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
            }
            return;
        }

        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.dequeue(dispatchEntry);
        traceOutboundQueueLengthLocked(connection);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        traceWaitQueueLengthLocked(connection);
    }
}
socketpair的server端写数据后,我们说过app层会收到这个数据,我们看看app是如何收到的,其实也就是找找client端的fd谁在监听
 
大家搜索一下ViewRootImpl的setView函数会发现有:mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
client端的InputChannel被传入到WindowInputEventReceiver构造函数中,我们看看其实现。
final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEvent(InputEvent event) {
            /** M: record current key event and motion event to dump input event info for ANR analysis. @{ */
            if (event instanceof KeyEvent) {
                mCurrentKeyEvent = (KeyEvent) event;
                mKeyEventStartTime = System.currentTimeMillis();
                mKeyEventStatus = INPUT_DISPATCH_STATE_STARTED;
            } else {
                mCurrentMotion = (MotionEvent) event;
                mMotionEventStartTime = System.currentTimeMillis();
                mMotionEventStatus = INPUT_DISPATCH_STATE_STARTED;
            }
            /** @} */
//这里没细看,但是我猜测应该是ViewRootImpl准备对输入事件进行分发了,关于Android事件分发机制基本上是Android软件工程师必考的面试题了,我app写的很少,对这个至今没很明白。。。。。。。。
            enqueueInputEvent(event, this, 0, true);
        }

        @Override
        public void onBatchedInputEventPending() {
            if (DEBUG_INPUT || DEBUG_KEY || DEBUG_MOTION) {
                Xlog.v(TAG, "onBatchedInputEventPending: this = " + this);
            }

            /// M : do not wait for vsync for move event to improve response time
            if (MOVE_RESPONSE_ENHANCE) {
                scheduleConsumeBatchedInputByHandler();
            } else {
                scheduleConsumeBatchedInput();
            }
        }

        @Override
        public void dispose() {
            unscheduleConsumeBatchedInput();
            super.dispose();
        }
    }
WindowInputEventReceiver继承自InputEventReceiver,看看其构造函数:
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null");
        }
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }

        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);

        mCloseGuard.open("dispose");
    }
client端的InputChannel被传递到 nativeInit中( mReceiverPtr 和前面分析的ptr一样,Java层用来保存native层对应对象的引用
 
看看nativeInit():
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        jniThrowRuntimeException(env, "InputChannel is not initialized.");
        return 0;
    }

    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
    if (status) {
        String8 message;
        message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }

    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jint>(receiver.get());
}
这些代码和前面的思路基本上一模一样了,我们只关注initialize():
status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}
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);
        }
    }
}
到这里就应该非常清楚了吧,client端的fd也被加入到looper中监听,一旦server端有数据过来, 便会调用回调函数,值得一提的是对应的回调函数handleEvent,至于为什么大家需要看看 addFd()的实现。 handleEvent会最终调用前面提到的 WindowInputEventReceiver中的 onInputEvent(),然后ViewRootImpl便会开始分发数据了
 
OK,到这里整个input流程都完结了,从按下按键到最终分发到app中,其中的道路不可谓不曲折。。。。。。











  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值