IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> android 触摸事件流转流程 -> 正文阅读

[移动开发]android 触摸事件流转流程

Iput 子系统

https://www.cnblogs.com/Ph-one/p/4849558.html

inputReader?

https://www.jianshu.com/p/34f5c7d55337

InputDispatcher分发事件

https://www.jianshu.com/p/6e250a8ff80c

1、InputReader获取事件,最终InputReader将input event放到InputDispatcher的mInboundQueue后唤醒InputDispacher。

2、InputDispacher从mInboundQueue头部取出事件交给dispatchKeyLocked执行分发处理

3、事件处理需要找到目标窗口mFocusedWindowHandle,findFocusedWindowTargetsLocked将焦点窗口与inputTargets建立InputChannel联系

4、InputDispatcher::dispatchEventLocked根据inputTargets的InputChannel对应的fd 从mConnectionsByFd中找到connection

5、将事件发送到inputTargets,其实等于做了一次搬运的工作,将InputDispatcher中mInboundQueue中的eventEntry事件取出后, 找到目标window后,将eventEntry封装dispatchEntry加入到connection的outboundQueue队列。

6、InputDispatcher::startDispatchCycleLocked中通过InputPublisher将DispatchEntry发送给窗口,再将DispatchEntry从outboundQueue移到waitQueue里。该通信过程是异步的,当窗口处理完事件后会通过handleReceiveCallback()回调函数通知InputDispatcher::handleReceiveCallback。

7、通过connection 的InputChannel 发送事件到对应窗口

InputChannel

https://www.jianshu.com/p/b09afd403f71

InputDispatcher通过publishKeyEvent把input事件发送给客户端,InputDispatcher是属于system_server进程,而客户端属于应用进程,两种通信属于跨进程通信,分析下system_server与应用建立通信的过程。

  1. frameworks/base/core/java/android/view/ViewRootImpl.java?接口setView?中创建客户端的InputChannel对象mInputChannel = new InputChannel();

2、frameworks/native/libs/input/InputTransport.cpp

InputTransport?中会创建socketpair对,一个是服务端套接字,一个是客户端套接字。

3、frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

应用在setView()的是会调用IWindowSession的addToDisplay()函数。这是添加窗口流程,它包含了App与SurfaceFlinger服务建立连接的部分,也是包含了InputChannel建立连接的部分。它本身是个binder调用,服务端是WMS,最终调用的是WMS的addWindow方法。

InputChannel通过InputChannel.openInputChannelPair分别窗建一对InputChannel,然后将Server端的InputChannel注册到InputDispatcher中,将Client端的InputChannel返回给客户端应用。socket pair的创建过程这里不详细说,简单总结下:openInputChannelPair 生成了两个Socket的fd, 代表一个双向通道的两端。初始化了两端的包括Native层和Java层的InputChannel对象,InputChannel封装了name和fd。

frameworks/native/services/inputflinger/InputDispatcher.cpp

Server端的InputChannel注册到InputDispatcher中

?

?

一个Connection 对象被创建出来,这个Connection表示客户端和服务端的一个输入数据通道,每个Connection都对应一个服务端的InputChannel,每个服务端的InputChannel又对应一个socket pair的fd。InputDispatcher用fd为索引,将所有的Connection保存在mConnectionByFd中再将这个fd注册在InputDispatcher的Looper的监控列表里,这样一旦对端(客户端)的socket写入数据,Looper就会被唤醒,接着就会调用回调函数handleReceiveCallback。另外,一个Dispatcher可能有多个Connection(多个Window)同时存在。

4、client端获取客户端的socket fd

inputChannels[1].transferTo(outInputChannel);

这个outInputChannel是客户端setView创建的,当参数传递过来的。这里很就是将client端的fd传给outInputChannel

在setView中会创建WindowInputEventReceiver对象,构造方法会调用super,在其父类InputEventReceiver的构造方法中会执行nativeInit,最终执行如下代码:

frameworks/base/core/jni/android_view_InputEventReceiver.cpp

?

此处的Looper便是应用主线程的Looper,将socket客户端的fd添加到应用线程的Looper来监听,当服务端有事件,触发回调方法NativeInputEventReceiver::handleEvent()

至此,socket正式建立连接。

InputEventReceiver 接收事件处理

https://www.cnblogs.com/TaigaCon/p/4750349.html

Android应用处理MotionEvent的过程

https://www.jianshu.com/p/c2e26c6d4ac1

https://blog.csdn.net/fisher_2005/article/details/76423409

https://www.jianshu.com/p/bc4c9e5f4b1c

?

?

?

?

打印log:

InputReader: dispatchTouches action DOWN now(ns)

InputEventReceiver: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' ~ Consuming input events, consumeBatches=false, frameTime=-1

InputTransport: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' consumer ~ consume: consumeBatches=false, frameTime=-1

InputTransport: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' consumer ~ consumed motion event, seq=1925

InputEventReceiver: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' ~ Dispatching input event.

?

?

打印 ?Dispatching batched input event pending notification

?

frameworks\base\core\java\android\view\InputEventReceiver.java

private void dispatchBatchedInputEventPending() {
????onBatchedInputEventPending();
}

?

frameworks\base\core\java\android\view\ViewRootImpl.java

final class WindowInputEventReceiver extends InputEventReceiver {
????public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
????????super(inputChannel, looper);
????}
????????????
????@Override
????public void onBatchedInputEventPending() {
????????if (mUnbufferedInputDispatch) {
????????????super.onBatchedInputEventPending();
????????} else {
????????????scheduleConsumeBatchedInput();
????????}
????}

void scheduleConsumeBatchedInput() {
????if (!mConsumeBatchedInputScheduled) {
????????mConsumeBatchedInputScheduled = true;
????????mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
????????????????mConsumedBatchedInputRunnable, null);
????}
}

?

对应channel 'WindowManager (client)' 是InputEventReceiver中方法

frameworks\base\core\java\android\view\InputEventReceiver.java

打印log:

InputReader: dispatchTouches action DOWN now(ns):

InputEventReceiver: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' ~ Consuming input events, consumeBatches=false, frameTime=-1

InputTransport: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' consumer ~ consume: consumeBatches=false, frameTime=-1

InputTransport: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' consumer ~ started batch event

InputEventReceiver: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' ~ Dispatching batched input event pending notification.

?

?

?

打印log:

InputReader: dispatchTouches action MOVE now(ns):

InputEventReceiver: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' ~ Consuming input events, consumeBatches=false, frameTime=-1

InputTransport: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' consumer ~ consume: consumeBatches=false, frameTime=-1

09-26 15:51:57.219 19960 19960 D InputTransport: channel '4893658 com.android.launcher3/com.android.launcher3.Launcher (client)' consumer ~ appended to batch event

等待Vsync 到来时,调用mConsumedBatchedInputRunnable

?


void doConsumeBatchedInput(long frameTimeNanos) {
????if (mConsumeBatchedInputScheduled) {
????????mConsumeBatchedInputScheduled = false;
????????if (mInputEventReceiver != null) {
????????????boolean consumed = mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos);
????????????if (consumed && frameTimeNanos != -1) {
????????????????scheduleConsumeBatchedInput();
????????????}
????????}
????????doProcessInputEvents();
????}
}

Vsync 到来,执行mInputEventReceiver.consumeBatchedInputEvents,这里会分发事件,也最后调到doProcessInputEvents()。如果条件满足则执行scheduleConsumeBatchedInput()为下个Vsync准备Choreographer.CALLBACK_INPUT?消息

doProcessInputEvents();?再次分发处理事件,一般这个时候事件队列为空

如果事件队列事件不为空,就会分发事件,并在trace 中有deliverInputEvent?标签

?

?

?

?

?

http://www.masonchang.com/blog/2014/8/25/androids-touch-resampling-algorithm

帧绘制60Hz,屏刷新扫描100Hz,从而底层上报move 事件是10ms(10?pixels) ,重采样频率是11ms。

?

?

事件内插处理

Vsync 到来时,如果采样时间点附近一个事件在采样点之前,一个在采样点跟Vsync之间,则将这两个事件做内插处理。

?vsync t=32 ms?st=27 ms,则t1=20 ms ?t2=30 ms?两个事件

?

?

Alpha 越大,越接近第二个事件;越小越接近第一个事件。

事件外插处理

vsync t=48st= 43ms,则t1=30?ms ?t2=40?ms?两个事件

?

重采样处理的结果:事件如下,除了第一个事件,都是16ms 一个事件。

?

?

514-484=30

30*0.3=9

484 + 9 = 493

432.494 - 409 = 23.494

23.494 * 0.242 = 5.566

432.494 + ?5.566 = 438

Choreographer?Vsnc控制

https://www.jianshu.com/p/47c866f6fb67

事件java层分发

?

https://blog.csdn.net/a992036795/article/details/51690303

https://www.jianshu.com/p/1c44bc932970

https://www.jianshu.com/p/bc4c9e5f4b1c

?

?

?

View分为两种,第一种可以有子View的直接或者间接继承ViewGroup,另一种不允许有子View的,直接或者间接继承View的,如TextView,ImageView等等。

每个Activity包含一个window,每个window都是一个phoneWindow,每个PhoneWindow包含一个DecorView,每一个DecorView都是一个Framelayout,Framelayout继承ViewGroup,ViewGroup继承View。

直接继承View 控件事件处理,如TextView

可以重写onTouchEventdispatchTouchEvent,另外setOnTouchListener可以设置onTouch,setOnClickListener可以设置onClick。

  1. 事件到来,执行dispatchTouchEvent
  2. 当设置了onTouch,则执行onTouch;如果onTouch 返回True,则表示事件被消耗了,结束;如果返回false,则会调用onTouchEvent

3、onTouchEvent?系统默认返回true;如果返回false,表示该View 不处理事件,后续的ACTION_MOVE、ACTION_UP就不会执行dispatchTouchEvent

默认事件执行:

第一组Action=0:ACTION_DOWN事件,从输出结果可以看出,执行顺序为了dispatchTouchEvent->onTouch->onTouchEvent。

第二组Action=1:ACTION_UP事件,执行顺序与之前相同,但是在末尾多了一个onClick

onTouch 返回true

第一组Action=0:ACTION_DOWN事件,从输出结果可以看出,执行顺序为了dispatchTouchEvent->onTouch结束了,后面的onTouchEvent没有再执行。

第二组??Action=2:ACTION_MOVE事件,与ACTION_DOWN事件事件的执行顺序相同。

第二组??Action=1:ACTION_UP事件,与ACTION_DOWN事件事件的执行顺序相同。

onTouchEvent 返回false

执行顺序变成dispatchTouchEvent->onTouch->onTouchEvent,但是整个事件分发过程中,只ACTION_DOWN响应,之后的事件都不再响应。当 onTouchEvent(event)返回false时,result也就返回false了,这个时候就代表dispatchEvent没有消费事件,后续的Action也就不会再执行了。这是因为这个view 没要加入事件处理目标链表。

长按跟click 都是在onTouchEvent中处理的。

?

ViewGroup事件

在ViewGroup当中涉及到事件分发的方法,相比View来说除了dispatchTouchEvent和onTouchEvent外还有onInterceptTouchEvent(默认返回false,不拦截)。

FrameLayout 中有一个TextView

点击TextView 事件响应:

  1. 第一组Action=0:ACTION_DOIWN事件,先执行了dispatchTouchEvent,然后是onInterceptTouchEvent,这两个都是FrameLayout的方法,之后的事件被传递到了TextView当中,按照我们之前说的顺序dispatchTouchEvent->onTouch->onTouchEvent进行事件分发。
  2. 第二组Action=2:ACTION_MOVE事件,与第一组的时间顺序相同。
  3. 第三组Action=1:ACTION_UP事件,与上述步骤基本相同,但是在输出末尾执行了View的onClick事件,这就与我们之前分析的View当中的事件分发机制不谋而合。

ViewGroup的事件分发与dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent三个方法有关,但是从这个例子当中我们发现所执行的方法只有dispatchTouchEvent和onInterceptTouchEvent,那么onTouchEvent在什么时候执行呢?我们设置的OnTouch方法以及OnClick方法在什么时候调用呢?

当点击TextView 外FrameLayout区域

同样的还是以不同的Action来分组分析:

  1. 第一租Action=0:ACTION_DOIWN事件,执行顺序为dispatchTouchEvent->onInterceptTouchEvent->onTouch->onTouchEvent,而且四个方法全部都是FrameLayout内的方法输出,没有了View事件。
  2. 第二组Action=2:ACTION_MOVE事件,执行顺序为dispatchTouchEvent->onTouch->onTouchEvent,到了这里onInterceptTouchEvent方法又不见了。
  3. 第三组Action=1:ACTION_UP事件,dispatchTouchEvent->onTouch->onTouchEvent->onClick

?

以为Down 事件会执行onInterceptTouchEvent,后面由于没找到view,mFirstTouchTarget 是null,所以之后就不执行onInterceptTouchEvent。

?

?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-27 16:20:36  更:2021-07-27 16:20:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/5 3:48:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码