【android 9】【input】【10.发送按键事件4——View的分发流程】

news2024/9/30 17:32:49

系列文章目录

可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm=1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm=1001.2014.3001.5501


目录

系列文章目录

1.简介

1.1 APP程序是如何接受普通按键事件?

1.1.1 App端代码案例

1.1.2 测试结果演示

2.流程分析

2.1 主要步骤

2.2 时序图

2.3 源码分析

 2.3.1 Looper::addFd

2.3.2 pollInner

 2.3.3 NativeInputEventReceiver::handleEvent

2.3.4  NativeInputEventReceiver::consumeEvents

2.3.5 InputConsumer::consume

2.3.6 dispatchInputEvent

2.3.7 onInputEvent

2.3.8 enqueueInputEvent

2.3.9 doProcessInputEvents

2.3.10 deliverInputEvent

2.3.11 InputStage

2.3.12 InputStage.deliver

 2.3.13 InputStage.apply

2.3.14 NativePreImeInputStage.onProcess

2.3.15 ViewPreImeInputStage.onProcess

2.3.16 ImeInputStage.onProcess

2.3.17 EarlyPostImeInputStage.onProcess

2.3.18 NativePostImeInputStage.onProcess

2.3.19 ViewPostImeInputStage.onProcess

2.3.20 ViewPostImeInputStage.processKeyEvent

2.4 View分发流程源码分析

2.4.1 DecorView.dispatchKeyEvent

2.4.1.1 情况一:当前应用存在焦点窗口时处理流程分析

2.4.1.1.1 Activity.dispatchKeyEvent

2.4.1.1.2 PhoneWindow.superDispatchTouchEvent

 2.4.1.1.3.DecorView.superDispatchTouchEvent

2.4.1.1.4 ViewGroup.dispatchKeyEvent

 2.4.1.1.5 View.dispatchKeyEvent

2.4.1.1.6 ViewRootImpl.finish

2.4.1.4.7 deliver

2.4.1.4.8 ViewRootImpl.finishInputEvent

2.4.1.4.9 InputEventReceiver.finishInputEvent

2.4.1.4.10 nativeFinishInputEvent

 2.4.1.4.11 finishInputEvent

2.4.1.4.12 sendFinishedSignal

2.4.1.4.13 sendUnchainedFinishedSignal

2.4.1.4.14 InputChannel::sendMessage

2.4.1.4.15 InputDispatcher::handleReceiveCallback

2.4.1.4.16 InputPublisher::receiveFinishedSignal

2.4.1.4.17 InputChannel::receiveMessage

2.4.1.4.18 finishDispatchCycleLocked

2.4.1.4.19 runCommandsLockedInterruptible

2.4.1.4.20 doDispatchCycleFinishedLockedInterruptible

2.4.1.2 情况二:当前应用没有焦点窗口,但存在Activity兜底处理的流程分析

2.4.1.2.1 Activity.dispatchKeyEvent

 2.4.1.2.2 keyEvent.dispatch

2.4.1.3 情况三:当前无应用,音量加减交给PhoneWindow处理流程分析

2.4.1.3.1 PhoneWindow.onKeyDown


1.简介

通过前文我们知道了IMS会通过已经注册好的socket发送按键事件到应用程序端,那么本篇便来讲解一下应用程序端的流程。

1.1 APP程序是如何接受普通按键事件?

主要步骤:

1.控件(如activity,View)实现View.OnKeyListener接口,并实现其方法onKey

2.控件调用其setOnKeyListener方法,传入监听按键响应的listener

3.按下按键,此时onKey方法会执行。

1.1.1 App端代码案例

public class MainActivity extends AppCompatActivity implements View.OnKeyListener {
//MainActivity实现View.OnKeyListener

    Button mActivitytest;
    EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initview();
    }
    
    //如果目标 View 没有消费掉此次事件(即返回值为 false)则会调用 Activity 的 onKeyDown或者onKeyup来处理。
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        int keycode = event.getKeyCode();
        mEditText.setText("onKeyDown按键监听到了key ="+keycode+ "按键按下");
        return true;
    }

    public boolean onKeyUp(int keyCode, KeyEvent event)
    {
        int keycode = event.getKeyCode();
        mEditText.setText("onKeyUp按键监听到了key ="+keycode+ "按键抬起");
        return true;
    }

    protected void initview()
    {
        mEditText = (EditText) findViewById(R.id.onkeytest);
        mEditText.setOnKeyListener(this);

    }


    @Override
    //应用程序消费按键事件的地方
    public boolean onKey(View view, int i, KeyEvent keyEvent) {
        int keycode = keyEvent.getKeyCode();
        int action = keyEvent.getAction();
        if(action == 0)
        {
            mEditText.setText("onKey按键监听到了key ="+keycode+ "按键按下");

        }else if(action == 1)
        {
            mEditText.setText("onKey按键监听到了key ="+keycode+ "按键抬起");

        }

        return true;
    }
}

 布局代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">



    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <EditText
            android:id="@+id/onkeytest"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textSize="50dp"/>

            <Button
                android:id="@+id/Activitytest"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="开始测试"
                android:layout_weight="1"
                android:textSize="50dp"/>

    </LinearLayout>
</LinearLayout>

1.1.2 测试结果演示

1.当我们只打开应用(重写了Activity的onKeyDown和onKeyup函数),而不点击EditText的区域时候,即EditText此时不是焦点,当按下音量减少按键,结果如下。即此时走的是Activity的兜底的onKeyDown和onKeyup函数。

 2.当我们只打开应用,然后点击EditText的区域时候,即EditText此时是焦点,当按下音量减少按键,结果如下。即此时走的是EditText的onKey函数。

 3.当我们不打开应用或者不重写Activity的onKeyDown和onKeyup函数时,按下音量减少按键。如图:

2.流程分析

2.1 主要步骤

第一步:当IMS通过socket发送按键事件后,应用端的NativeInputEventReceiver::handleEvent会被调用。

第二步:然后应用端从socket下读取按键事件,然后应用端会调用WindowInputEventReceiver(ViewRootImpl 的内部类)重写的 onInputEvent 方法,此方法就是 java 层事件分发的起点。

第三步:应用端会通过InputStage责任链的方式,将按键事件进行派发,最终会派发到ViewPostImeInputStage.processKeyEvent,这个便是View派发的起点。

第四步:如果有焦点窗口,则View会派发按键事件到焦点窗口的onkey函数,如果无焦点窗口,但重写了Activity的onKeyDown和onKeyup函数,则会交给Activity的onKeyDown和onKeyup函数处理,如果当前无Activity或者不重写Activity的onKeyDown和onKeyup函数时,按下音量减少按键,此时会交给meida服务去减少音量。

第五步:当第四步无论是那种情况,处理完成后都会调用调用InputEventReceiver的finishInputEvent函数通过socket向IMS服务发送此按键事件已经完成处理的消息。此时IMS服务侧的handleReceiveCallback回调函数会被执行。

第六步:IMS服务侧的handleReceiveCallback回调函数执行中会从IMS服务端的socket下读取应用端发送的消息,然后将处理完成的事件从等待队列中删除。

2.2 时序图

 (可保存到本地放大观看)

2.3 源码分析

在【android 9】【input】【8.发送按键事件2——InputDispatcher线程】篇中,我们知道当发送普通按键时,IMS最终会通过socket将按键消息发送给应用端,而在【android 9】【input】【9.发送按键事件3——Inputchannel的创建过程】篇中,我们知道应用端会监听来自IMS的消息。

 2.3.1 Looper::addFd

上文中我们知道此函数的主要作用为调用epoll_ctl监听此应用的socket,即监听来自IMS的输入事件。

int Looper::addFd(int fd, int ident, int events,
        const sp<LooperCallback>& callback, void* data) {
    {
        if (!callback.get()) {
            if (! mAllowNonCallbacks) {
                ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
                return -1;
            }

            if (ident < 0) {
                ALOGE("Invalid attempt to set NULL callback with ident < 0.");
                return -1;
            }
        } else {
            ident = POLL_CALLBACK;
        }
        AutoMutex _l(mLock);
        Request request;
        request.fd = fd;
        request.ident = ident;
        request.events = events;
        request.seq = mNextRequestSeq++;
        request.callback = callback; // 是指 NativeInputEventReceiver
        request.data = data;
        if (mNextRequestSeq == -1) mNextRequestSeq = 0;
        
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);

        ssize_t requestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            // 通过 epoll 监听 fd
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            ......
            mRequests.add(fd, request); // 该fd 的 request 加入到 mRequests 队列
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            ......
            mRequests.replaceValueAt(requestIndex, request);
        }
    } 
    return 1;
}

2.3.2 pollInner

Looper机制中线程循环调用pollInner函数。(后续本人会分析一篇关于Looper的机制)

int Looper::pollInner(int timeoutMillis)
{
    int result = POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;
    mPolling = true;

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);//当无消息的时候,会阻塞在此处

    
    //处理所有消息
    for (int i = 0; i < eventCount; i++)
    {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd.get())
        {
        }
        else
        {
            ssize_t requestIndex = mRequests.indexOfKey(fd);//此时mRequests不为空,因为在监听的时候添加了socket的fd和对应的回调对象,
			//此时根据socket中的fd找到对应的回调对象。
            if (requestIndex >= 0)
            {
                int events = 0;
                if (epollEvents & EPOLLIN)
                    events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT)
                    events |= EVENT_OUTPUT;
                if (epollEvents & EPOLLERR)
                    events |= EVENT_ERROR;
                if (epollEvents & EPOLLHUP)
                    events |= EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));//往mResponses塞入了事件
            }
        }
    }
Done:;


    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++)
    {
        Response &response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK)//在addFd中ident就是POLL_CALLBACK
        {
            int fd = response.request.fd;
            int events = response.events;
            void *data = response.request.data;

            int callbackResult = response.request.callback->handleEvent(fd, events, data);//执行回调函数
            if (callbackResult == 0)
            {
                removeFd(fd, response.request.seq);
            }

            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
    }
    return result;
}
void Looper::pushResponse(int events, const Request& request) {
    Response response;
    response.events = events;
    response.request = request;
    mResponses.push(response);
}

 2.3.3 NativeInputEventReceiver::handleEvent

从上面我们知道,当应用端监听的socket下有输入事件时,此函数会被调用。

主要作用是:

1.应用端调用consumeEvents消费事件数据。

此函数的ALOOPER_EVENT_INPUT代表socket可读,即应用程序要开始分发来自IMS的输入事件。

ALOOPER_EVENT_OUTPUT代表socket可写,即应用程序向IMS发送完成输入事件的处理的消息。

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {

    if (events & ALOOPER_EVENT_INPUT) {//socket可读
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");//调用handleReceiveCallback告诉ims此消息已经被成功消费了。
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
	
	/**
    if (events & ALOOPER_EVENT_OUTPUT) {//socket可写
        for (size_t i = 0; i < mFinishQueue.size(); i++) {
            const Finish& finish = mFinishQueue.itemAt(i);
            status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
            if (status) {
                mFinishQueue.removeItemsAt(0, i);

                if (status == WOULD_BLOCK) {
                    if (kDebugDispatchCycle) {
                        ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",
                                getInputChannelName().c_str(), i, mFinishQueue.size());
                    }
                    return 1; // keep the callback, try again later
                }

                ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                        getInputChannelName().c_str(), status);
                if (status != DEAD_OBJECT) {
                    JNIEnv* env = AndroidRuntime::getJNIEnv();
                    String8 message;
                    message.appendFormat("Failed to finish input event.  status=%d", status);
                    jniThrowRuntimeException(env, message.string());
                    mMessageQueue->raiseAndClearException(env, "finishInputEvent");
                }
                return 0; // remove the callback
            }
        }
        if (kDebugDispatchCycle) {
            ALOGD("channel '%s' ~ Sent %zu queued finish events; none left.",
                    getInputChannelName().c_str(), mFinishQueue.size());
        }
        mFinishQueue.clear();
        setFdEvents(ALOOPER_EVENT_INPUT);
        return 1;
    }
	*/


    return 1;
}

2.3.4  NativeInputEventReceiver::consumeEvents

此函数的主要作用是:

1.mInputConsumer的consume()函数从Inputchannel中读取一条InputMessage

2.将c++的inputEvent生成java的事件类型。

3.通过jni调用java层的dispatchInputEvent函数开始分发。

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
			//consumeBatches是 false
			//outConsumedBatch是空


    ScopedLocalRef<jobject> receiverObj(env, NULL);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;
        int32_t displayId;
		//通边mInputConsumer的consume()函数从Inputchannel中读取一条InputMessage。 
        //解析为InputEvent后,通过inputEvent参数传出*/ 
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent, &displayId);
        assert(inputEvent);

        if (!skipCallbacks) {
			/**
            if (!receiverObj.get()) {
                receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
                if (!receiverObj.get()) {
                    ALOGW("channel '%s' ~ Receiver object was finalized "
                            "without being disposed.", getInputChannelName().c_str());
                    return DEAD_OBJECT;
                }
            }
			*/

            jobject inputEventObj;
            switch (inputEvent->getType()) {
            case AINPUT_EVENT_TYPE_KEY:
                inputEventObj = android_view_KeyEvent_fromNative(env,
                        static_cast<KeyEvent*>(inputEvent));//将c++的inputEvent生成java的事件类型
                break;

            default:
                assert(false); // InputConsumer should prevent this from ever happening
                inputEventObj = NULL;
            }

            if (inputEventObj) {//通过jni调用dispatchInputEvent函数开始分发inputEventObj
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
                        displayId);
                if (env->ExceptionCheck()) {
                    ALOGE("Exception dispatching input event.");
                    skipCallbacks = true;
                }
                env->DeleteLocalRef(inputEventObj);
            } else {
                ALOGW("channel '%s' ~ Failed to obtain event object.",getInputChannelName().c_str());
                skipCallbacks = true;
            }
        }

        if (skipCallbacks) {//如果发送异常,则直接发送完成信号
            mInputConsumer.sendFinishedSignal(seq, false);
        }
    }
}

2.3.5 InputConsumer::consume

主要作用是:

1.从socket中读取数据并转化为InputMessage结构体的格式。

status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent,
        int32_t* displayId) {
			
    *outSeq = 0;
    *outEvent = NULL;
    *displayId = -1;  // Invalid display.

    // //获取下一条输入消息。
	//循环,直到可以返回事件或没有接收到其他事件为止。
    while (!*outEvent) {
		/**
        if (mMsgDeferred) {//默认false不执行
		//mMsg包含上一次调用中尚未处理的有效输入消息。
            mMsgDeferred = false;
        }*/
		else {
            // 从inputchannel中接收一个数据
            status_t result = mChannel->receiveMessage(&mMsg);
			/**
            if (result) {//ok是0
                // Consume the next batched event unless batches are being held for later.
                if (consumeBatches || result != WOULD_BLOCK) {
                    result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId);
                    if (*outEvent) {
                        break;
                    }
                }
                return result;
            }
			*/
        }

        switch (mMsg.header.type) {
        case InputMessage::TYPE_KEY: {
            KeyEvent* keyEvent = factory->createKeyEvent();//创建了一个java层的KeyEvent对象
            if (!keyEvent) return NO_MEMORY;

            initializeKeyEvent(keyEvent, &mMsg);//将InputMessage保存的消息,保存到keyEvent中
            *outSeq = mMsg.body.key.seq;
            *outEvent = keyEvent;//赋值为keyEvent,则while循环不为空,会退出
            break;
        }
        default:
            return UNKNOWN_ERROR;
        }
    }
    return OK;
}
status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
		//从socket对应的mFd取出数据,存储在msg中
    } while (nRead == -1 && errno == EINTR);

    if (nRead < 0) {//代表读取错误
        int error = errno;
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.c_str(), errno);
#endif
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (nRead == 0) { // check for EOF
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.c_str());
#endif
        return DEAD_OBJECT;
    }

    if (!msg->isValid(nRead)) {
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ received invalid message", mName.c_str());
#endif
        return BAD_VALUE;
    }

#if DEBUG_CHANNEL_MESSAGES
    ALOGD("channel '%s' ~ received message of type %d", mName.c_str(), msg->header.type);
#endif
    return OK;
}
void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
    event->initialize(
            msg->body.key.deviceId,
            msg->body.key.source,
            msg->body.key.action,
            msg->body.key.flags,
            msg->body.key.keyCode,
            msg->body.key.scanCode,
            msg->body.key.metaState,
            msg->body.key.repeatCount,
            msg->body.key.downTime,
            msg->body.key.eventTime);
}




void KeyEvent::initialize(
        int32_t deviceId,
        int32_t source,
        int32_t action,
        int32_t flags,
        int32_t keyCode,
        int32_t scanCode,
        int32_t metaState,
        int32_t repeatCount,
        nsecs_t downTime,
        nsecs_t eventTime) {
    InputEvent::initialize(deviceId, source);
    mAction = action;
    mFlags = flags;
    mKeyCode = keyCode;
    mScanCode = scanCode;
    mMetaState = metaState;
    mRepeatCount = repeatCount;
    mDownTime = downTime;
    mEventTime = eventTime;
}




void InputEvent::initialize(int32_t deviceId, int32_t source) {
    mDeviceId = deviceId;
    mSource = source;
}

2.3.6 dispatchInputEvent

主要作用为:

1.调用其子类 WindowInputEventReceiver(ViewRootImpl 的内部类)重写的 onInputEvent 方法,此方法就是 java 层事件分发的起点。

//InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event, displayId);
}

2.3.7 onInputEvent

//接着调用其子类 WindowInputEventReceiver(ViewRootImpl 的内部类)重写的 onInputEvent 方法,此方法就是 java 层事件分发的起点。
//ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEvent(InputEvent event, int displayId) {
            enqueueInputEvent(event, this, 0, true);
        }
}

2.3.8 enqueueInputEvent

主要作用为:

1.调用doProcessInputEvents进行事件的分发。

void enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

		//始终按顺序对输入事件进行排队,而不管其时间戳如何。
        QueuedInputEvent last = mPendingInputEventTail;// mPendingInputEventTail 代表 QueuedInputEvent 链表尾节点
        if (last == null) {//如果是首次进入,头尾节点都指向第一个构造的 QueuedInputEvent
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {//否则将事件放入队尾
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);//追踪QueuedInputEvent的长度

        if (processImmediately) {
            doProcessInputEvents();//处理输入事件
        } else {
            scheduleProcessInputEvents();
        }
}
private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,InputEventReceiver receiver, int flags) {
        QueuedInputEvent q = mQueuedInputEventPool;
        if (q != null) {
            mQueuedInputEventPoolSize -= 1;
            mQueuedInputEventPool = q.mNext;
            q.mNext = null;
        } else {
            q = new QueuedInputEvent();
        }

        q.mEvent = event;
        q.mReceiver = receiver;
        q.mFlags = flags;
        return q;
}

2.3.9 doProcessInputEvents

void doProcessInputEvents() {
        // 头节点不为空,代表链表中有事件处理
        while (mPendingInputEventHead != null) {
			//按顺序取出
            QueuedInputEvent q = mPendingInputEventHead;
			//头节点后移
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;//将取出来的 QueuedInputEvent 的 mNext 置空

            mPendingInputEventCount -= 1;//QueuedInputEvent 链表数量减一
            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                    mPendingInputEventCount);

            long eventTime = q.mEvent.getEventTimeNano();//此事件发生的时间
            long oldestEventTime = eventTime;
            if (q.mEvent instanceof MotionEvent) {//如果是触摸事件
                MotionEvent me = (MotionEvent)q.mEvent;//转化为motion事件
                if (me.getHistorySize() > 0) {
                    oldestEventTime = me.getHistoricalEventTimeNano(0);
                }
            }
            mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);//更新时间

            deliverInputEvent(q);//事件分发
        }

			//我们现在已经处理完所有可以处理的输入事件,因此可以立即清除挂起的标志。
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
}

2.3.10 deliverInputEvent

主要作用为:

1.调用InputStage的deliver分发事件。

下面我们需要先介绍一下InputStage类。

private void deliverInputEvent(QueuedInputEvent q) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",q.mEvent.getSequenceNumber());
        mStartCheckTime = SystemClock.elapsedRealtime();

        if (mInputEventConsistencyVerifier != null) {//用于调试信息的
            mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
        }

        InputStage stage;
        if (q.shouldSendToSynthesizer()) {//此时是false
            stage = mSyntheticInputStage;
        } else {
			//shouldSkipIme 代表事件是否传递给输入法,对于按键事件,
            //仅仅在主动设置了 FLAG_DELIVER_POST_IME 时返回 true,
            //触摸事件则是判断其事件来源,一般情况的触摸事件都是返回 true
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;//此时是按键事件所以,nativePreImeStage值是nativePreImeStage
        }

        if (q.mEvent instanceof KeyEvent) {
            mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
        }

        if (stage != null) {
            handleWindowFocusChanged();//焦点窗口如果发生变化,则调整焦点窗口,如果没发生变化。则return
            stage.deliver(q);//分发事件
        } else {
            finishInputEvent(q);
        }
}

2.3.11 InputStage

首先InputStage是一个抽象的父类,此处采用的是职责链设计模式,每一个InputStage的实现类中都含有一个next变量,用于指向下一个InputStage的实现类,就像链表一样。责任链模式是一种行为型设计模式,它允许你构建一个对象链,让请求从链的一端进入,然后依次沿着链传递处理,直到链上的某个对象能够处理该请求。职责链上的每一个处理者都是一个对象,其内部会包含mNext指向下一个对象,这个对象可以对请求进行处理也可以将请求发送给下一个对象去处理。

类图如下:

那么InputStage是什么时候被初始化的呢?

是在setView函数里面会初始化成一个类似于链表的形式。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) 
{
	.......
	mInputChannel = new InputChannel();
	res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
							
	mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
							
	......
	mSyntheticInputStage = new SyntheticInputStage();
    InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
    InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
    InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
    InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
    InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
    InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);

    mFirstInputStage = nativePreImeStage;
    mFirstPostImeInputStage = earlyPostImeStage;

}

初始化后的InputStage的链表形式如下:

所以,如果是按键事件,则分发的起点是NativePreImeInputStage,如果是触摸事件,则分发的起点是EarlyPostImeInputStage。

2.3.12 InputStage.deliver

//InputStage的deliver处理
public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {//q.mFlags是0,所以此处不执行forward,FLAG_FINISHED代表已经处理完成的事件
                forward(q);
            } else if (shouldDropInputEvent(q)) {//是否丢弃事件
                finish(q, false);
            } else {//此处会执行
                apply(q, onProcess(q));//onProcess函数返回的是FORWARD,代表交给下一个处理InputStage
            }
}

 2.3.13 InputStage.apply

主要作用为:

1.如果当前InputStage的返回结果是FORWARD,则调用forward传递给下一个InputStage去处理。如果返回的是FINISH_HANDLED,则调用finish表示处理完成。如果最终此链表所有的都不处理此消息,则返回的是FINISH_NOT_HANDLED。

protected void apply(QueuedInputEvent q, int result) {
            if (result == FORWARD) {//FORWARD代表交给下一个InputStage处理
                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 forward(QueuedInputEvent q) {
            onDeliverToNext(q);
}	



protected void onDeliverToNext(QueuedInputEvent q) {
            if (DEBUG_INPUT_STAGES) {
                Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
            }
            if (mNext != null) {
                mNext.deliver(q);//指向下一个deliver,最终会执行到 ViewPostImeInputStage的onProcess:
            } else {
                finishInputEvent(q);
            }
}

2.3.14 NativePreImeInputStage.onProcess

//NativePreImeInputStage
        protected int onProcess(QueuedInputEvent q) {
			/*
            if (mInputQueue != null && q.mEvent instanceof KeyEvent) {//一般mInputQueue是空的
                mInputQueue.sendInputEvent(q.mEvent, q, true, this);
                return DEFER;
            }*/
            return FORWARD;//FORWARD代表交给下一个阶段去处理
        }

2.3.15 ViewPreImeInputStage.onProcess

此时便传递给了ViewPreImeInputStage阶段。我们看看ViewPreImeInputStage的onprocess的处理。

//ViewPreImeInputStage
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            }
            return FORWARD;
        }
		
		private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;
            if (mView.dispatchKeyEventPreIme(event)) {//在输入法之前消费key,比如:处理back按键来更新应用程序ui界面,而不是允许输入法接受,然后关闭输入法他自己
                return FINISH_HANDLED;
            }
            return FORWARD;
        }
		
		public boolean dispatchKeyEventPreIme(KeyEvent event) {
            return onKeyPreIme(event.getKeyCode(), event);//默认onKeyPreIme返回false,除非有view控件继承自view,然后重写此函数onKeyPreIme
        }

2.3.16 ImeInputStage.onProcess

接下来便传递给了输入法,ImeInputStage。

//ImeInputStage
protected int onProcess(QueuedInputEvent q) {
     if (mLastWasImTarget && !isInLocalFocusMode()) {
                InputMethodManager imm = InputMethodManager.peekInstance();
                if (imm != null) {
                    final InputEvent event = q.mEvent;
                    if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
                    int result = imm.dispatchInputEvent(event, q, this, mHandler);//派发给输入法
                    if (result == InputMethodManager.DISPATCH_HANDLED) {
                        return FINISH_HANDLED;//输入法消费事件完成
                    } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
                        // The IME could not handle it, so skip along to the next InputStage
                        return FORWARD;//发送给下一个阶段
                    } else {
                        return DEFER; // callback will be invoked later
                    }
                }
            }
     return FORWARD;
}

2.3.17 EarlyPostImeInputStage.onProcess

接下来便传递给了EarlyPostImeInputStage。

final class EarlyPostImeInputStage extends InputStage {

        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {//走这里,如果是按键事件
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                }
            }
            return FORWARD;
        }

        private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;

            if (mAttachInfo.mTooltipHost != null) {
                mAttachInfo.mTooltipHost.handleTooltipKey(event);
            }

            // 如果按键的目的是退出触摸模式,那么我们会使用它,并认为它已经处理好了。
            if (checkForLeavingTouchModeAndConsume(event)) {//这里只有键盘的上下左右中方向键和enter按键,
			//tab按键,空格按键,光标移动到开始按键,光标移动到末尾按键和向上翻页按键和向下翻页按键才会返回会true之外,
			//其他所有按键都是fasle
                return FINISH_HANDLED;
            }


            mFallbackEventHandler.preDispatchKeyEvent(event);
            return FORWARD;
        }
}

2.3.18 NativePostImeInputStage.onProcess

final class NativePostImeInputStage extends AsyncInputStage
            implements InputQueue.FinishedInputEventCallback {

        protected int onProcess(QueuedInputEvent q) {
            if (mInputQueue != null) {
                mInputQueue.sendInputEvent(q.mEvent, q, false, this);//将消息传递给NativeActivity,一般mInputQueue为空
                return DEFER;
            }
            return FORWARD;
}

2.3.19 ViewPostImeInputStage.onProcess

最终普通的按键会走到ViewPostImeInputStage去处理。

final class ViewPostImeInputStage extends InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {//如果是按键事件
                return processKeyEvent(q);
            } else {//如果是motion事件
                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);
                }
            }
        }
}

2.3.20 ViewPostImeInputStage.processKeyEvent

//ViewPostImeInputStage.java
private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;

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

mView 指向当前界面的顶层布局 DecorView,注意,此时便是走到了View的分发流程。我们放在2.4进行分析。

2.4 View分发流程源码分析

首先我们需要知道Activity的主要构成,才能梳理View的分发流程。此篇源码本人会另写一篇。此处只说结论。

一个Activity包含一个Window对象,而Window对象是抽象类,这个对象是由PhoneWindow来实现的,PhoneWindow将DecorView作为整个应用窗口的根View,然后这个DecorView又将屏幕划分为两个区域,一个是TitleView,一个是ContentView。ContentView本质是Viewgroup。然后平常我们做的布局就是展示在ContentView中的。

左边为原生的Activity结构图,右边为对应的布局。

其中左边的TitleView和右边的test是对应的。左边contentview就是右边大片空白的地方。

左边为本文测试程序中的Activity结构图,右边为对应的布局。

其中左边的TitleView和右边的Activitytest是对应的。左边的button对应右边的开始测试,左边的EditText对应右边的Activitytest和开始测试中间空白部分。左边contentview就是Activitytest以下的所有地方。

2.4.1 DecorView.dispatchKeyEvent

我们此时接着2.3.20分析。此时会走到DecorView,DecorView是当前界面的顶层布局。

此时存在三种情况:

情况一:当前应用存在焦点窗口,则最红会走到焦点窗口的Onkey函数。

情况二:当前应用不存在焦点窗口,但是重写了Activity的onKeyDown和onKeyUp,此时会走Activity的兜底函数onKeyDown和onKeyUp。

情况三:当前无应用,比如我们打开手机直接按音量加减,则页面会响应音量加减。此时会调用PhoneWindow的onKeyDown和onKeyUp。发送到meida服务,调节音量。

//DecorView.java
    public boolean dispatchKeyEvent(KeyEvent event) {
        final int keyCode = event.getKeyCode();
        final int action = event.getAction();
        final boolean isDown = action == KeyEvent.ACTION_DOWN;

        if (isDown && (event.getRepeatCount() == 0)) {
			/**
            if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
                boolean handled = dispatchKeyShortcutEvent(event);
                if (handled) {
                    return true;
                }
            }


            //如果某个面板处于打开状态,请在没有弦面板键的情况下对其执行快捷方式
            if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
                if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
                    return true;
                }
            }
			*/
        }

        if (!mWindow.isDestroyed()) {
            final Window.Callback cb = mWindow.getCallback();//mWindow是phonewindow
			//Window.Callback 指向当前 Activity,在 Activity 启动时会调用自己的 attach 方法,
			//此方法中会将自己作为 callback 传给 window。
            final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                    : super.dispatchKeyEvent(event);//情况一和二:返回值代表目标 View 对事件的处理结果
			
            if (handled) {
                return true;
            }
        }

        return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
                : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
				//情况三:调用PhoneWindow的onKeyDown,onKeyUp 用于PhoneWindow消费音量加减等按键
}
2.4.1.1 情况一:当前应用存在焦点窗口时处理流程分析

即此时是当我们只打开应用,然后点击EditText的区域时候,会弹出输入法,此时EditText此时是焦点,当按下音量减少按键,结果如下。最终会走向焦点应用的EditText的onKey函数。

2.4.1.1.1 Activity.dispatchKeyEvent
public boolean dispatchKeyEvent(KeyEvent event) {
        onUserInteraction();//空实现

        //让操作栏打开菜单,以响应菜单键的优先级高于处理它的窗口
        final int keyCode = event.getKeyCode();
		/**
        if (keyCode == KeyEvent.KEYCODE_MENU &&//如果是菜单栏
                mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
            return true;
        }
		*/

        Window win = getWindow();
        if (win.superDispatchKeyEvent(event)) {//情况一:接着就会调用 PhoneWindow 的 superDispatchTouchEvent 方法,此处到最后的焦点窗口的onKey函数。
		//其返回值代表目标 View 对事件的处理结果,
            return true;
        }
        View decor = mDecor;//此时decor是DecorView,为根view
        if (decor == null) decor = win.getDecorView();
        return event.dispatch(this, decor != null? decor.getKeyDispatcherState() : null, this);
		//情况二:我们可以看到如果目标 View 没有消费掉此次事件(即返回值为 false)则会调用 Activity 的 onKeyDown或者onKeyup来处理。
}
2.4.1.1.2 PhoneWindow.superDispatchTouchEvent
//PhoneWindow.superDispatchTouchEvent
public boolean superDispatchKeyEvent(KeyEvent event) {
        return mDecor.superDispatchKeyEvent(event);
}
 
 2.4.1.1.3.DecorView.superDispatchTouchEvent

DecorView 继承自 FrameLayout,而FrameLayout继承自ViewGroup,因此此时调用的是ViewGroup的dispatchKeyEvent函数。

//DecorView.superDispatchTouchEvent
public boolean superDispatchKeyEvent(KeyEvent event) {
	
		/*
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {//如果是返回键
            final int action = event.getAction();
            // Back cancels action modes first.
            if (mPrimaryActionMode != null) {
                if (action == KeyEvent.ACTION_UP) {
                    mPrimaryActionMode.finish();
                }
                return true;
            }
        }*/

        if (super.dispatchKeyEvent(event)) {//走这里,调用ViewGroup类的dispatchKeyEvent函数
            return true;
        }
		/*
        return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);//第一种情况不走这里
		*/
}
2.4.1.1.4 ViewGroup.dispatchKeyEvent

因为按键事件只需要派发给焦点窗口即可,所以相比于触摸事件,是比较简单的。触摸事件会有一个链表保存所有View。然后会遍历此View,而按键事件则不需要。

最终会调用到mFocused.dispatchKeyEvent函数。

//ViewGroup.java
//按键事件只需要派发给焦点窗口即可
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 1);//一致性检验
        }

        if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))//PFLAG_FOCUSED代表是焦点视图,PFLAG_HAS_BOUNDS代表已经完成了布局。
		//所以此处的含义是如果当前ViewGroup的视图是焦点视图并且已经完成布局拥有边界
                == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
            if (super.dispatchKeyEvent(event)) {
                return true;
            }
        } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)//如果当前的ViewGroup代表的view不是焦点视图,则寻找到焦点窗口视图
		//调用焦点视图的dispatchKeyEvent方法。
                == PFLAG_HAS_BOUNDS) {
            if (mFocused.dispatchKeyEvent(event)) {
                return true;
            }
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);//一致性检验
        }
        return false;
    }
 2.4.1.1.5 View.dispatchKeyEvent

此时的mListenerInfo是不为空的,因为我们在调用mEditText.setOnKeyListener(this);最终会将listener保存到mListenerInfo.mOnKeyListener中,所以,此时我们就完成了事件的派发。焦点onKey函数得到调用。

//view
//将关键事件分派到焦点路径上的下一个视图。此路径从视图树的顶部向下延伸到当前聚焦的视图。
//如果这个视图有焦点,它将分派给自己。否则,它将沿着焦点路径调度下一个节点。
public boolean dispatchKeyEvent(KeyEvent event) {


        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {//此时mListenerInfo不为空,我们在调用mEditText.setOnKeyListener(this);
				//会将listener保存到li.mOnKeyListener
            return true;
        }
		/*
        if (event.dispatch(this, mAttachInfo != null
                ? mAttachInfo.mKeyDispatchState : null, this)) {
            return true;
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        return false;
	

mEditText.setOnKeyListener(this)的源码分析如下:

//EditText继承自TextView
//TextView继承自View
public void setOnKeyListener(OnKeyListener l) 
{
        getListenerInfo().mOnKeyListener = l;
}


ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
}

此时我们仍然需要注意返回值,此时返回值是ture。

返回值链路为:

View.dispatchKeyEvent------>ViewGroup.dispatchKeyEvent----->DecorView.superDispatchTouchEvent------>PhoneWindow.superDispatchTouchEvent------>

Activity.dispatchKeyEvent------>DecorView.dispatchKeyEvent------>ViewPostImeInputStage.processKeyEvent函数。

最后processKeyEvent函数会返回FINISH_HANDLED。表示已经完成了按键事件的处理。

然后继续返回到了 2.3.13 InputStage.apply函数,此时会执行 finish(q, true)函数。

2.4.1.1.6 ViewRootImpl.finish

此时仍会传递到下一个阶段。此时就剩最后一个阶段了,是SyntheticInputStage。

protected void finish(QueuedInputEvent q, boolean handled) {
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
            if (handled) {//此时会true
                q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
            }
            forward(q);//传递到下一个阶段
}




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



protected void onDeliverToNext(QueuedInputEvent q) {
            if (DEBUG_INPUT_STAGES) {
                Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
            }
            if (mNext != null) {//走这里
                mNext.deliver(q);
            } else {
                finishInputEvent(q);
            }
}
2.4.1.4.7 deliver

主要作用为:

1.SyntheticInputStage发现按键事件已经被消费了,则不再调用apply函数,而是再次传递给下一个,此时发现mNext为空,最终会调用finishInputEvent函数。

public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);//走这里,即当事件以及被消费了,就不用走apply了,直接再传递给下一个
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                apply(q, onProcess(q));
            }
}


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



protected void onDeliverToNext(QueuedInputEvent q) {
            if (DEBUG_INPUT_STAGES) {
                Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
            }
            if (mNext != null) {
                mNext.deliver(q);
            } else {
                finishInputEvent(q);//此时mNext为空,会走这里
            }
}
2.4.1.4.8 ViewRootImpl.finishInputEvent

主要作用为:

1.调用InputEventReceiver的finishInputEvent函数。

//当处理完后会调用 finishInputEvent() 方法结束此次分发。
private void finishInputEvent(QueuedInputEvent q) {

        if (q.mReceiver != null) {
            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;//此时为true
            q.mReceiver.finishInputEvent(q.mEvent, handled);//此时mReceiver是NativeInputEventReceiver
        } else {
            q.mEvent.recycleIfNeededAfterDispatch();
        }

        recycleQueuedInputEvent(q);//从队列中回收InputEvent事件
}
2.4.1.4.9 InputEventReceiver.finishInputEvent

主要作用为:

1.调用nativeFinishInputEvent函数。

public final void finishInputEvent(InputEvent event, boolean handled) {
	/**
        if (event == null) {
            throw new IllegalArgumentException("event must not be null");
        }
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to finish an input event but the input event "
                    + "receiver has already been disposed.");
        }
	*/
		else {
            int index = mSeqMap.indexOfKey(event.getSequenceNumber());//从请求队列中取出之前IMS发送过来的按键事件
			/*
            if (index < 0) {
                Log.w(TAG, "Attempted to finish an input event that is not in progress.");
            } */
			else {
                int seq = mSeqMap.valueAt(index);
                mSeqMap.removeAt(index);//从mSeqMap移除此按键事件
                nativeFinishInputEvent(mReceiverPtr, seq, handled);//向input发送event已完成发送的消息
            }
        }
        event.recycleIfNeededAfterDispatch();//如果是MotionEvent事件,则回收,如果是KeyEvent则不回收
}
2.4.1.4.10 nativeFinishInputEvent

主要作用为:

1.调用c++层的NativeInputEventReceiver对象的finishInputEvent函数。

static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,jint seq, jboolean handled){
    sp<NativeInputEventReceiver> receiver =reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    status_t status = receiver->finishInputEvent(seq, handled);//调用c++层的NativeInputEventReceiver对象的finishInputEvent
	/*
    if (status && status != DEAD_OBJECT) {
        String8 message;
        message.appendFormat("Failed to finish input event.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
    }*/
}
 2.4.1.4.11 finishInputEvent
status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {

    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);//handled是true,seq是IMS发送到应用端的event的消息号,
	//代表发送给IMS告诉此按键event已经完成处理。
	//返回值是0代表成功
	/*
    if (status) {//如果发送失败,则生成finsh放入容器,客户端并设置ALOOPER_EVENT_OUTPUT属性,
	//当下一次IMS分发线程分发下一个按键事件到达应用端的时候,客户端会重新发送此事件的完成事件。
        if (status == WOULD_BLOCK) {
            if (kDebugDispatchCycle) {
                ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
                        "Enqueued for later.", getInputChannelName().c_str());
            }
            Finish finish;
            finish.seq = seq;
            finish.handled = handled;
            mFinishQueue.add(finish);
            if (mFinishQueue.size() == 1) {
                setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
            }
            return OK;
        }
        ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                getInputChannelName().c_str(), status);
    }*/
    return status;
}
2.4.1.4.12 sendFinishedSignal

1.调用sendUnchainedFinishedSignal函数。

status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
	/**
    if (!seq) {
        ALOGE("Attempted to send a finished signal with sequence number 0.");
        return BAD_VALUE;
    }

    // Send finished signals for the batch sequence chain first.
    size_t seqChainCount = mSeqChains.size();//首先发送批次序列链的完成信号。主要用于motion事件
    if (seqChainCount) {
        uint32_t currentSeq = seq;
        uint32_t chainSeqs[seqChainCount];
        size_t chainIndex = 0;
		
        for (size_t i = seqChainCount; i > 0; ) {
             i--;
             const SeqChain& seqChain = mSeqChains.itemAt(i);
             if (seqChain.seq == currentSeq) {
                 currentSeq = seqChain.chain;
                 chainSeqs[chainIndex++] = currentSeq;
                 mSeqChains.removeAt(i);
             }
        }
		
        status_t status = OK;
		
        while (!status && chainIndex > 0) {
            chainIndex--;
            status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
        }
		
        if (status) {
            // An error occurred so at least one signal was not sent, reconstruct the chain.
            for (;;) {
                SeqChain seqChain;
                seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
                seqChain.chain = chainSeqs[chainIndex];
                mSeqChains.push(seqChain);
                if (!chainIndex) break;
                chainIndex--;
            }
            return status;
        }
    }
	*/

    // Send finished signal for the last message in the batch.
    return sendUnchainedFinishedSignal(seq, handled);//向input发送event已完成发送的消息
}
2.4.1.4.13 sendUnchainedFinishedSignal

主要作用为:

1.此函数会生成完成事件处理的InputMessage消息,并通过socket,发送发送消息到IMS侧。

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);
}
2.4.1.4.14 InputChannel::sendMessage

主要作用为:

1.通过socket,向IMS发送已完成事件处理的消息,此时会触发此时会触发input端的handleReceiveCallback函数。

//向input发送event已完成发送的消息,此时会触发input端的handleReceiveCallback函数
status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
        nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite < 0) {
        int error = errno;
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (size_t(nWrite) != msgLength) {
        return DEAD_OBJECT;
    }
    return OK;//ok的值是0
}
2.4.1.4.15 InputDispatcher::handleReceiveCallback

主要作用为:

1.IMS服务端会调用receiveFinishedSignal函数,从socket中读取信息,序列号和完成处理的标志,并保存到msg中,返回0代表成功。

2.调用finishDispatchCycleLocked函数,内部会生成一个commond命令,并放入命令队列中。

3.调用runCommandsLockedInterruptible函数,循环从mCommandQueue中取出command并执行。执行的命令是这个doDispatchCycleFinishedLockedInterruptible函数doDispatchCycleFinishedLockedInterruptible主要是将处理完成的事件从等待队列中删除。

//查看handleReceiveCallback函数
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
    InputDispatcher* d = static_cast<InputDispatcher*>(data);

    { // acquire lock
        AutoMutex _l(d->mLock);

        ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
        if (connectionIndex < 0) {//如果连接不存在,则返回
            ALOGE("Received spurious receive callback for unknown input channel.  "
                    "fd=%d, events=0x%x", fd, events);
            return 0; // remove the callback
        }

        bool notify;
        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
        if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {//如果事件不等于发生错误或者挂断
            if (!(events & ALOOPER_EVENT_INPUT)) {//如果事件不是表示socket有可读
                ALOGW("channel '%s' ~ 收到未处理轮询事件的虚假回调.  "
                        "events=0x%x", connection->getInputChannelName().c_str(), events);
                return 1;//如果返回的事件是未处理的poll event,则返回
            }

            nsecs_t currentTime = now();
            bool gotOne = false;
            status_t status;
			//启动一个死循环,启动死循环后,会一直读取消息,并根据序列号生成命令,保存在命令数组中,
			//等到无消息可读,则会返回-EAGAIN,即-11
            for (;;) {
                uint32_t seq;
                bool handled;
                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);//从socket中读取信息,并保存到msg中,返回0代表成功
				
				/**
                if (status) {//如果返回的不是ok,则退出循环,当返回-EAGAIN,代表无数据可读,退出循环
                    break;
                }
				*/
                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);//内部会生成一个commond命令,并放入命令队列中。
                gotOne = true;
            }
            if (gotOne) {
                d->runCommandsLockedInterruptible();//循环从mCommandQueue中取出command并执行。执行的命令是这个doDispatchCycleFinishedLockedInterruptible函数。
				//doDispatchCycleFinishedLockedInterruptible主要是将事件从等待队列中删除
                if (status == WOULD_BLOCK) {
                    return 1;
                }
            }

            notify = status != DEAD_OBJECT || !connection->monitor;
            if (notify) {
                ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                        connection->getInputChannelName().c_str(), status);
            }
        } else {//代表发生错误
			//Monitor channels从未显式注销。
			//当远程端点关闭时,我们会自动执行此操作,所以不要担心它们。
            notify = !connection->monitor;
            if (notify) {
                ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                        "events=0x%x", connection->getInputChannelName().c_str(), events);
            }
        }

        //取消注册inputchannel
        d->unregisterInputChannelLocked(connection->inputChannel, notify);
        return 0; // remove the callback
    } // release lock
}
2.4.1.4.16 InputPublisher::receiveFinishedSignal

主要作用为:

1.调用receiveMessage从socket中读取信息。

2.将消息序列和处理完成的标志取出来并赋值。

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

    InputMessage msg;
    status_t result = mChannel->receiveMessage(&msg);//从socket中读取信息,并保存到msg中,返回0代表成功
	
	/**
    if (result) {
        *outSeq = 0;
        *outHandled = false;
        return result;
    }
	*/
    if (msg.header.type != InputMessage::TYPE_FINISHED) {//如果返回的消息类型不是成功类型
        ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
                mChannel->getName().c_str(), msg.header.type);
        return UNKNOWN_ERROR;//返回,不知道的错误
    }
    *outSeq = msg.body.finished.seq;//从msg中取出序列号和handle
    *outHandled = msg.body.finished.handled;
    return OK;
}
2.4.1.4.17 InputChannel::receiveMessage

主要作用为:

1.从socket对应的mFd取出数据,存储在msg中。

status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);//从socket对应的mFd取出数据,存储在msg中
		
    } while (nRead == -1 && errno == EINTR);

    if (nRead < 0) {//代表读取错误
        int error = errno;
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.c_str(), errno);
#endif
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (nRead == 0) { // 检查是否有错误
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.c_str());
#endif
        return DEAD_OBJECT;
    }

    if (!msg->isValid(nRead)) {
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ received invalid message", mName.c_str());
#endif
        return BAD_VALUE;
    }

#if DEBUG_CHANNEL_MESSAGES
    ALOGD("channel '%s' ~ received message of type %d", mName.c_str(), msg->header.type);
#endif
    return OK;
}
2.4.1.4.18 finishDispatchCycleLocked

主要作用为:

1.其内部会生成一个command,并入队到mCommandQueue中,传入的函数doDispatchCycleFinishedLockedInterruptible就是命令本身。

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

    connection->inputPublisherBlocked = false;
	//STATUS_BROKEN表示发生了不可恢复的通信错误,STATUS_ZOMBIE表示input channel已注销。
	
	/**
    if (connection->status == Connection::STATUS_BROKEN
            || connection->status == Connection::STATUS_ZOMBIE) {
        return;
    }
	*/

    // 通知其他系统组件并准备开始下一个分发循环
    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
}
void InputDispatcher::onDispatchCycleFinishedLocked(
        nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
	//生成一个command,并入队到mCommandQueue中,传入的函数doDispatchCycleFinishedLockedInterruptible就是命令本身
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
    commandEntry->connection = connection;//
    commandEntry->eventTime = currentTime;
    commandEntry->seq = seq;
    commandEntry->handled = handled;
}
InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command);
    mCommandQueue.enqueueAtTail(commandEntry);
    return commandEntry;
}
2.4.1.4.19 runCommandsLockedInterruptible

主要作用为:

1.从mCommandQueue中取出刚才添加的command并执行。

bool InputDispatcher::runCommandsLockedInterruptible() {
    if (mCommandQueue.isEmpty()) {
        return false;
    }
	
	//循环从mCommandQueue中取出command并执行
    do {
        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();

        Command command = commandEntry->command;
        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'

        commandEntry->connection.clear();
        delete commandEntry;
    } while (! mCommandQueue.isEmpty());
    return true;
}
2.4.1.4.20 doDispatchCycleFinishedLockedInterruptible

主要作用为:

 1.根据seq序列号从waitQueue队列中取出对应的dispatchEntry,然后让其出队,代表已经完成此事件的发送。

void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
    sp<Connection> connection = commandEntry->connection;
    nsecs_t finishTime = commandEntry->eventTime;
    uint32_t seq = commandEntry->seq;
    bool handled = commandEntry->handled;

    // Handle post-event policy actions.
    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);//根据seq序列号从waitQueue队列中取出对应的dispatchEntry
    if (dispatchEntry) {
        nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;//用输入事件处理完成的时间减去此事件分发的时间就得到了处理此次事件的总耗时
        if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {//打印出所有分发时间超过 2s 的事件
            std::string msg =
                    StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
                    connection->getWindowName().c_str(), eventDuration * 0.000001f);
            dispatchEntry->eventEntry->appendDescription(msg);
            ALOGI("%s", msg.c_str());
        }

        bool restartEvent;//restartEvent 决定是否将事件再次分发
        if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
			//判断事件是否需要再次分发
            restartEvent = afterKeyEventLockedInterruptible(connection,dispatchEntry, keyEntry, handled);
        } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
            MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
            restartEvent = afterMotionEventLockedInterruptible(connection,
                    dispatchEntry, motionEntry, handled);
        } else {
            restartEvent = false;
        }


        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
            connection->waitQueue.dequeue(dispatchEntry);//从waitQueue队列中让此seq对应的dispatchEntry出队,代表已经完成发送。
            traceWaitQueueLengthLocked(connection);//追踪队列长度
            if (restartEvent && connection->status == Connection::STATUS_NORMAL) {//如果此事件需要再次分发并且连接正常
                connection->outboundQueue.enqueueAtHead(dispatchEntry);//入队outboundQueue
                traceOutboundQueueLengthLocked(connection);//追踪队列长度
            } else {
                releaseDispatchEntryLocked(dispatchEntry);//释放事件
            }
        }

        startDispatchCycleLocked(now(), connection);//启动下一个事件处理循环。
    }
}
2.4.1.2 情况二:当前应用没有焦点窗口,但存在Activity兜底处理的流程分析

即当我们只打开应用,而不点击EditText的区域时候,即EditText此时不是焦点,当按下音量减少按键,结果如下。即最终会走的是Activity的兜底的onKeyDown和onKeyup函数。

2.4.1.2.1 Activity.dispatchKeyEvent
//Activity.java
public boolean dispatchKeyEvent(KeyEvent event) {
        onUserInteraction();//空实现

        //让操作栏打开菜单,以响应菜单键的优先级高于处理它的窗口
        final int keyCode = event.getKeyCode();
		/**
        if (keyCode == KeyEvent.KEYCODE_MENU &&//如果是菜单栏
                mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
            return true;
        }
		

        Window win = getWindow();
        if (win.superDispatchKeyEvent(event)) {//情况一:接着就会调用 PhoneWindow 的 superDispatchTouchEvent 方法,此处到最后的焦点窗口的onKey函数。
		//其返回值代表目标 View 对事件的处理结果,
            return true;
        }*/
        View decor = mDecor;//此时decor是DecorView,为根view
        if (decor == null) decor = win.getDecorView();
        return event.dispatch(this, decor != null? decor.getKeyDispatcherState() : null, this);
		//情况二:我们可以看到如果目标 View 没有消费掉此次事件(即返回值为 false)则会调用 Activity 的 onKeyDown或者onKeyup来处理。
}
 2.4.1.2.2 keyEvent.dispatch

主要作用为:

1.此时便会调用Activity的onKeyDown函数和onKeyUp函数进行处理。

然后返回值为true,

返回链路为DecorView.dispatchKeyEvent------>ViewPostImeInputStage.processKeyEvent函数。

最后processKeyEvent函数会返回FINISH_HANDLED。表示已经完成了按键事件的处理。

然后继续返回到了 2.3.13 InputStage.apply函数,此时会执行 finish(q, true)函数。

后续内容和2.4.1.1.6 ViewRootImpl.finish到2.4.1.1.20一样,请查看这些章节。

keyEvent.java
public final boolean dispatch(Callback receiver, DispatcherState state,
            Object target) {
				//此时receiver是Activity
        switch (mAction) {
            case ACTION_DOWN: {
                mFlags &= ~FLAG_START_TRACKING;
                if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
                        + ": " + this);
                boolean res = receiver.onKeyDown(mKeyCode, this);//注意此处便是Activity兜底的onKeyDown函数
                if (state != null) {
                    if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
                        if (DEBUG) Log.v(TAG, "  Start tracking!");
                        state.startTracking(this, target);
                    } else if (isLongPress() && state.isTracking(this)) {//如果此key是长按
                        try {
                            if (receiver.onKeyLongPress(mKeyCode, this)) {//则调用Activity的onKeyLongPress函数
                                if (DEBUG) Log.v(TAG, "  Clear from long press!");
                                state.performedLongPress(this);
                                res = true;
                            }
                        } catch (AbstractMethodError e) {
                        }
                    }
                }
                return res;
            }
            case ACTION_UP:
                if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
                        + ": " + this);
                if (state != null) {
                    state.handleUpEvent(this);
                }
                return receiver.onKeyUp(mKeyCode, this);//注意此处便是Activity兜底的onKeyUp函数
        }
        return false;
}
2.4.1.3 情况三:当前无应用,音量加减交给PhoneWindow处理流程分析

此时DecorView.dispatchKeyEvent会调用PhoneWindow的onKeyDown和onKeyUp处理。

//DecorView.java
    public boolean dispatchKeyEvent(KeyEvent event) {
        final int keyCode = event.getKeyCode();
        final int action = event.getAction();
        final boolean isDown = action == KeyEvent.ACTION_DOWN;

        if (isDown && (event.getRepeatCount() == 0)) {
			/**
            if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
                boolean handled = dispatchKeyShortcutEvent(event);
                if (handled) {
                    return true;
                }
            }


            //如果某个面板处于打开状态,请在没有弦面板键的情况下对其执行快捷方式
            if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
                if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
                    return true;
                }
            }
			*/
        }

        if (!mWindow.isDestroyed()) {
            final Window.Callback cb = mWindow.getCallback();//mWindow是phonewindow
			//Window.Callback 指向当前 Activity,在 Activity 启动时会调用自己的 attach 方法,
			//此方法中会将自己作为 callback 传给 window。
            final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                    : super.dispatchKeyEvent(event);//情况一和二:返回值代表目标 View 对事件的处理结果
			
            if (handled) {
                return true;
            }
        }

        return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
                : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
				//情况三:调用PhoneWindow的onKeyDown,onKeyUp 用于PhoneWindow消费音量加减等按键
}

2.4.1.3.1 PhoneWindow.onKeyDown

主要作用为:

1.发送给media服务进行多媒体的相关操作,如调节音量大小。

    protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) {

        final KeyEvent.DispatcherState dispatcher =
                mDecor != null ? mDecor.getKeyDispatcherState() : null;

        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_UP://音量加
            case KeyEvent.KEYCODE_VOLUME_DOWN://音量减少
            case KeyEvent.KEYCODE_VOLUME_MUTE: {//扬声器静音按键

                if (mMediaController != null) {
                    mMediaController.dispatchVolumeButtonEventAsSystemService(event);//发送给media音量加减的事件
                } else {
                    getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(event,
                            mVolumeControlStreamType);
                }
                return true;
            }
            // These are all the recognized media key codes in
            // KeyEvent.isMediaKey()
            case KeyEvent.KEYCODE_MEDIA_PLAY://多媒体键 播放
            case KeyEvent.KEYCODE_MEDIA_PAUSE://多媒体键 暂停
            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: //多媒体键 播放/暂停
            case KeyEvent.KEYCODE_MUTE://话筒静音按键
            case KeyEvent.KEYCODE_HEADSETHOOK:
            case KeyEvent.KEYCODE_MEDIA_STOP://多媒体键 停止
            case KeyEvent.KEYCODE_MEDIA_NEXT://多媒体键 下一首
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS://多媒体键 上一首
            case KeyEvent.KEYCODE_MEDIA_REWIND://多媒体键 快退
            case KeyEvent.KEYCODE_MEDIA_RECORD://多媒体按键 录音
            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {//多媒体键 快进
                if (mMediaController != null) {
                    if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) {
                        return true;
                    }
                }
                return false;
            }

            case KeyEvent.KEYCODE_MENU: {//菜单按键
                onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event);
                return true;
            }

            case KeyEvent.KEYCODE_BACK: {//返回按键
                if (event.getRepeatCount() > 0) break;
                if (featureId < 0) break;
                // Currently don't do anything with long press.
                if (dispatcher != null) {
                    dispatcher.startTracking(event, this);
                }
                return true;
            }

        }

        return false;
    }

然后继续返回到了 2.3.13 InputStage.apply函数,此时会执行 finish(q, true)函数。

后续内容和2.4.1.1.6 ViewRootImpl.finish到2.4.1.1.20一样,请查看这些章节。

至此,普通按键事件的派发便分析完成了,下一篇,我们会对motion事件处理和分发流程做一个详细分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1913277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW平台从离散光子到连续光子的光子计数技术

光子计数技术用于将输入光子数转换为离散脉冲。常见的光子计数器假设光子是离散到达的&#xff0c;记录到来的每一个光子。但是&#xff0c;当两个或多个光子同时到达时&#xff0c;计数器会将其记录为单个脉冲&#xff0c;从而只计数一次。当连续光子到达时&#xff0c;离散光…

基于YOLOv8深度学习的CT扫描图像肾结石智能检测系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

使用八股搭建神经网络

神经网络搭建八股 使用tf.keras 六步法搭建模型 1.import 2.train, test 指定输入特征/标签 3.model tf.keras.model.Sequential 在Squential,搭建神经网络 4.model.compile 配置训练方法&#xff0c;选择哪种优化器、损失函数、评测指标 5.model.fit 执行训练过程&a…

移动互联安全

什么是移动互联 从狭义的角度来说&#xff0c;移动互联网是一个以宽带IP为技术核心&#xff0c;可同时提供语音、传真、图像、多媒体等高品质电信服务的新一代开放的电信基础网络。 从广义的角度来说&#xff0c;移动互联网是指将互联网提供的技术、平台、应用以及商业模式&…

拖动事件 dragEnterEvent、放置事件 dropEvent、resize事件resizeEvent的实现

拖动事件 dragEnterEvent、放置事件 dropEvent、resize事件resizeEvent的实现 拖动事件 dragEnterEvent 放置事件 dropEvent resize事件resizeEvent DragFileExample.h #ifndef DRAGFILEEXAMPLE_H #define DRAGFILEEXAMPLE_H#include <QWidget> #include <QDragEnterE…

【Python大语言模型系列】Windows环境下部署Chatglm2-6B-int4大语言模型(完整教程)

这是我的第319篇原创文章。 一、引言 电脑配置 &#xff1a; python版本要求&#xff1a;3.8torch版本&#xff1a;2.0.1cuda&#xff1a;11.7windows系统&#xff1a;Windows 10 显卡&#xff1a;6G以上GPU 二、实现过程 2.1 下载chatglm2-6b的项目源码 上chatglm2-6B的官…

[PM]流程与结构设计

流程图 流程就是为了达到特定目标, 进行的一系列有逻辑性的操作步骤, 由两个及已上的步骤, 完成一个完整的行为过程, 即可称为流程, 流程图就是对这个过程的图形化展示 分类 业务流程图 概念: 描述业务流程的一种图, 通过特定符号和连线表示具体某个业务的处理步骤和过程作…

推荐一个比 Jenkins 使用更简单的项目构建和部署工具

最近发现了一个比 Jenkins 使用更简单的项目构建和部署工具&#xff0c;完全可以满足个人以及一些小企业的需求&#xff0c;分享一下。 项目介绍 Jpom 是一款 Java 开发的简单轻量的低侵入式在线构建、自动部署、日常运维、项目监控软件。 日常开发中&#xff0c;Jpom 可以解…

来聊聊Redis持久化AOF管道通信的设计

写在文章开头 最近遇到很多烦心事&#xff0c;希望通过技术来得以放松&#xff0c;今天这篇文章笔者希望会通过源码的方式分析一下AOF如何通过Linux父子进程管道通信的方式保证进行AOF异步重写时还能实时接收用户处理的指令生成的AOF字符串&#xff0c;从而保证尽可能的可靠性…

保密U盘仍然存在数据安全危机?该怎么用才能规避?

保密U盘以前主要用于国家涉密单位或部门&#xff0c;但随着人们对于信息安全的重视越来越高&#xff0c;在民用企事业单位以及个人用户方面也应用得日益广泛。 使用保密U盘在安全性上比普通U盘具有优势&#xff0c;但却仍然存在安全危机&#xff0c;具体为&#xff1a; 病毒和…

万字学习——DCU编程实战

参考资料 2.1 DCU软件栈&#xff08;DCU ToolKit, DTK&#xff09; DCU 开发与使用文档 (hpccube.com) DCU软件栈 DCU的软件栈—DCU Toolkit&#xff08;DTK&#xff09; HIP&#xff08;Heterogeneous-Compute Interface for Portability&#xff09;是AMD公司在2016年提出…

基于jeecgboot-vue3的Flowable流程-集成仿钉钉流程(五)仿钉钉流程的json数据保存与显示

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、需要做一个界面保存与显示仿钉钉的流程&#xff0c;先建一个表&#xff0c;用online建 2、通过上面生成代码&#xff0c;放入到相应的前后端工程里 3、修改前端仿钉钉流程的设计功能&a…

Java版Flink使用指南——分流导出

大纲 新建工程编码Pom.xml自定义无界流分流 测试工程代码 在之前的案例中&#xff0c;我们一直使用的是单个Sink来做数据的输出。实际上&#xff0c;Flink是支持多个输出流的。本文我们就来讲解如何在Flink数据输出时做分流处理。 我们将基于《Java版Flink使用指南——自定义无…

java如何实现一个死锁 ?

死锁(Deadlock)是指在并发系统中,两个或多个线程(或进程)因争夺资源而互相等待,导致它们都无法继续执行的一种状态。 一、简易代码 public class DeadlockExample {private static final Object lock1 = new Object();private

Python面试宝典第9题:买卖股票

题目 给定一个整型数组&#xff0c;它的第i个元素是一支给定股票第i天的价格。如果最多只允许完成一笔交易&#xff08;即买入和卖出一支股票一次&#xff09;&#xff0c;设计一个算法来计算你所能获取的最大利润。注意&#xff1a;你不能在买入股票前卖出股票。 示例 1&#…

前端面试题36(js栈和堆)

在JavaScript中&#xff0c;内存管理是自动进行的&#xff0c;主要通过栈(stack)和堆(heap)两种方式来分配和管理内存。理解这两者对于深入学习JavaScript以及优化代码性能非常关键。 栈 (Stack) 栈是一种后进先出&#xff08;Last In, First Out, LIFO&#xff09;的数据结构…

U盘启动快捷键查询

电脑开机一般默认自身硬盘启动系统&#xff0c;如需要U盘重装系统&#xff0c;开机时一直按对应机型的U盘启动快捷键&#xff0c;选择对应USB设备即可U盘启动。 一、品牌台式 二、品牌笔记本 三、组装电脑

Go语言---Json

JSON (JavaScript Object Notation)是一种比XML 更轻量级的数据交换格式&#xff0c;在易于人们阅读和编写的同时&#xff0c;也易于程序解析和生成。尽管JSON是 JavaScript的一个子集&#xff0c;但 JSON采用完全独立于编程语言的文本格式&#xff0c;且表现为键/值对集合的文…

红日靶场----(三)漏洞利用

上期已经信息收集阶段已经完成&#xff0c;接下来是漏洞利用。 靶场思路 通过信息收集得到两个吧靶场的思路 1、http://192.168.195.33/phpmyadmin/&#xff08;数据库的管理界面&#xff09; root/root 2、http://192.168.195.33/yxcms/index.php?radmin/index/login&am…

深入探索大语言模型

深入探索大语言模型 引言 大语言模型&#xff08;LLM&#xff09;是现代人工智能领域中最为重要的突破之一。这些模型在自然语言处理&#xff08;NLP&#xff09;任务中展示了惊人的能力&#xff0c;从文本生成到问答系统&#xff0c;无所不包。本文将从多个角度全面介绍大语…