[Android] 輸入系統(二)


在上一篇文章的最后,我們發現InputDispatcher是調用了InputChannel->sendMessage把鍵值發送出去,那么相應的,也有接收鍵值的地方。接收函數是InputChannel->receiveMessage。

在InputConsumer::consume內找到了receiveMessage,從類名能看出來發送端與接收端相當於生產者與消費者的關系。

status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
            // Receive a fresh message.
            status_t result = mChannel->receiveMessage(&mMsg);
}

 

receiveMessage內調用的是socket的接收函數recv

status_t InputChannel::receiveMessage(InputMessage* msg) {
    do {
        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
    } while (nRead == -1 && errno == EINTR);
}

 

 

事件接收端NativeInputEventReceiver

那么究竟是誰來消費這些事件呢,我們在NativeInputEventReceiver里面找到了答案。

在NativeInputEventReceiver內有個事件處理函數handleEvent,該函數是looperCallback的虛函數,NativeInputEventReceiver作為looperCallback的子類,自然有義務實現handleEvent這個函數。handleEvent就可以監聽I/O事件。一旦有I/O事件,如上述的socket send事件,handleEvent就會被啟動,進行后續的處理。

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
}

 

既然有LooperCallback(NativeInputEventReceiver),必然會有Looper。雖然Looper不是本篇文章的研究對象,但是我們有必要理清下面的問題:

  • 究竟與NativeInputEventReceiver對應的這個Looper是什么?
  • 這個Looper是怎樣與LooperCallback關聯起來的呢?

   

實際上,一切起始於ViewRootImpl的setView方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
    //在這里傳入了當前線程的Looper
    new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
...
}

 

InputEventReceiver作為WindowInputEventReceiver的子類,會一起被創建出來。在InputEventReceiver的構造方法中,會調用native方法nativeInit

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);
}

 

在NativeInputEventReceiver的nativeInit方法中,創建了NativeInputEventReceiver對象,並調用它的initialize方法

static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    ...
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
    ...
}

 

initialize方法只做了一件事,就是把NativeInputEventReceiver與Looper關聯起來

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

Looper的方法addFd實現了關聯Looper與LooperCallback(NativeInputEventReceiver)的功能,我們先來分析一下傳給addFd的參數

  • fd,fd即inputChannel的socket fd,Looper會偵測該fd的狀態
  • events,即傳入的ALOOPER_EVENT_INPUT,只有fd的狀態是INPUT的時候才會觸發調用LooperCallback中的handleEvent方法
  • this,即NativeInputEventReceiver,當fd狀態為Input時,NativeInputEventReceiver中的handleEvent方法會被調用

 

 

在consumeEvents內,我們能看到調用了InputConsume::consume來接收InputDispatcher發送過來的事件

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    for (;;) {
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        }
}

 

 

輸入事件在consumeEvents內將會被處理完成,其中包含了四個主要步驟:

  1. 獲取輸入事件
  2. 把輸入事件轉換成java也能處理的格式
  3. 輸入事件分發到相應窗口去處理
  4. 處理結果反饋

 

 

1. 獲取輸入事件已在上面闡述過

 

2. 輸入事件轉換

以Key為例,輸入事件只是把事件內部的成員拆分,然后通過JNI調用java的構造函數來生成相應的java event對象,后面的事件處理都在java層

            jobject inputEventObj;
            switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:
                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast<KeyEvent*>(inputEvent));
                break;






// ----------------------------------------------------------------------------

jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) {
    jobject eventObj = env->CallStaticObjectMethod(gKeyEventClassInfo.clazz,
            gKeyEventClassInfo.obtain,
            nanoseconds_to_milliseconds(event->getDownTime()),
            nanoseconds_to_milliseconds(event->getEventTime()),
            event->getAction(),
            event->getKeyCode(),
            event->getRepeatCount(),
            event->getMetaState(),
            event->getDeviceId(),
            event->getScanCode(),
            event->getFlags(),
            event->getSource(),
            NULL);
    if (env->ExceptionCheck()) {
        ALOGE("An exception occurred while obtaining a key event.");
        LOGE_EX(env);
        env->ExceptionClear();
        return NULL;
    }
    return eventObj;
}


    public static KeyEvent obtain(long downTime, long eventTime, int action,
                    int code, int repeat, int metaState,
                    int deviceId, int scancode, int flags, int source, String characters) {
        KeyEvent ev = obtain();
        ev.mDownTime = downTime;
        ev.mEventTime = eventTime;
        ev.mAction = action;
        ev.mKeyCode = code;
        ev.mRepeatCount = repeat;
        ev.mMetaState = metaState;
        ev.mDeviceId = deviceId;
        ev.mScanCode = scancode;
        ev.mFlags = flags;
        ev.mSource = source;
        ev.mCharacters = characters;
        return ev;
    }

 

 

 

3.輸入事件分發

這里是在java層的事件分發,最終目的是為了調用到窗口的onTouch這類回調函數。

                env->CallVoidMethod(receiverObj.get(),
                       gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);

 

 

還記得上面InputEventReceiver初始化時的流程嗎?是通過setView--->new WindowInputEventReceiver--->new InputEventReceiver--->new NativeInputEventReceiver這樣一步一步創建的。

通過上述的JNI調用,會調用到WindowInputEventReceiver的dispatchInputEvent方法,不過由於WindowInputEventReceiver並沒有自己實現這個方法,因此會調用父類InputEventReceiver::dispatchInputEvent,內部會真正調用到WindowInputEventReceiver::onInputEvent

    public void dispatchInputEvent(InputEvent event) {
        onInputEvent(event);
    }

 

在onInputEvent內,轉到了ViewRootImpl這邊進行處理

public void onInputEvent(InputEvent event) {
    enqueueInputEvent(event, this, 0, true);
}
  
void enqueueInputEvent(InputEvent event,
    InputEventReceiver receiver, int flags, boolean processImmediately) {
    doProcessInputEvents();
}

 

由於事件隊列內會包含多個事件,因此在doProcessInputEvent時,需要分別對所有的事件都進行分發

    void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

            mPendingInputEventCount -= 1;

            deliverInputEvent(q);
        }
    }

 

deliverInputEvent會調用到InputState的deliver方法

        public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                apply(q, onProcess(q));
            }
        }

由於一開始我們的事件還沒有完成,因此不會帶上FLAG_FINISHED,而且我們的事件時一般事件,並不會被丟棄,因此會走apply分支。

 

 

首先會調用onProcess處理事件

        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                // If delivering a new non-key event, make sure the window is
                // now allowed to start updating.
                handleDispatchDoneAnimating();
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }

 

 

以Key為例,我們會調用到processKeyEvent

        private int processKeyEvent(QueuedInputEvent q) {

            // Deliver the key to the view hierarchy.
            if (mView.dispatchKeyEvent(event)) {
                return FINISH_HANDLED;
            }
        }

 

然后調用了View類的dispatchKeyEvent方法,最終會調用到onKey這個回調函數

    public boolean dispatchKeyEvent(KeyEvent event) {

        // Give any attached key listener a first crack at the event.
        //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
            return true;
        }
    }

 

 

4. 處理結果反饋

然后還剩下apply這個方法需要分析。如果onProcess正常處理完成后,會返回FINISH_HANDLED,否則返回FINISHED_NOT_NHANDLED。

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


        protected void finish(QueuedInputEvent q, boolean handled) {
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
            if (handled) {
                q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
            }
            forward(q);
        }


        protected void forward(QueuedInputEvent q) {
            onDeliverToNext(q);
        }


        protected void onDeliverToNext(QueuedInputEvent q) {
            if (mNext != null) {
                mNext.deliver(q);
            } else {
                finishInputEvent(q);
            }
        }


    private void finishInputEvent(QueuedInputEvent q) {
        if (q.mReceiver != null) {
            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
            q.mReceiver.finishInputEvent(q.mEvent, handled);
        } else {
            q.mEvent.recycleIfNeededAfterDispatch();
        }

        recycleQueuedInputEvent(q);
    }

     

 

 

mReceiver.finishInputEvent就是NativeInputEvent的finishInputEvent

status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {

    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
}


status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
        while (!status && chainIndex-- > 0) {
            status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
        }
}


status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_FINISHED;
    msg.body.finished.seq = seq;
    msg.body.finished.handled = handled;
    return mChannel->sendMessage(&msg);
}

最后也是調用sendMessage把消息反饋給InputDispatcher。

到這里,上層的處理已經完成,接下來就是InputDispatcher的反饋處理。

 

 

InputDispatcher反饋處理

反饋處理在handleReceiveCallback中進行,其中包含兩個部分:

  1. 接收反饋消息
  2. 處理反饋消息
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
            for (;;) {
                uint32_t seq;
                bool handled;
                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
                if (status) {
                    break;
                }
                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
                gotOne = true;
            }
}

 

 

1. 接收反饋消息

接收反饋消息是調用的inputPublisher的receiveFinishedSignal方法,內部還是調用了mChannel->receiveMessage

status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {

    status_t result = mChannel->receiveMessage(&msg);

}

 

 

2. 處理反饋消息

處理反饋消息是調用了finishDispatchCycleLocked。

void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, uint32_t seq, bool handled) {

    // Notify other system components and prepare to start the next dispatch cycle.
    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
}

 

void InputDispatcher::onDispatchCycleFinishedLocked(
        nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);

}

 

 

postCommandLocked其實也是發送消息給InputDispatcherThread,那么在分發線程下一次處理消息的時候會首先處理doDispatchCycleFinishedLockedInterruptible。

doDispatchCycleFinishedLockedInterruptible是實際上反饋進行處理的地方,其中包含了下面幾個處理步驟:

  1. 從waitQueue中取出所反饋的事件
  2. 事件是否處理超時,如果是則做超時處理
  3. 從waitQueue中刪除所反饋的事件
  4. 立刻展開下一次的outboundQueue事件監聽

 

void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
        CommandEntry* commandEntry) {

    // Handle post-event policy actions.
    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);


        if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
            String8 msg;
            msg.appendFormat("Window '%s' spent %0.1fms processing the last input event: ",
                    connection->getWindowName(), eventDuration * 0.000001f);
            dispatchEntry->eventEntry->appendDescription(msg);
            ALOGI("%s", msg.string());
        }

       
        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
            connection->waitQueue.dequeue(dispatchEntry);
        }


        // Start the next dispatch cycle for this connection.
        startDispatchCycleLocked(now(), connection);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM