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与应用建立通信的过程。
- 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=48,st= 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
可以重写onTouchEvent和dispatchTouchEvent,另外setOnTouchListener可以设置onTouch,setOnClickListener可以设置onClick。
- 事件到来,执行dispatchTouchEvent。
- 当设置了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 事件响应:
- 第一组Action=0:ACTION_DOIWN事件,先执行了dispatchTouchEvent,然后是onInterceptTouchEvent,这两个都是FrameLayout的方法,之后的事件被传递到了TextView当中,按照我们之前说的顺序dispatchTouchEvent->onTouch->onTouchEvent进行事件分发。
- 第二组Action=2:ACTION_MOVE事件,与第一组的时间顺序相同。
- 第三组Action=1:ACTION_UP事件,与上述步骤基本相同,但是在输出末尾执行了View的onClick事件,这就与我们之前分析的View当中的事件分发机制不谋而合。
ViewGroup的事件分发与dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent三个方法有关,但是从这个例子当中我们发现所执行的方法只有dispatchTouchEvent和onInterceptTouchEvent,那么onTouchEvent在什么时候执行呢?我们设置的OnTouch方法以及OnClick方法在什么时候调用呢?
当点击TextView 外FrameLayout区域
同样的还是以不同的Action来分组分析:
- 第一租Action=0:ACTION_DOIWN事件,执行顺序为dispatchTouchEvent->onInterceptTouchEvent->onTouch->onTouchEvent,而且四个方法全部都是FrameLayout内的方法输出,没有了View事件。
- 第二组Action=2:ACTION_MOVE事件,执行顺序为dispatchTouchEvent->onTouch->onTouchEvent,到了这里onInterceptTouchEvent方法又不见了。
- 第三组Action=1:ACTION_UP事件,dispatchTouchEvent->onTouch->onTouchEvent->onClick
以为Down 事件会执行onInterceptTouchEvent,后面由于没找到view,mFirstTouchTarget 是null,所以之后就不执行onInterceptTouchEvent。
标签:InputChannel,触摸,流转,事件,ACTION,launcher3,com,android 来源: https://blog.csdn.net/lei7143/article/details/119117299