ViewRootImpl源码解析 (二) - 事件分发

【Android】 专栏收录该内容
201 篇文章 2 订阅

概述

前文链接:ViewRootImpl源码解析 (一) - View的更新

前文讲了ViewRootImpl中View更新相关的逻辑,此文将讨论下ViewRootImpl中的事件分发逻辑,或者说是事件回传逻辑。

此处先列出笔者的个人对ViewRootImpl的理解,如有问题欢迎评论指正。

View的所有更新UI的操作最终都必须经过操作系统在系统进程的处理,才能够通过硬件展示到用户面前。
ViewRootImpl担任了window与view的中间人的角色,View可以通过ViewRootImpl将更新UI的操作告知操作系统,而widnow也可以将操作系统层面的触摸等操作反馈给View。

ViewRootImpl事件分发入口

ViewRootImpl.WindowInputEventReceiver.onInputEvent()这个方法是ViewRootImpl中接收事件的入口。

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

        @Override
        public void onBatchedInputEventPending() {
            if (mUnbufferedInputDispatch) {
                super.onBatchedInputEventPending();
            } else {
                scheduleConsumeBatchedInput();
            }
        }

        @Override
        public void dispose() {
            unscheduleConsumeBatchedInput();
            super.dispose();
        }
    }

事件的类型,MotionEvent与KeyEvent

InputEvent这个类是传递过来的事件。
InputEvent这个类有两个子类:MotionEvent与KeyEvent

  • MotionEvent:触摸事件
  • KeyEvent:键盘点击事件

触摸事件的调用栈

enqueueInputEvent()起,一直到view的dispatchTouchEvent(),传递的流程比较简单,因此直接列出调用栈:

  • ViewRootImpl.WindowInputEventReceiver.onInputEvent()
  • ViewRootImpl.enqueueInputEvent()
  • ViewRootImpl.doProcessInputEvents()
  • ViewRootImpl.deliverInputEvent()
  • ViewPostImeInputStage.deliver()
  • ViewPostImeInputStage.onProcess()
  • ViewPostImeInputStage.processPointerEvent()
  • View.dispatchPointerEvent()
  • View.dispatchTouchEvent()

键盘点击事件的调用栈

键盘点击事件和触摸事件的调用栈大部分都是一样的,主要是在ViewPostImeInputStage.onProcess()中会区分处理,调用栈如下:

  • ViewRootImpl.WindowInputEventReceiver.onInputEvent()
  • ViewRootImpl.enqueueInputEvent()
  • ViewRootImpl.doProcessInputEvents()
  • ViewRootImpl.deliverInputEvent()
  • ViewPostImeInputStage.deliver()
  • ViewPostImeInputStage.onProcess()
  • ViewPostImeInputStage.processKeyEvent()

键盘事件的这块知识点笔者不是很熟悉,有兴趣的可以自行研究下源码,也欢迎在评论中分享讨论。

Decorview中的事件传递

在activity中,ViewRootImpl的mView实际上就是DecorView。
对具体原理有兴趣的读者可以看下笔者的前文:
todo

DecorView中对dispatchTouchEvent()进行了重写,因此并不是我们熟悉的直接将事件分发给子View。

DecorView.dispatchTouchEvent()

dispatchTouchEvent()中最终调用的Window.Callback的dispatchTouchEvent(),那么此处mWindow.getCallback()获得的对象是什么呢?继续往下看。

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

Activity.attach()

在Activity.attach()中可以看到,window的callback其实就是它对应的activity。
于是我们可以直接看下activity的dispatchTouchEvent()方法。

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
————————————————————省略
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
————————————————————省略
            }

Activity.dispatchTouchEvent()

此处的逻辑如下:

  1. 如果用户触摸的屏幕,那么会触发onUserInteraction()逻辑。(此逻辑与本文内容无关,有兴趣的可以自行看下源码)
  2. 调用到window的superDispatchTouchEvent(),如果已经处理的事件,那么久直接return。
  3. 如果window没有处理事件,那么会走到activity的onTouchEvent()逻辑。
  4. onTouchEvent()逻辑中就是判断window的shouldCloseOnTouch()是否返回true,如果是true,就直接关闭activity,并且return true表示该事件已经处理过。反之就return false,表示该事件没有处理过。
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
    
    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }

PhoneWindow.superDispatchTouchEvent()

widnow的superDispatchTouchEvent()方法实际上就是调用的DecorView的superDispatchTouchEvent()方法。

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

DecorView.superDispatchTouchEvent()

DecorView的superDispatchTouchEvent()方法,实际上调用的是View的dispatchTouchEvent()方法。

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

DecorView事件传递调用栈

  • DecorView.dispatchTouchEvent()
  • Activity…dispatchTouchEvent()
  • PhoneWindow.superDispatchTouchEvent()
  • DecorView.superDispatchTouchEvent()
  • View.dispatchTouchEvent()

为什么DecorView的事件传递要如此麻烦,最终不还是调用到View.dispatchTouchEvent()?
回答:
根据调用栈可以看到,事件传给了activity与window,这表示activity与window都可以对事件进行拦截,或者做特殊处理。
比如这几个方法:

  • activity.onUserInteraction()
  • window.superDispatchTouchEvent()
  • widnow.shouldCloseOnTouch()
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值