Android中Touch事件分發源頭的源碼分析及內部類扮演的角色

Android上層實現java代碼實現,僅僅實現功能還不能體現實力,還要寫出一手看起來優雅的代碼,今天總結下java內部類如何可以優雅的實現Android Touch事件從源頭傳遞給Activityf分發。

外圍類的窗口及橋樑

我們從實際列子出發,看下Android源碼中如何通過作爲內部類實現Touch事件完整地傳遞由fromwork層到WMS到ViewRootImpl通過PhoneWindow聯繫到Activity再到佈局View呢。
WindowManagerService通過代理對象(IWindow.Stub.Proxy)進程間通信方式Binder通知到遠端IWindow.Stub對象,這個對象正好是ViewRootImpl的內部類W實例mWindow,因爲W是其內部類,用來和遠端進程通信,接受消息,作爲一個接收端,收到消息之後並很輕鬆通知到外圍類,可以就是一體的內部溝通,代碼如下:詳細推薦看一篇文章:http://blog.csdn.net/a992036795/article/details/51690303
我們可以把通過點擊事件把touch事件的調用流程dump出來:

  button1.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
               Thread.dumpStack();
                return false;
            }
        });

打印出來結果如下:

08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at cn.egame.terminal.paySdk.demo.MainActivity.access$100(MainActivity.java:65)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at cn.egame.terminal.paySdk.demo.MainActivity$9.onTouch(MainActivity.java:579)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.View.dispatchTouchEvent(View.java:9384)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2197)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2197)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2197)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2555)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2197)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at com.android.internal.policy.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2461)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1777)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.app.Activity.dispatchTouchEvent(Activity.java:2865)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at com.android.internal.policy.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2422)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.View.dispatchPointerEvent(View.java:9610)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4447)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4313)
08-15 20:15:09.628 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3847)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3900)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3866)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3992)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3874)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4049)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3847)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3900)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3866)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3874)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3847)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6146)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6120)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6081)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6262)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:192)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:330)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.os.Looper.loop(Looper.java:137)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5621)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
08-15 20:15:09.629 16091-16091/com.yzf0813.test W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)

通過上面整理流程可以看到ActivityThread中looper接收到event事件處理,整個入口開始處調用了InputEventReceiver.dispatchInputEvent(InputEventReceiver.java)這個是由底層調用到,WindowInputEventReceiver繼承了InputEventReceiver,並且是ViewRootImpl的內部類,繼而走到ViewRootImpl.enqueueInputEvent。在ViewRootImpl的內部類$ViewPostImeInputStage.processPointerEvent()來關聯到mView,即在setView時傳入的DecorView

private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;

            mAttachInfo.mUnbufferedDispatchRequested = false;
            boolean handled = mView.dispatchPointerEvent(event);
            if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
                mUnbufferedInputDispatch = true;
                if (mConsumeBatchedInputScheduled) {
                    scheduleConsumeBatchedInputImmediately();
                }
            }
            return handled ? FINISH_HANDLED : FORWARD;
        }

在View的實現中,dispatchPointerEvent的邏輯如下,這樣一來,點擊事件就傳遞給了mView的dispatchTouchEvent方法。

  public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }

mView是一個PhoneWindow.DecorView對象,其實現了dispatchTouchEvent(),DecorView是PhoneWindow的內部類,很方便的把事件通知到外圍類,其dispatchTouchEvent實現

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

getCallback()爲外圍類PhoneWindow的成員方法,Activity實現了Window.CallBack,在Activity.attach()方法內創建了PhoneWindow,並持有了Activity對象,其對CallBack的實現了。最後touch事件傳遞了Activity這層了,即Activity對dispatchTouchEvent的實現。

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        //this爲當前Activity
        mWindow.setCallback(this);

        mWindow.setOnWindowDismissedCallback(this);
        /*****省略代碼*****/
    }

Activity類實現了Window.Callback這個接口

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback 

setCallBack和getCallBack均爲Window的成員方法

public abstract class Window {
*******
public void setCallback(Callback callback) {
        mCallback = callback;
    }

    /**
     * Return the current Callback interface for this window.
     */
    public final Callback getCallback() {
        return mCallback;
    }
    ********
    }

總結髮現通過內部類的方式可以暴露對外窗口,作爲溝通的橋樑方便接受消息而來內部通知消息。用java中的內部類作用而言,即使內部類可以訪問外網類PhoneWindow的private和protected的域權限,就像一個對外暴露的窗口並實現隱藏,那內部類其他作用設計:

1.內部類可以很好的實現隱藏
 一般的非內部類,是不允許有 privateprotected權限的,但內部類可以
2.內部類擁有外圍類的所有元素的訪問權限
3.可是實現多重繼承
  用內部類來繼承或實現接口從而達到擴展外網類的功能
4.可以避免修改接口而實現同一個類中兩種同名方法的調用。
  用內部類來實現接口從而避免同名方法,做到同時存在不影響各自。
發佈了39 篇原創文章 · 獲贊 58 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章