天天看點

Android實驗法分析Touch事件傳遞

# 1、概述

本文通過簡單的實驗法擷取Android事件傳遞

# 2、測試代碼

測試代碼很簡單,随便寫一個按鈕監聽onTouch事件,在onTouch函數中抛出一個異常,代碼如下:

```Java

      Button button = findViewById(R.id.btn_test);

      button.setOnTouchListener(new View.OnTouchListener() {

            @Override

            public boolean onTouch(View view, MotionEvent motionEvent) {

                Log.d(TAG," onTouch "+ motionEvent);

                throw new RuntimeException("event touch test");

//                return false;

            }

       });

``` 

# 3、測試結果:

結果當然崩了,不會寫bug的程式員不是好程式員

```

2019-07-15 20:55:37.297 18471-18471/com.test.playground D/EventTestActivity:  onTouch MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=118.0, y[0]=29.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=642618046, downTime=642618046, deviceId=9, source=0x1002 }

2019-07-15 20:55:37.300 18471-18471/com.test.playground E/InputEventReceiver: Exception dispatching input event.

2019-07-15 20:55:37.301 18471-18471/com.test.playground E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback

2019-07-15 20:55:37.302 18471-18471/com.test.playground E/MessageQueue-JNI: java.lang.RuntimeException: event touch test

        at com.test.playground.EventTestActivity$1.onTouch(EventTestActivity.java:23)

        at android.view.View.dispatchTouchEvent(View.java:10019)

        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)

        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)

        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)

        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)

        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)

        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)

        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2632)

        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2264)

        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:414)

        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1808)

        at android.app.Activity.dispatchTouchEvent(Activity.java:3064)

        at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:376)

        at android.view.View.dispatchPointerEvent(View.java:10243)

        at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4438)

        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4306)

        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)

        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)

        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)

        at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3999)

        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)

        at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4056)

        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)

        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3906)

        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3872)

        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3880)

        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3853)

        at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6247)

        at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6221)

        at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6182)

        at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6350)

        at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)

        at android.os.MessageQueue.nativePollOnce(Native Method)

        at android.os.MessageQueue.next(MessageQueue.java:323)

        at android.os.Looper.loop(Looper.java:136)

        at android.app.ActivityThread.main(ActivityThread.java:6121)

        at java.lang.reflect.Method.invoke(Native Method)

        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)

        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)

2019-07-15 20:55:37.303 18471-18471/com.test.playground D/AndroidRuntime: Shutting down VM

```

# 4 原理分析

## 4.1 事件從螢幕觸摸到應用程序簡述

觸摸事件産生的大緻原理是:使用者對硬體進行操作(觸摸屏)會導緻這個硬體産生對應的中斷。該硬體的驅動程式會處理這個中斷。不同的硬體驅動程式處理的方式不同,不過最終都是将資料處理後存放進對應的/dev/input/eventX檔案中。是以硬體驅動程式完成了觸摸事件的資料收集。

在native層主要是通過下面3個元件來對觸摸事件進行處理的,這3個元件都運作在系統服務中:

- EventHub : 它的作用是監聽、讀取/dev/input目錄下産生的新事件,并封裝成RawEvent結構體供InputReader使用。

- InputReader : 通過EventHub從/dev/input節點擷取事件資訊,轉換成EventEntry事件加入到InputDispatcher的mInboundQueue隊列中。

- InputDispatcher : 從mInboundQueue隊列取出事件,轉換成DispatchEntry事件加入到Connection的outboundQueue隊列。然後使用InputChannel分發事件到java層

## 4.2 應用程序中Touch系統架構原理

![圖1-Android事件傳遞類圖](https://upload-images.jianshu.io/upload_images/16477783-141711068790a784.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- WindowInputEventReceiver: 在ViewRootImpl.setView中被初始化,當事件到來時會從native中回調onInputEvent方法到java層。是事件派發的動力所在。

- ViewRootImpl: 處理視圖方面的,同Input, Window等服務互動的大管家,會在Activity顯示視圖時初始化

- InputStage: 被抽象成責任鍊模式的父類,代表着事件處理的階段

- View, ViewGroup, DecorView: 視圖樹的主要組成成分

從上面調用棧中可以得到下面調用的時序圖:

![圖2-Event傳遞時序圖](https://upload-images.jianshu.io/upload_images/16477783-7662aed97147975b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## 4.3 應用開發階段事件傳遞

 從3 的調用棧中可以看出 

![圖3-Touch事件在應用開發階段傳遞流程](https://upload-images.jianshu.io/upload_images/16477783-a6e9782be819799c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

這一塊有很多朋友都寫過,這裡就不在重複。

# 5 總結

本文通過onTouch的調用棧為線索,檢視逐級代碼。了解系統代碼有助于加深對系統的了解,對于開發者而言隻需要了解Activity、ViewGroup、View直接的傳遞。

由于能力有限,有些地方描述不夠詳盡,請見諒。