从你触摸屏幕开始分析android触摸事件分发

作者: super_shanks | 来源:发表于2017-03-01 14:22 被阅读1820次

    本篇只会讲到触发ViewGroup的dispatchTouchEvent为止,因为接下去的一搜一大把,或者说有点基础的应该都了解。

    • 从触摸开始

      首先,请你打开命令行工具。输入adb shell进入到shell命令,然后输入getevent,会监听打印触摸屏幕的event信息。

    add device 1: /dev/input/event5
    name: "msm8974-taiko-mtp-snd-card Headset Jack"
    add device 2: /dev/input/event4
    name: "msm8974-taiko-mtp-snd-card Button Jack"
    add device 3: /dev/input/event3
    name: "hs_detect"
    add device 4: /dev/input/event1
    name: "touch_dev"
    add device 5: /dev/input/event0
    name: "qpnp_pon"
    add device 6: /dev/input/event2
    name: "gpio-keys"

       当你使出你的一阳指点击屏幕的时候,变回不断的去获取到你的点击事件,就像这样:
    

    /dev/input/event1: 0000 0000 00000000
    /dev/input/event1: 0003 0039 000005cf
    /dev/input/event1: 0003 0035 0000020b
    /dev/input/event1: 0003 0036 0000068d
    /dev/input/event1: 0000 0000 00000000
    /dev/input/event1: 0003 0036 0000068c
    /dev/input/event1: 0003 0030 00000005
    /dev/input/event1: 0000 0000 00000000
    /dev/input/event1: 0003 0039 ffffffff
    /dev/input/event1: 0000 0000 00000000

      这些操作全都是Linux Kernel去做的,只要你点击了屏幕了,硬件设备变回产生硬件终端,Kernel收到硬件终端之后,会对其进行加工,包装成event事件之后添加到/dev/input/目录下,就像如上所示的event1。
      
     [](http://www.cnblogs.com/tnxk/archive/2012/10/26/2741326.html)
    * ####Android系统的监听
      Android会不断的去监控/dev/input/目录下的所有的设备节点,一旦发现有新的设备节点可读时就会立马读出事件并进行处理。
    
        * ######WMS
      而这里的复杂步骤涉及到frameWork层,我们就从WMS开始吧,
      先是有SystemServer启动的WMS。SystemServer.java的startOtherServices()
    

    wm = WindowManagerService.main(context, inputManager,
    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
    !mFirstBoot, mOnlyCore);

    并且同样在这个方法中初始化了InputManagerService,掌管输入事件的服务。
    

    inputManager = new InputManagerService(context);

    我们看到`WindowManagerService`的main方法传入的就是这个inputManager。
    在InputManagerService的构造方法中,用到了
    
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    
    native的方法,nativce层不是重点,我这边就快速的将过去了。
    在`frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp`中(没有在本地编译过源码的同学可以去 http://androidxref.com/ 查看,基于当前最新的7.1.1)
    

    static jlong nativeInit(JNIEnv* env, jclass /* clazz /,
    jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
    jniThrowRuntimeException(env, "MessageQueue is not initialized.");
    return 0;
    }
    NativeInputManager
    im = new NativeInputManager(contextObj, serviceObj,
    messageQueue->getLooper());
    im->incStrong(0);
    eturn reinterpret_cast<jlong>(im);
    }

    然后看内部类nativeInputManger
    

    NativeInputManager::NativeInputManager(jobject contextObj,
    ...
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
    }

    我们看到创建了一个EventHub类,并且将其交给InputManger并生成一个InputManger对象。
    /frameworks/native/services/inputflinger/InputManager.cpp
    

    InputManager::InputManager(
    const sp<EventHubInterface>& eventHub,
    const sp<InputReaderPolicyInterface>& readerPolicy,
    const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
    }

    一个分发对象,一个reader对象,并且调用initialize方法
    

    void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
    }

    创建读线程和分发线程
    
       至此,所有的初始化先都ok了,在SystemServer.java,创建了InputManagerService之后没几行就调用了            `inputManager.start();`,
    

    public void start() {
    ...
    nativeStart(mPtr);
    ...
    }

    又看到了native。。。来吧继续相当枯燥的native,我要快进了,我有点写的想吐。。
    `frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp`
    

    static void nativeStart(JNIEnv* env, jclass /* clazz /, jlong ptr) {
    NativeInputManager
    im = reinterpret_cast<NativeInputManager*>(ptr);

        status_t result = im->getInputManager()->start();
        if (result) {
            jniThrowRuntimeException(env, "Input manager could not be started.");
        }
    

    }

    `/frameworks/native/services/inputflinger/InputManager.cpp
    `
    

    status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
    ALOGE("Could not start InputDispatcher thread due to error %d.", result);
    return result;
    }

        result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
        if (result) {
            ALOGE("Could not start InputReader thread due to error %d.", result);
    
            mDispatcherThread->requestExit();
            return result;
        }
    
        return OK;
    

    }

    启动了读线程和分发线程
    `/frameworks/native/services/inputflinger/InputReader.cpp`
    

    bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
    }
    void InputReader::loopOnce() {
    ...
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    ...
    }

    不断的loop去通过EventHub去getEvents(越来越偏了,getEvents不继续往下了,知道这个深度已经对于非framework工程师来说已经够了)
    在getEvents方法中去从dev/input/目录下读取设备节点并加工,并返回给InputReader进行处理。
    
              之后的处理过程以及一系列跳转也是相当复杂,由于本文的初衷并非详解最底层的东西,
              故而此处一并略过直接到底层将event回传给java层的最末
    
        * ######InputEventReceiver
       在此我们只需要知道由InputChannel构建起了UI进程和底层system_server进程的socket通道。
       最终会从NativeInputEventReceiver.cpp处调起InputEventReceiver的方法
    
    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }
    
        InputEventReceiver是个抽象类,我们在ViewRootImpl中定义了如下
    

    final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
    super(inputChannel, looper);
    }

        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }
    
        @Override
        public void onBatchedInputEventPending() {
            if (mUnbufferedInputDispatch) {
                super.onBatchedInputEventPending();
            } else {
                scheduleConsumeBatchedInput();
            }
        }
    
        @Override
        public void dispose() {
            unscheduleConsumeBatchedInput();
            super.dispose();
        }
    }
    
    那么我们就看看enqueueInputEvent到底做了些什么操作:
    

    void enqueueInputEvent(InputEvent event,
    InputEventReceiver receiver, int flags, boolean processImmediately) {
    ...
    if (processImmediately) {
    doProcessInputEvents();
    } else {
    scheduleProcessInputEvents();
    }
    }
    void doProcessInputEvents() {
    while (mPendingInputEventHead != null) {
    ...
    deliverInputEvent(q);
    }
    ...
    }
    private void deliverInputEvent(QueuedInputEvent q) {
    ...
    if (stage != null) {
    stage.deliver(q);
    } else {
    finishInputEvent(q);
    }
    }

    最终就在这个deliver方法中,而这个stage是个InputStage对象,这个类内部是链表结构,最终会将q分发到可以处理的窗口ViewPostImeInputStage,由它的processPointerEvent方法来处理
    

    private int processPointerEvent(QueuedInputEvent q) {
    final MotionEvent event = (MotionEvent)q.mEvent;
    final View eventTarget =
    (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
    mCapturingView : mView;
    ...
    boolean handled = eventTarget.dispatchPointerEvent(event);
    ...
    return handled ? FINISH_HANDLED : FORWARD;
    }

    调用的view的dispatchPointerEvent方法:
    

    public final boolean dispatchPointerEvent(MotionEvent event) {
    if (event.isTouchEvent()) {
    return dispatchTouchEvent(event);
    } else {
    return dispatchGenericMotionEvent(event);
    }
    }

    而我们知道,window的最底层的View就是DecorView,那么这个时候调用的应该就是DecorView的dispatchTouchEvent方法
    

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    final Window.Callback cb = mWindow.getCallback();
    return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
    ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

    还记的callBack是谁么,在Activity的attach方法中,`mWindow.setCallback(this);
    `
    这个callback就是activity本身,所以我们要去Activity中查看它的dispatchTouchEvent方法
    

    public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
    return true;
    }
    return onTouchEvent(ev);
    }

    这里分了两步,先去getWindow().superDispatchTouchEvent(ev),这一步会从PhoneWindow->DecorView->ViewGroup(DecorView继承FrameLayout就是ViewGroup),最后实际上是触发了ViewGroup的dispatchTouchEvent方法,也就是activity会先将事件交给DecorView去处理,如果被消耗掉,就返回true。如果没有消耗这个事件,就回调Activity自己的onTouchEvent。
    
        * ######总结
    那么把上面的一大坨我们简略的来讲如下的流程:
       * 用户触摸屏幕产生设备节点中断并保存到/dev/input/目录下
       * 底层的EventHub监听目录,将事件读出并加工返回给随着WMS一起启动的底层的InputReader
       * InputReader处理加工之后交给InputDispatcher来进行分发,通过socket通知UI进程的InputEventReceiver接收到事件
       * InputEventReceiver将回调事件一步步传递给Activity来进行分发
       * Activity先将事件交给DecorView来进行处理,如果DecorView消耗则返回true,否则自己回调onTouchEvent方法
    
    
    
       以上!

    相关文章

      网友评论

        本文标题:从你触摸屏幕开始分析android触摸事件分发

        本文链接:https://www.haomeiwen.com/subject/mmsqgttx.html