Android點(diǎn)擊事件分發(fā)機(jī)制源碼分析1——Activity

Android點(diǎn)擊事件分發(fā)機(jī)制源碼分析1——Activity
Android點(diǎn)擊事件分發(fā)機(jī)制源碼分析2——ViewGroup
Android點(diǎn)擊事件分發(fā)機(jī)制源碼分析3——View

一 產(chǎn)生點(diǎn)擊事件

當(dāng)用戶(hù)用點(diǎn)擊顯示屏產(chǎn)生一個(gè)點(diǎn)擊事件,本章討論點(diǎn)擊事件是如何傳到Activity上的,Android 源碼為sdk25。

二 系統(tǒng)如何將點(diǎn)擊事件派發(fā)給Activity

1. handleLaunchActivity

啟動(dòng)一個(gè)新的Activity的時(shí)候,會(huì)調(diào)用ActivityThread的handleLaunchActivity方法,我們從這里開(kāi)始看:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    //最終回調(diào)目標(biāo)Activity的onConfigurationChanged()
    handleConfigurationChanged(null, null);
    //初始化wms
    WindowManagerGlobal.initialize();
    //最終回調(diào)目標(biāo)Activity的onCreate
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        Bundle oldState = r.state;
        //最終回調(diào)目標(biāo)Activity的onStart,onResume.
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed);

        if (!r.activity.mFinished && r.startsNotResumed) {
            r.activity.mCalled = false;
            mInstrumentation.callActivityOnPause(r.activity);
            r.paused = true;
        }
    } else {
        //存在error則停止該Activity
        ActivityManagerNative.getDefault()
            .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
    }
}

handleLaunchActivity主要做兩件事:

  1. 首先調(diào)用performLaunchActivity,該在函數(shù)內(nèi)部通過(guò)Java反射機(jī)制創(chuàng)建目標(biāo)Activity,然后調(diào)用它的onCreate及onStart函數(shù)。
  2. 調(diào)用handleResumeActivity,會(huì)在其內(nèi)部調(diào)用目標(biāo)Activity的onResume函數(shù)。

1.1 進(jìn)入performLaunchActivity:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }

    ComponentName component = r.intent.getComponent();
    if (component == null) {
        component = r.intent.resolveActivity(
            mInitialApplication.getPackageManager());
        r.intent.setComponent(component);
    }

    if (r.activityInfo.targetActivity != null) {
        component = new ComponentName(r.activityInfo.packageName,
                r.activityInfo.targetActivity);
    }

   // 通過(guò)反射創(chuàng)建activity實(shí)例
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        ...
    }

    try {
        //創(chuàng)建Application對(duì)象
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        if (activity != null) {
            Context appContext = createBaseContextForActivity(r, activity);
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            //調(diào)用activity的attach方法
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor);

            if (customIntent != null) {
                activity.mIntent = customIntent;
            }
            r.lastNonConfigurationInstances = null;
            activity.mStartedActivity = false;
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }

            activity.mCalled = false;
            //最終會(huì)調(diào)用Activity的onCreate方法
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            ...
            r.activity = activity;
            r.stopped = true;
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }
            if (!r.activity.mFinished) {
                if (r.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            if (!r.activity.mFinished) {
                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state,
                            r.persistentState);
                } else {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state);
                }
                ...
            }
        }
        r.paused = true;

        mActivities.put(r.token, r);

    }  catch (Exception e) {
        ...
    }

    return activity;
}

主要做了三件事:

  1. 創(chuàng)建一個(gè)activity實(shí)例
  2. 調(diào)用activity的attach方法
  3. 通過(guò)mInstrumentation來(lái)回調(diào)activity的onCreate方法

1.1.1 activity的attach方法

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*/);

       // 創(chuàng)建window,可以看出來(lái)實(shí)際上是創(chuàng)建一個(gè)PhoneWindow 
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
       //設(shè)置mwindow回調(diào),會(huì)讓window持有activity的引用
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();
       
        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }
       
       // 設(shè)置windowManager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

主要做三件事:

  1. 創(chuàng)建創(chuàng)建出window 它的實(shí)現(xiàn)是PhoneWindow
  2. 給window設(shè)置回調(diào)接口,這個(gè)接口的實(shí)現(xiàn)是activity自己
  3. 給window設(shè)置windowManager
1.1.1.1 activity的callback方法
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback {

activity實(shí)現(xiàn)了Window.Callback,看下Callback接口做了什么:

public interface Callback {
        /**
         * Called to process key events.  At the very least your
         * implementation must call
         * {@link android.view.Window#superDispatchKeyEvent} to do the
         * standard key processing.
         *
         * @param event The key event.
         *
         * @return boolean Return true if this event was consumed.
         */
        public boolean dispatchKeyEvent(KeyEvent event);

        /**
         * Called to process a key shortcut event.
         * At the very least your implementation must call
         * {@link android.view.Window#superDispatchKeyShortcutEvent} to do the
         * standard key shortcut processing.
         *
         * @param event The key shortcut event.
         * @return True if this event was consumed.
         */
        public boolean dispatchKeyShortcutEvent(KeyEvent event);

        /**
         * Called to process touch screen events.  At the very least your
         * implementation must call
         * {@link android.view.Window#superDispatchTouchEvent} to do the
         * standard touch screen processing.
         *
         * @param event The touch screen event.
         *
         * @return boolean Return true if this event was consumed.
         */
        public boolean dispatchTouchEvent(MotionEvent event);
        //代碼省略
}

可以看到callback里面有一個(gè)dispatchTouchEvent方法,actiivty里的dispatchTouchEvent就是對(duì)這個(gè)方法的具體實(shí)現(xiàn)。那么這個(gè)window.callback里的點(diǎn)擊事件又是從哪里傳過(guò)來(lái)的,dispatchTouchEvent又是什么時(shí)候被調(diào)用的呢?繼續(xù)看

1.1.2 調(diào)用activity的onCreate方法

看下mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);方法的內(nèi)部實(shí)現(xiàn):

public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
    }

activity.performCreate的內(nèi)部:

   final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        restoreHasCurrentPermissionRequest(icicle);
        //調(diào)用activity的 onCreate
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

2. handleResumeActivity

 final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
            ....
           // 最終會(huì)調(diào)用activity.onResume()方法
        // TODO Push resumeArgs into the activity for consideration
        ActivityClientRecord r = performResumeActivity(token, clearHide);
            ...
                if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                //獲得一個(gè)View對(duì)象
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                // 獲得ViewManager對(duì)象
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    //把剛才的decor對(duì)象加入到ViewManager中
                    wm.addView(decor, l);
                }
            ...
}


主要做了兩件事:

  1. 調(diào)用activity的onResume方法
  2. 將view和window關(guān)聯(lián)在一塊

2.1 調(diào)用activity的onResume方法

在handleResumeActivity里面調(diào)用了performResumeActivity:

    public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
            ...
            //調(diào)用 activity.perforResume()
               r.activity.performResume();
}

它會(huì)調(diào)用activity里的performResume:

final void performResume() {
        performRestart();

        mFragments.execPendingActions();

        mLastNonConfigurationInstances = null;

        mCalled = false;
        // mResumed is set by the instrumentation
        mInstrumentation.callActivityOnResume(this);
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onResume()");
        }

        // invisible activities must be finished before onResume() completes
        if (!mVisibleFromClient && !mFinished) {
            Log.w(TAG, "An activity without a UI must call finish() before onResume() completes");
            if (getApplicationInfo().targetSdkVersion
                    > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
                throw new IllegalStateException(
                        "Activity " + mComponent.toShortString() +
                        " did not call finish() prior to onResume() completing");
            }
        }

        // Now really resume, and install the current status bar and menu.
        mCalled = false;

        mFragments.dispatchResume();
        mFragments.execPendingActions();

        onPostResume();
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPostResume()");
        }
    }

主要做了兩件事:

  1. 調(diào)用 performRestart();最終會(huì)調(diào)用activity的onStart方法
  2. 使用mInstrumentation 來(lái)控制activity的 onResume執(zhí)行

3 將xml文件添加到decorView

通過(guò)上面的分析可以知道,在activity的onResume方法執(zhí)行之后,會(huì)將decorView添加到窗口,那么我們自己寫(xiě)的布局是什么時(shí)候添加到窗口上面取得呢?我們一般都是在onCreate方法中調(diào)用setContentView方法來(lái)設(shè)置自己寫(xiě)的布局的。那么就先分析一下setContentView

3.1 setContentView

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

getWindow()返回的就是在attach方法里面創(chuàng)建的PhoneWindow。
看一下官方是怎么解釋W(xué)indow這個(gè)類(lèi)的:

· Window:abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

指的是window是一個(gè)被加到window manager的頂層view。
再看一下View這個(gè)類(lèi)官方的解釋?zhuān)?/p>

· View:This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling.

View是一個(gè)基本的用于用戶(hù)交互的不見(jiàn),它占據(jù)了屏幕上的一塊區(qū)域,用于繪制和事件處理

我們進(jìn)入PhoneWindow的setContentView方法:

   @Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
           //mContentParent為ViewGroup類(lèi)型,它的初值為null
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

主要做了以下事情:

  1. 創(chuàng)建一個(gè)DecorView,并初始化這個(gè)decorView
  2. 將我們自己的布局添加到mContentParent

3.1.1 創(chuàng)建并初始化DecorView

看一下installDecor的內(nèi)部實(shí)現(xiàn):

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);

        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);


        }
    }

它做了下面的事情:

  1. 調(diào)用generateDecor創(chuàng)建一個(gè)DecorView,并初始化
  2. 初始化mContentParent
3.1.1.1 初始化DecorView
 protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

創(chuàng)建一個(gè)DecorView

3.1.1.2 初始化mContentParent
protected ViewGroup generateLayout(DecorViewdecor){

  ......

  intlayoutResource;

  intfeatures = getLocalFeatures();

  if((features & ((1 << FEATURE_LEFT_ICON) |(1 <<FEATURE_RIGHT_ICON))) != 0) {

      if(mIsFloating) {

      //根據(jù)情況取得對(duì)應(yīng)標(biāo)題欄的資源id

     layoutResource =  com.android.internal.R.layout.dialog_title_icons;

     }

       ......

}

  mDecor.startChanging();
 View in =mLayoutInflater.inflate(layoutResource, null);
 //加入標(biāo)題欄
 decor.addView(in,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

   /*

ID_ANDROID_CONTENT的值為”com.android.internal.R.id.content”

     這個(gè)contentParent由findViewById返回,實(shí)際上就是mDecorView的一部分。

   */

   ViewGroupcontentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

   ......

   mDecor.finishChanging();

   return contentParent;

}

主要做了兩件事:

  1. decorView設(shè)置標(biāo)題欄的信息
  2. 初始化contentParent,contentParent是decorView的一部分,其實(shí)decorview內(nèi)部有兩個(gè)子元素:titlebar和contentParent

4 DecorView和Window

再回到AcrivityThread的handleResumeActivity方法,會(huì)調(diào)用

wm.addView(decor, l);

decor就是decorview,他是自定義布局的頂層布局,我們?cè)趕etContentView的時(shí)候已經(jīng)把自己寫(xiě)的布局添加到decorView里了,所以它現(xiàn)在代表的就是我們的自定義布局,現(xiàn)在調(diào)用addView方法的目的就是把我們自己的布局添加到窗口上。
wm是WindowManager實(shí)例,它的具體實(shí)現(xiàn)類(lèi)是WindowManagerImpl,WindowManagerImpl將實(shí)際的操作委托給一個(gè)名為mGlobal的成員來(lái)完成,mGlobal的類(lèi)型是WindowManagerGlobal。現(xiàn)在看下WindowManagerGlobal的addview方法。

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    
        synchronized (mLock) {

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

上面代碼先創(chuàng)建一個(gè)ViewRootImpl實(shí)例,然后將DecorView、viewRootImpl、wparams方別放入mViews、mRoots和mParams中,完成窗口信息的添加工作,然后通過(guò)ViewRoot和WMS通信,完成新窗口的添加工作。

5 ViewRootImpl

下面看一下root.setView(view, wparams, panelParentView)方法的內(nèi)部實(shí)現(xiàn),因?yàn)楸疚闹魂P(guān)注點(diǎn)擊事件分發(fā)機(jī)制,所以只保留setView方法中和點(diǎn)擊事件相關(guān)的代碼:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }

             
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

                view.assignParent(this);
            }
        }
    }

和點(diǎn)擊事件相關(guān)的流程如下:

  1. 當(dāng)窗口不包含INPUT_FEATURE_NO_INPUT_CHANNEL屬性的時(shí)候,創(chuàng)建并初始化InputChannel,InputChannel是窗口接收來(lái)自InputDispatcher輸入的管道。
  2. 會(huì)調(diào)用 mWindowSession.addToDisplay方法將窗口添加到WMS中,之后InputChannel就可以準(zhǔn)備接收各種事件了。
  3. 最后會(huì)創(chuàng)建一個(gè)WindowInputEventReceiver用于接收并處理輸入事件。

當(dāng)一個(gè)輸入時(shí)間被派發(fā)給ViewRootImpl所在的窗口時(shí),Looper就會(huì)喚醒并觸發(fā)InputEventReceiver的onInputEvent()回調(diào)(引用自《深入理解Android卷3》)。

我們看下其源碼:

    /**
     * Called when an input event is received.
     * The recipient should process the input event and then call {@link #finishInputEvent}
     * to indicate whether the event was handled.  No new input events will be received
     * until {@link #finishInputEvent} is called.
     *
     * @param event The input event that was received.
     */
    public void onInputEvent(InputEvent event) {
        finishInputEvent(event, false);
    }

不過(guò)finishInputEvent方法并不會(huì)被實(shí)現(xiàn),因?yàn)楸徽{(diào)用的對(duì)象實(shí)際上是WindowInputEventReceiver,而WindowInputEventReceiver內(nèi)部會(huì)重寫(xiě)onInputEvent方法:

        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }

它會(huì)調(diào)用:

void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

        // Always enqueue the input event in order, regardless of its time stamp.
        // We do this because the application or the IME may inject key events
        // in response to touch events and we want to ensure that the injected keys
        // are processed in the order they were received and we cannot trust that
        // the time stamp of injected events are monotonic.
        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

被傳入的processImmediately值為true,所以會(huì)執(zhí)行doProcessInputEvents:

 void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

            mPendingInputEventCount -= 1;
            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                    mPendingInputEventCount);

            long eventTime = q.mEvent.getEventTimeNano();
            long oldestEventTime = eventTime;
            if (q.mEvent instanceof MotionEvent) {
                MotionEvent me = (MotionEvent)q.mEvent;
                if (me.getHistorySize() > 0) {
                    oldestEventTime = me.getHistoricalEventTimeNano(0);
                }
            }
            mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);

            deliverInputEvent(q);
        }

在這個(gè)方法中會(huì)處理所有的輸入事件,它會(huì)遍歷調(diào)用deliverInputEvent:

private void deliverInputEvent(QueuedInputEvent q) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                q.mEvent.getSequenceNumber());
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
        }

        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }

        if (stage != null) {
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }

stage.deliver(q);這里用到了責(zé)任鏈模式,點(diǎn)擊事件最終會(huì)由ViewPostImeInputStage的onProcess來(lái)處理:

 @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }

根據(jù)不同的事件類(lèi)型會(huì)回調(diào)不同的方法,如果是觸摸事件會(huì)回調(diào)processPointerEvent:

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

            mAttachInfo.mUnbufferedDispatchRequested = false;
            final View eventTarget =
                    (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
                            mCapturingView : mView;
            mAttachInfo.mHandlingPointerEvent = true;
            boolean handled = eventTarget.dispatchPointerEvent(event);
            maybeUpdatePointerIcon(event);
            mAttachInfo.mHandlingPointerEvent = false;
            if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
                mUnbufferedInputDispatch = true;
                if (mConsumeBatchedInputScheduled) {
                    scheduleConsumeBatchedInputImmediately();
                }
            }
            return handled ? FINISH_HANDLED : FORWARD;
        }

7 從ViewRootImpl派發(fā)到View系統(tǒng)

方法里的mView就是DecorView,eventTarget.dispatchPointerEvent(event);也就是會(huì)回調(diào)DecorView的dispatchPointerEvent,DecorView沒(méi)有重寫(xiě)這個(gè)方法,最終會(huì)調(diào)用它的父類(lèi)View中的dispatchPointerEvent:

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

因?yàn)槭怯|摸事件所以會(huì)調(diào)用dispatchTouchEvent,而DecorView又重寫(xiě)了該方法:


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

這里的cb就是Activity,是在Activity的attach方法中傳到mWindow中去的。所以最終會(huì)回調(diào)的是Activity的dispatchTouchEvent方法。
(完)

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容