TraceView帮我解决了touch事件的源头

之前在了解touch事件的时候,都是从activity->phoneWindow->decoreVoew->content部分的view事件分发,而不知道底层是如何传递给view的,该篇文章就是介绍,底层是如何将事件传递给应用层的。下面先把traceView的截图列出来: alt text 当屏幕收到触摸事件后,底层会给主线程的消息队列中插入一条消息,然后唤醒上层,上层的接收端是NativeInputEventReceriver的handleEvent: alt text 接着调用了consumeEvents方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
    bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {

        if (inputEventObj.get()) {
            if (kDebugDispatchCycle) {
                ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
            }
            //调用到java层的android/view/InputEventReceiver的dispatchInputEvent方法
            env->CallVoidMethod(receiverObj.get(),
                                gInputEventReceiverClassInfo.dispatchInputEvent, seq,
                                inputEventObj.get());
    }
}

int register_android_view_InputEventReceiver(JNIEnv* env) {
    int res = RegisterMethodsOrDie(env, "android/view/InputEventReceiver",
            gMethods, NELEM(gMethods));
    //指向了java层的android/view/InputEventReceiver类
    jclass clazz = FindClassOrDie(env, "android/view/InputEventReceiver");
    gInputEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);

    gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
            gInputEventReceiverClassInfo.clazz,
            "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
    gInputEventReceiverClassInfo.onFocusEvent =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(Z)V");
    gInputEventReceiverClassInfo.onPointerCaptureEvent =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onPointerCaptureEvent",
                             "(Z)V");
    gInputEventReceiverClassInfo.onDragEvent =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onDragEvent", "(ZFF)V");
    gInputEventReceiverClassInfo.onTouchModeChanged =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onTouchModeChanged", "(Z)V");
    gInputEventReceiverClassInfo.onBatchedInputEventPending =
            GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onBatchedInputEventPending",
                             "(I)V");
    return res;
}

可以看到上面通过register_android_view_InputEventReceiver方法的注册,将gInputEventReceiverClassInfo指向了java层的android/view/InputEventReceiver。而在consumeEvents中通过env->CallVoidMethod调用jni到了android/view/InputEventReceiver的dispatchInputEvent方法。接下来到了java层,直接看traceview

1
InputEventReceiver.dispatchInputEvent->ViewRootImpl$WindowInputEventReceiver.onInputEvent->ViewRootImpl.enqueueInputEvent->ViewRootImpl.doProcessInputEvents->ViewRootImpl.deliverInputEvent->ViewRootImpl$InputStage.deliver->ViewRootImpl$InputStage.apply->deliver->ViewRootImpl$InputStage.forward->ViewRootImpl$InputStage.onDeliverToNext....->ViewPostImeInputStage.onProcess->ViewPostImeInputStage.processPointerEvent->View.dispatchPointerEvent->DecorView.dispatchTouchEvent->Activity.dispatchTouchEvent->PhoneWindow.superDispatchTouchEvent->DecorView.superDispatchTouchEvent->ViewGroup.dispatchTouchEvent

单击view时候的trace文件

其实这个只是单次的点击事件,如果是多次触发点击,在viewrootimpl中会通过给Choreographer插入一条CALLBACK_INPUT的事件,这个根源也是在NativeInputEventReceiver的consumeEvents中触发了WindowInputEventReceiver的onBatchedInputEventPending,关键代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    if (kDebugDispatchCycle) {
        ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
              getInputChannelName().c_str(), toString(consumeBatches), frameTime);
    }

    if (consumeBatches) {
        mBatchedInputEventPending = false;
        traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
    }
    if (outConsumedBatch) {
        *outConsumedBatch = false;
    }

    ScopedLocalRef<jobject> receiverObj(env, nullptr);
    bool skipCallbacks = false;
    for (;;) {
        uint32_t seq;
        InputEvent* inputEvent;

        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        if (status == WOULD_BLOCK) {
            if (!skipCallbacks && !mBatchedInputEventPending && mInputConsumer.hasPendingBatch()) {
                // There is a pending batch.  Come back later.
                if (!receiverObj.get()) {
                    receiverObj.reset(GetReferent(env, mReceiverWeakGlobal));
                    if (!receiverObj.get()) {
                        ALOGW("channel '%s' ~ Receiver object was finalized "
                              "without being disposed.",
                              getInputChannelName().c_str());
                        return DEAD_OBJECT;
                    }
                }

                mBatchedInputEventPending = true;
                traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
                if (kDebugDispatchCycle) {
                    ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
                          getInputChannelName().c_str());
                }
                //调用到gInputEventReceiverClassInfo的onBatchedInputEventPending方法
                env->CallVoidMethod(receiverObj.get(),
                                    gInputEventReceiverClassInfo.onBatchedInputEventPending,
                                    mInputConsumer.getPendingBatchSource());
                if (env->ExceptionCheck()) {
                    ALOGE("Exception dispatching batched input events.");
                    mBatchedInputEventPending = false; // try again later
                    traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
                }
            }
            return OK;
        }
    }
}

alt text 最终触发到ConsumeBatchedInputRunnable,最后会执行doProcessInputEvents->deliverInputEvent…,也就上单次点击的方法调用。从这里也可以看出,如果触摸事件积累很多的时候,会通过给Choreographer插入一条CALLBACK_INPUT的事件,然后等到下次vsync信号来的时候,才会去处理touch事件,减轻主线程的压力。通过此机制,Android 在保证输入响应的同时,最大限度减少 UI 线程的负载,优化整体流畅性。
多次触摸屏幕的时候trace文件

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy