总结系列——聊聊android里面的事件分发

本文详细梳理了Android事件分发的全过程,从InputReader到InputDispatcher,再到View体系的事件处理。探讨了Activity是否是事件分发起点、InputChannel与SocketPair的作用,以及ACTION_CANCEL的产生等关键点。此外,还涉及到了长按事件、多点触控和ANR等相关问题,通过引用多篇优质博客资源,帮助读者深入理解Android事件分发机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

android 中的事件分发,已经是老生常谈的问题了,一般都是从Activity开始聊,但是真的是这样吗?hongyang大神之前一篇纠错,解决了我一个很大的困惑,也算是扫盲吧,这篇文章,是从头到尾来梳理一遍,android事件分发。
楼主这两天发现一个博主比我写的好的多,特贴出地址如下:
反思|Android 事件分发机制的设计与实现

流程

我自信不会比网上的很多大神写的博客更好,所以这一篇相当于组合,把相关代码组合在一起。
这里我不会贴过多代码,但会给出相关参考连接。其实很多东西不需要一行一行去读代码,知道流程,场景,问题,应对方式就可以了,就能学以致用了。

首先,事件分发的起点在哪里?这里大家其实可以去网上搜索,其实很多博客都有介绍,是InputReader去读取硬件事件,然后分发到InputDispatcher,这部分博客流程和细节可以参考Android Input(三)-InputReader获取事件,还有Android触摸事件的传递(四-1)–输入系统-InputReader,当然也有偏向硬件层面的Android事件分发完全解析之事件从何而来,大家也可以自己去网上搜索,搜索InputReader就行。
然后经过InputDispatcher来进行分发,这里详细的流程可以参看gityuan大神的博客:Input系统—ANR原理分析,流程很详细。
其中涉及到三个队列,以及InputChannal,InputChannal是个很有意思的东西,之前看源码都没有在意过,后来才发现,原来他的初始化,就是在ViewRootImpl里面,详细可以参考博客Android输入系统IMS(2)–基础知识socketpair,InputChannal其实就是通过socket来进行通信,注意其中一句话

实际上socketpair 函数跟pipe 函数是类似的,也只能在同个主机上具有亲缘关系的进程间通信,但pipe 创建的匿名管道是半双工的,而socketpair 可以认为是创建一个全双工的管道。

摘抄自 Android输入系统IMS(2)–基础知识socketpair

与pipe的区别:pipe是单工通信,一端要么是读端要么是写端,而socketpair实现了双工套接字,也就没有所谓的读端和写端的区分。

摘抄自Android Framework 输入子系统(02)核心机制 双向通信(socketpair+binder)

InputChannal既然是通过ViewRootImpl里面的setView来创建的,但是Socket的创建并不在应用端,而是在WMS端,再把InputChannel和socket绑定起来,这里有篇博客关于InputChannel和SocketPair这块总结的很好,Android Input(五)-InputChannel通信

这里如果对SocketPair不了解或者有疑问的话,可能无法理解soketPair怎么明明在wms端创建,怎么实现和app端通信。其实重点就是socket的fd,以上的博客有讲解,可以了解下。
接下来,就进入了我们的事件分发流程体系了(View体系)。
上面这几篇博文几乎囊括了事件分发的framework层所有知识点,暂时不涉及到我们应用层的事件分发,因为这一块实在是烂大街了,随便一搜索到处都是,我就不提供参考博客了。从这里,我们也能看到其中一些,容易搞混的知识点,下面简单提出几个,以后遇到了,会不定期来更新。

问题

Activity是事件分发的起点吗?

这个问题,说是也不是,说不是也是。说是Activity,是因为我们上层能感知到的,确实是从Activity开始。但是,如果阅读了上面的内容,我相信你一定清楚,作为和InputDispatcher交互的通道,首先到达的肯定是ViewRootImpl,因为是通过InputChannel通信的。注意WMS和InputDispatcher是一个进程,不同线程。
那么先到达VRI(以后ViewRootImpl都简写为VRI)的哪里呢?
在setView里面,同时注册了一个WindowInputEventReceiver,并把InputChannel和Looper传进去了。其中有一个方法名叫onInputEvent,很明显,这里就是接收输入事件的。
在这里插入图片描述
再看onInputEvent的父类InputEventReceiver的怎么说:

//InputEventReceiver:

    /**
     * Called when an input event is received.
     * The recipient should process the input event and then call {@link #finishInputEvent}
     * to indicate whether the event was handled.  No new input events will be received
     * until {@link #finishInputEvent} is called.
     *
     * @param event The input event that was received.
     */
    @UnsupportedAppUsage
    public void onInputEvent(InputEvent event) {
   
        finishInputEvent(event, false);
    }

	//这里是唯一调用onInputEvent的地方。
    // Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    private void dispatchInputEvent(int seq, InputEvent event) {
   
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }

从Called from native code.就能看出来是底层的回调了。
看着这种方式,有没有很熟悉?没错,同样是ViewRootImpl,用来监听底层屏幕刷新回调的Choreographer也是使用这种方式。
具体注册是在

//DisplayEventReceiver
    /**
     * Schedules a single vertical sync pulse to be delivered when the next
     * display frame begins.
     */
    @UnsupportedAppUsage
    public void scheduleVsync() {
   
        if (mReceiverPtr == 0) {
   
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
   
            nativeScheduleVsync(mReceiverPtr);
        }
    }

底层回调是在


    // Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
   
        onVsync(timestampNanos, physicalDisplayId, frame);
    }

好了,这个问题就说到这。

分发流程?

事件分发流程,老生长谈了,看完了前面的几篇博客,几乎也明白整个流程,我们这里再强调下,我们不说前面的,也不说后面的view分发,说说中间的,毫无疑问,最开始到达,InputEventReceiver之后,会通过Window.Callback来传递给Activity:

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

这里的Window.Callback就是Activi

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值