美文网首页
Android点击事件分发机制源码分析1——Activity

Android点击事件分发机制源码分析1——Activity

作者: hello_小丁同学 | 来源:发表于2017-11-26 18:17 被阅读57次

    Android点击事件分发机制源码分析1——Activity
    Android点击事件分发机制源码分析2——ViewGroup
    Android点击事件分发机制源码分析3——View

    一 产生点击事件

    当用户用点击显示屏产生一个点击事件,本章讨论点击事件是如何传到Activity上的,Android 源码为sdk25。

    二 系统如何将点击事件派发给Activity

    1. handleLaunchActivity

    启动一个新的Activity的时候,会调用ActivityThread的handleLaunchActivity方法,我们从这里开始看:

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;
    
        //最终回调目标Activity的onConfigurationChanged()
        handleConfigurationChanged(null, null);
        //初始化wms
        WindowManagerGlobal.initialize();
        //最终回调目标Activity的onCreate
        Activity a = performLaunchActivity(r, customIntent);
        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            //最终回调目标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. 首先调用performLaunchActivity,该在函数内部通过Java反射机制创建目标Activity,然后调用它的onCreate及onStart函数。
    2. 调用handleResumeActivity,会在其内部调用目标Activity的onResume函数。

    1.1 进入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);
        }
    
       // 通过反射创建activity实例
        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 {
            //创建Application对象
            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);
                //调用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;
                //最终会调用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. 创建一个activity实例
    2. 调用activity的attach方法
    3. 通过mInstrumentation来回调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*/);
    
           // 创建window,可以看出来实际上是创建一个PhoneWindow 
            mWindow = new PhoneWindow(this, window);
            mWindow.setWindowControllerCallback(this);
           //设置mwindow回调,会让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());
                }
            }
           
           // 设置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. 创建创建出window 它的实现是PhoneWindow
    2. 给window设置回调接口,这个接口的实现是activity自己
    3. 给window设置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实现了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里面有一个dispatchTouchEvent方法,actiivty里的dispatchTouchEvent就是对这个方法的具体实现。那么这个window.callback里的点击事件又是从哪里传过来的,dispatchTouchEvent又是什么时候被调用的呢?继续看

    1.1.2 调用activity的onCreate方法

    看下mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);方法的内部实现:

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

    activity.performCreate的内部:

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

    2. handleResumeActivity

     final void handleResumeActivity(IBinder token,
                boolean clearHide, boolean isForward, boolean reallyResume) {
                ....
               // 最终会调用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();
                    //获得一个View对象
                    View decor = r.window.getDecorView();
                    decor.setVisibility(View.INVISIBLE);
                    // 获得ViewManager对象
                    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对象加入到ViewManager中
                        wm.addView(decor, l);
                    }
                ...
    }
    
    
    

    主要做了两件事:

    1. 调用activity的onResume方法
    2. 将view和window关联在一块

    2.1 调用activity的onResume方法

    在handleResumeActivity里面调用了performResumeActivity:

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

    它会调用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. 调用 performRestart();最终会调用activity的onStart方法
    2. 使用mInstrumentation 来控制activity的 onResume执行

    3 将xml文件添加到decorView

    通过上面的分析可以知道,在activity的onResume方法执行之后,会将decorView添加到窗口,那么我们自己写的布局是什么时候添加到窗口上面取得呢?我们一般都是在onCreate方法中调用setContentView方法来设置自己写的布局的。那么就先分析一下setContentView

    3.1 setContentView

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

    getWindow()返回的就是在attach方法里面创建的PhoneWindow。
    看一下官方是怎么解释Window这个类的:

    · 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是一个被加到window manager的顶层view。
    再看一下View这个类官方的解释:

    · 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是一个基本的用于用户交互的不见,它占据了屏幕上的一块区域,用于绘制和事件处理

    我们进入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类型,它的初值为null
                mContentParent.addView(view, params);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
            mContentParentExplicitlySet = true;
        }
    

    主要做了以下事情:

    1. 创建一个DecorView,并初始化这个decorView
    2. 将我们自己的布局添加到mContentParent

    3.1.1 创建并初始化DecorView

    看一下installDecor的内部实现:

        private void installDecor() {
            mForceDecorInstall = false;
            if (mDecor == null) {
                mDecor = generateDecor(-1);
    
            } else {
                mDecor.setWindow(this);
            }
            if (mContentParent == null) {
                mContentParent = generateLayout(mDecor);
    
    
            }
        }
    

    它做了下面的事情:

    1. 调用generateDecor创建一个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());
        }
    

    创建一个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) {
    
          //根据情况取得对应标题栏的资源id
    
         layoutResource =  com.android.internal.R.layout.dialog_title_icons;
    
         }
    
           ......
    
    }
    
      mDecor.startChanging();
     View in =mLayoutInflater.inflate(layoutResource, null);
     //加入标题栏
     decor.addView(in,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    
       /*
    
    ID_ANDROID_CONTENT的值为”com.android.internal.R.id.content”
    
         这个contentParent由findViewById返回,实际上就是mDecorView的一部分。
    
       */
    
       ViewGroupcontentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    
       ......
    
       mDecor.finishChanging();
    
       return contentParent;
    
    }
    

    主要做了两件事:

    1. decorView设置标题栏的信息
    2. 初始化contentParent,contentParent是decorView的一部分,其实decorview内部有两个子元素:titlebar和contentParent

    4 DecorView和Window

    再回到AcrivityThread的handleResumeActivity方法,会调用

    wm.addView(decor, l);
    

    decor就是decorview,他是自定义布局的顶层布局,我们在setContentView的时候已经把自己写的布局添加到decorView里了,所以它现在代表的就是我们的自定义布局,现在调用addView方法的目的就是把我们自己的布局添加到窗口上。
    wm是WindowManager实例,它的具体实现类是WindowManagerImpl,WindowManagerImpl将实际的操作委托给一个名为mGlobal的成员来完成,mGlobal的类型是WindowManagerGlobal。现在看下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;
            }
        }
    

    上面代码先创建一个ViewRootImpl实例,然后将DecorView、viewRootImpl、wparams方别放入mViews、mRoots和mParams中,完成窗口信息的添加工作,然后通过ViewRoot和WMS通信,完成新窗口的添加工作。

    5 ViewRootImpl

    下面看一下root.setView(view, wparams, panelParentView)方法的内部实现,因为本文只关注点击事件分发机制,所以只保留setView方法中和点击事件相关的代码:

    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);
                }
            }
        }
    
    

    和点击事件相关的流程如下:

    1. 当窗口不包含INPUT_FEATURE_NO_INPUT_CHANNEL属性的时候,创建并初始化InputChannel,InputChannel是窗口接收来自InputDispatcher输入的管道。
    2. 会调用 mWindowSession.addToDisplay方法将窗口添加到WMS中,之后InputChannel就可以准备接收各种事件了。
    3. 最后会创建一个WindowInputEventReceiver用于接收并处理输入事件。

    当一个输入时间被派发给ViewRootImpl所在的窗口时,Looper就会唤醒并触发InputEventReceiver的onInputEvent()回调(引用自《深入理解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);
        }
    

    不过finishInputEvent方法并不会被实现,因为被调用的对象实际上是WindowInputEventReceiver,而WindowInputEventReceiver内部会重写onInputEvent方法:

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

    它会调用:

    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,所以会执行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);
            }
    

    在这个方法中会处理所有的输入事件,它会遍历调用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);这里用到了责任链模式,点击事件最终会由ViewPostImeInputStage的onProcess来处理:

     @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);
                    }
                }
            }
    

    根据不同的事件类型会回调不同的方法,如果是触摸事件会回调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派发到View系统

    方法里的mView就是DecorView,eventTarget.dispatchPointerEvent(event);也就是会回调DecorView的dispatchPointerEvent,DecorView没有重写这个方法,最终会调用它的父类View中的dispatchPointerEvent:

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

    因为是触摸事件所以会调用dispatchTouchEvent,而DecorView又重写了该方法:

    
        @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中去的。所以最终会回调的是Activity的dispatchTouchEvent方法。
    (完)

    参考:

    相关文章

      网友评论

          本文标题:Android点击事件分发机制源码分析1——Activity

          本文链接:https://www.haomeiwen.com/subject/madwmxtx.html