其他分享
首页 > 其他分享> > 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=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。

  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。

 

 

标签:InputChannel,触摸,流转,事件,ACTION,launcher3,com,android
来源: https://blog.csdn.net/lei7143/article/details/119117299