安卓输入事件在应用进程内的传递和处理

194 阅读2分钟

安卓系统输入事件分发模块和应用进程之间的传递是通过socket,应用在接受到之后其实也是借助Looper机制把输入事件包装成一个消息,丢进消息队列,然后再通过view树分发到具体的控件去处理。本文介绍下应用进程接受到事件之后传递到Acitivty的过程。

重要类图

input.png

接收事件

安卓应用进程在NativeInputEventReceiver中接受InputDispatcher发送的输入事件,然后在通过层层调用会构造一个消息丢进消息队列等待处理。下面看看具体的源码

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    // 调用consumeEvents处理消息
    status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
    
    return KEEP_CALLBACK;
}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
// 这里会调用java层的ViewRootImpl.dispatchInputEvent方法
    bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
                    env->CallVoidMethod(receiverObj.get(),
                    gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
}
        

我们接着看ViewRootImpl.dispatchInputEvent方法

    public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
    // 这里构造消息,然后放进消息队列
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = event;
        args.arg2 = receiver;
        Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args);
        msg.setAsynchronous(true);
        mHandler.sendMessage(msg);
    }

分发事件

接下来看看消息的处理过程,这个消息怎么一步一步分发到Actiity的。 上面说过把Input事件封装成消息之后会丢进消息队列,然后交给ViewRootHandler来处理,

    final class ViewRootHandler extends Handler {
        private void handleMessageImpl(Message msg){
                        case MSG_DISPATCH_INPUT_EVENT: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    InputEvent event = (InputEvent) args.arg1;
                    InputEventReceiver receiver = (InputEventReceiver) args.arg2;
                    enqueueInputEvent(event, receiver, 0, true);
                    args.recycle();
                } break;
        }
    }
    
        // 通常情况下processImmediately为true
    void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }
    
        void doProcessInputEvents() {
        // 循环从PendingInputEvent 取出事件进一步分发
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

// 这里接下来会依次调用
//ViewPostImeInputStage.processPointerEvent
//DecorView.dispatchTouchEvent(MotionEvent ev)
//Activity.dispatchTouchEvent
          deliverInputEvent(q);
        }
    }

在Activity内的分发

Input事件传到Activity之后,接下来怎么在Activity中的view树中传递。一棵view树总的来说分为两种对象,一个viewgroup, 一个view. Activity, ViewGroup, View三个对象都有dispatchTouchEvent, onTouchEvent函数,ViewGroup多一个onInterceptTouchEvent函数。 Input事件在这三个对象内的传输过程是:

  1. Activity在dispatchTouchEvent函数中会把事件传递给ViewGroup, 如果ViewGroup没有处理,那么Activity会在onTouchEvent函数中处理这个事件
  2. viewGroup会在dispatchTouchEvent函数中会把事件传递给View,如果View没有处理,那么ViewGroup会在onInterceptTouchEvent函数中决定是否需要拦截这个事件,如果拦截了,就会在onTouchEvent函数中处理这个事件, 如果不拦截,就会把事件交给Activity去处理。
  3. View接收到事件之后会在onTouchEvent函数中处理这个事件, 如果没有处理,就会把事件原路返回给ViewGroup去处理。

参考

安卓14源码