Android 输入系统之InputDispatcher2ViewRootImpl篇----终
来源:互联网 发布:windows10怎么下载软件 编辑:程序博客网 时间:2024/05/16 09:35
本来没打算写这一篇的,因为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 3mInputMonitor.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中,其中的道路不可谓不曲折。。。。。。
0 0
- Android 输入系统之InputDispatcher2ViewRootImpl篇----终
- Android 输入系统之EventHub篇
- Android 输入系统之InputReader篇
- Android 输入系统之InputDispatcher篇
- Android输入系统之输入路径详解
- Android输入系统之输入路径详解
- Android输入法之输入系统
- Android输入系统之InputChannel
- Android输入系统之App跟输入系统建立联系
- Android输入系统之InputChannel(上)
- Android输入系统之InputChannel(下)
- Android输入系统之InputChannel(上)
- Android输入系统之InputChannel(下)
- Android输入系统之activity_window_decor_view关系
- Android核心分析---inputAndroid输入系统之输入路径详解
- Android核心分析(14)------ Android GWES之输入系统
- Android核心分析(14)------ Android GWES之输入系统
- Android核心分析(13)------ Android GWES之输入系统 .
- '\r' 与 '\n'的区别
- C语言实现链表之双向链表(四)清空链表
- Please ensure that adb is correctly located at……问题解决方案
- JavaScript 数据结构(5):单链表 LinkList
- Race
- Android 输入系统之InputDispatcher2ViewRootImpl篇----终
- 视频马赛克的实时检测
- 多线程问题
- Context Switches上下文切换性能详解
- CUDA 的 Threading:Block 和 Grid 的設定與 Warp
- iOS对文件的操作(沙盒)
- org.hibernate.hql.internal.ast.ErrorCounter reportError
- 黑马程序员——C基础——关键字、常量、变量、运算符
- lintCode刷题--最长上升连续子序列