• 在线客服

  • 扫描二维码
    下载博学谷APP

  • 扫描二维码
    关注博学谷微信公众号

  • 意见反馈

原创 事件是先到DecorView还是先到Window?

发布时间:2020-03-23 11:29:55 浏览 5012 来源:博学谷 作者:照照

    事件是先到DecorView还是先到Window?答案是显而易见的,事件当然是先到DecorView。为什么呢?这就需要我们弄清楚DecorViewPhoneWindowActivity/Dialog之间传递的顺序,下面我们来看看Input系统、Framework层、DecorViewActivity的相关内容,相信大家就能理解事件先到DecorView的本质原因了。

     

    DecorView还是Window

     

    1Input系统

     

    当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点,这便产生了最原生态的内核事件。接着,输入系统取出原生态的事件,经过层层封装后成为KeyEvent或者MotionEvent ;最后,交付给相应的目标窗口(Window)来消费该输入事件。

     

    1)当屏幕被触摸,Linux内核会将硬件产生的触摸事件包装为Event存到/dev/input/event[x]目录下。

     

    2Input系统—InputReader线程loop起来让EventHub调用getEvent()不断的从/dev/input/文件夹下读取输入事件。然后转换成EventEntry事件加入到InputDispatchermInboundQueue

     

    3Input系统—InputDispatcher线程:从mInboundQueue队列取出事件,转换成DispatchEntry事件加入到connectionoutboundQueue队列。再然后开始处理分发事件 (比如分发到ViewRootImplWindowInputEventReceiver中),取出outbound队列,放入waitQueue.

     

    4Input系统—UI线程:创建socket pair,分别位于”InputDispatcher”线程和focused窗口所在进程的UI主线程,可相互通信。

     

    2Framework

     

    //InputEventReceiver.dispachInputEvent()

     

    private void dispatchInputEvent(int seq, InputEvent event) {
       

    mSeqMap.put(event.getSequenceNumber(), seq);
       

    onInputEvent(event); 

     

    }

     

    Native层通过JNI执行Framework层的InputEventReceiver.dispachInputEvent(),而真正调用的是继承了InputEventReceiverViewRootImpl.WindowInputEventReceiver。所以这里执行的WindowInputEventReceiverdispachInputEvent()

     

    final class WindowInputEventReceiver extends InputEventReceiver {

     

    public void onInputEvent(InputEvent event) {

     

    enqueueInputEvent(event, this, 0, true);

     

    }

     

    ...

     

    }

     

    ViewRootImpl

     

    void enqueueInputEvent(InputEvent event,

     

    InputEventReceiver receiver, int flags, boolean processImmediately) {

     

    ...

     

    if (processImmediately) {

     

     //关键点:执行Input事件

     

    doProcessInputEvents();

     

    } else {

     

     //走一遍Handler延迟处理事件

     

    scheduleProcessInputEvents();

     

    }

     

    }

     

    void doProcessInputEvents() {

     

    while (mPendingInputEventHead != null) {

     

    QueuedInputEvent q = mPendingInputEventHead;

     

    mPendingInputEventHead = q.mNext;

     

    if (mPendingInputEventHead == null) {

     

    mPendingInputEventTail = null;

     

    }

     

    q.mNext = null;

     

    mPendingInputEventCount -= 1;

     

    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;

     

    if (me.getHistorySize() > 0) {

     

    oldestEventTime = me.getHistoricalEventTimeNano(0);

     

    }

     

    }

     

    mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);

     

    //关键点:进一步派发事件处理

     

    deliverInputEvent(q);

     

    }

     

    ...
        

    }

     

    private void deliverInputEvent(QueuedInputEvent q) {

     

    Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",

     

    q.mEvent.getSequenceNumber());

     

    if (mInputEventConsistencyVerifier != null) {

     

    mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);

     

    }

     

    InputStage stage;

     

    if (q.shouldSendToSynthesizer()) {

     

    stage = mSyntheticInputStage;

     

    } else {

     

    stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;

     

    }

     

    if (stage != null) {

     

    //关键点:上面决定将事件派发到那个InputStage中处理

     

    stage.deliver(q);

     

    } else {

     

    finishInputEvent(q);

     

    }

     

    }

     

    ViewRootImpl.ViewPostImeInputStage

     

    前面事件会派发到ViewRootImpl.ViewPostImeInputStage中处理,它的父类InputStage.deliver()方法会调用apply()来处理Touch事件:

     

    @Override

     

    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) {

     

    //关键点:执行分发touch事件

     

    return processPointerEvent(q);

     

    } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {

     

    return processTrackballEvent(q);

     

    } else {

     

    return processGenericMotionEvent(q);

     

    }

     

    }

     

    }

     

    private int processPointerEvent(QueuedInputEvent q) {

     

    final MotionEvent event = (MotionEvent)q.mEvent;

     

    ...

     

    //关键点:mView分发Touch事件,mView就是DecorView

     

    boolean handled = mView.dispatchPointerEvent(event);

     

    maybeUpdatePointerIcon(event);

     

    maybeUpdateTooltip(event);

     

    ...

     

    }

     

    3、DecorView

     

    如果你熟悉安卓的WindowActivityDialog对应的ViewRootImpl成员mView就是DecorViewViewdispatchPointerEvent()代码如下:

     

    //View.java

     

    public final boolean dispatchPointerEvent(MotionEvent event) {
        

     

    if (event.isTouchEvent()) {
           

     //分发Touch事件

     

    return dispatchTouchEvent(event);
        

    } else {

     

    return dispatchGenericMotionEvent(event);

     

    }

     

    }

     

    因为DecorView继承FrameLayout,上面所以会调用DecorViewdispatchTouchEvent()

     

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

     

    }

     

    上面Window.Callback都被ActivityDialog实现,所以变量cb可能就是ActivityDialog

     

    4、Activity

     

    当上面cbActivity时,执行ActivitydispatchTouchEvent():

     

    public boolean dispatchTouchEvent(MotionEvent ev) {

     

    if (ev.getAction() == MotionEvent.ACTION_DOWN) {

     

    onUserInteraction();

     

    }

     

    if (getWindow().superDispatchTouchEvent(ev)) {//关键点:getWindow().superDispatchTouchEvent(ev)

     

    return true;

     

    }

     

    return onTouchEvent(ev);

     

    }

     

    如果你熟悉安卓的WindowActivitygetWindow()拿到的就是PhoneWindow,下面是PhoneWindow的代码:

     

    //PhoneWindow.java

     

    @Override

     

    public boolean superDispatchTouchEvent(MotionEvent event) {
       

     

     //调用DecorViewsuperDispatchTouchEvent

     

    return mDecor.superDispatchTouchEvent(event);

     

    }

     

    下面是DecorView.superDispatchTouchEvent()代码:

     

    //DecorView.java

     

    public boolean superDispatchTouchEvent(MotionEvent event) {
       

     //调用ViewGroupdispatchTouchEvent()开始我们常见的分发Touch事件

     

    return super.dispatchTouchEvent(event);

     

    }

     

    因为解耦的原因,所以要DecorView -> Activity -> PhoneWindow -> DecorView传递事件。ViewRootImpl并不知道有Activity这种东西存在!它只是持有了DecorView。所以,不能直接把触摸事件送到Activity.dispatchTouchEvent();不直接分发给DecorView,而是要通过PhoneWindow来间接发送也是因为Activity不知道有DecorView!但是,Activity持有PhoneWindow ,而PhoneWindow当然知道自己的窗口里有些什么了,所以能够把事件派发给DecorView。在Android中,Activity并不知道自己的Window中有些什么,这样耦合性就很低了。不管Window里面的内容如何,只要Window仍然符合Activity制定的标准,那么它就能在Activity中很好的工作。当然,这就是解耦所带来的扩展性的好处。

     

    看到这里,想必大家对于事件是先到DecorView还是先到Window已经有了自己的答案了。如果觉得本文对你有所帮助,不妨把文章分享出去让更多的人看到。

    申请免费试学名额    

在职想转行提升,担心学不会?根据个人情况规划学习路线,闯关式自适应学习模式保证学习效果
讲师一对一辅导,在线答疑解惑,指导就业!

上一篇: IT程序员男的性格有什么特点?什么原因导致的? 下一篇: IT培训机构有什么热门的课程?

相关推荐 更多

热门文章

  • 前端是什么
  • 前端开发的工作职责
  • 前端开发需要会什么?先掌握这三大核心关键技术
  • 前端开发的工作方向有哪些?
  • 简历加分-4步写出HR想要的简历
  • 程序员如何突击面试?两大招带你拿下面试官
  • 程序员面试技巧
  • 架构师的厉害之处竟然是这……
  • 架构师书籍推荐
  • 懂了这些,才能成为架构师
  • 查看更多

扫描二维码,了解更多信息

博学谷二维码