事件分发源码分析(一)

     网上有很多关于事件分发的讲解,我也看过很多,但是看完后总是会只记得大概,于是就试试自己去看看源码。

    如有错误,请及时提出

    首先我们来分析下触摸事件传递流程:

     我们手点屏幕---->屏幕硬件通过某种方式接受到相应信息---->将相应信息传递到活动---->传到decorview ---->向下分发

     前两种属于硬件直接忽略,那事件是怎么传到活动的呢?不清楚,但是我们知道活动是通过ViewRootImpl关联decorview

所以如果要传到视图应该会通过viewRootImpl传递让我们先看看活动源码:

public boolean dispatchTouchEvent(MotionEvent ev){
        if(ev.getAction()== MotionEvent.ACTION_DOWN){
            onUserInteraction();
        }
        if(getWindow()。superDispatchTouchEvent(ev)){
            返回true;
        }
        返回onTouchEvent(ev);
    }
这段代码说明事件的分发和窗口有关,我们再看看getWindow:
public Window getWindow(){
        返回mWindow;
    }
查看mWindow初始化:

最终的无效附加(Context context,ActivityThread aThread,
            Instrumentation instr,IBinder token,int ident,
            应用程序应用程序,意图意图,ActivityInfo信息,
            CharSequence标题,活动父项,字符串标识,
            NonConfigurationInstances lastNonConfigurationInstances,
            配置配置,字符串引用者,IVoiceInteractor voiceInteractor,
            窗口窗口,ActivityConfigCallback activityConfigCallback){
        attachBaseContext(上下文);

        mFragments.attachHost(null / * parent * /);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        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());
            }
        }

        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;

        mWindow.setColorMode(info.colorMode);
    }
上面代码可以发现是PhoneWindow:

//这是包含窗口装饰的窗口的顶层视图。
    私人DecorView mDecor;
在这个类里看到这个属性,根据他的注释可以得知这是顶层视图,到此已经到了视图,而我们并没有找到怎么传递的。由于先创建窗口后有图,我们去看看WMS( WindowManagerService):

 // ------------------------------------------------ -------------
    //输入事件和焦点管理
    // ------------------------------------------------ -------------

    final InputMonitor mInputMonitor = new InputMonitor(this);
进入InputMonitor类发现多处输入事件与服务里的输入管理有关联:

private void updateInputDispatchModeLw(){
        mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled,mInputDispatchFrozen);
    }
回头看看这个mIputManager是InputManagerService类:

    最终的InputManagerService mInputManager;
再到InputManagerService类:

public void start(){
    Slog.i(TAG,“启动输入管理器”);
    nativeStart(mPtr);
    ........
}
可以看到这里调用了底层代码,于是我在网上找了下:

static void nativeStart(JNIEnv * env,jclass / * clazz * /,jlong​​ ptr){
    NativeInputManager * im = reinterpret_cast <NativeInputManager *>(ptr);

    status_t result = im-> getInputManager() - > start();
    if(result){
        jniThrowRuntimeException(env,“输入管理器无法启动。”);
    }
}

可以看到这里调用了getInputManager的启动方法:

status_t InputManager :: start(){
    status_t result = mDispatcherThread-> run(“InputDispatcher”,PRIORITY_URGENT_DISPLAY);
    if(result){
        ALOGE(“由于错误%d,无法启动InputDispatcher线程”,result);
        返回结果;
    }

    result = mReaderThread-> run(“InputReader”,PRIORITY_URGENT_DISPLAY);
    if(result){
        ALOGE(“由于错误%d,无法启动InputReader线程”,result);

        mDispatcherThread-> requestExit();
        返回结果;
    }

    返回OK;
}
可以看到这里开启了两个线程:mDispatcherThread和mReaderThread,可以根据字面意思看出前者用来分发,后者用来接受(查找了一番也是如此)

//原生回调。
    private void notifyConfigurationChanged(long当Nanos){
        mWindowManagerCallbacks.notifyConfigurationChanged();
    }
由此可知,这里是通过监听然后回调,至于怎么监听的有兴趣的同学可以自行研究下,接着往下看:

/ *通知输入设备配置已更改。* /
    @覆盖
    public void notifyConfigurationChanged(){
        // TODO(多显示器):通知与此输入设备关联的正确显示。
        mService.sendNewConfiguration(DEFAULT_DISPLAY);

        同步(mInputDevicesReadyMonitor){
            if(!mInputDevicesReady){
                mInputDevicesReady = true;
                mInputDevicesReadyMonitor.notifyAll();
            }
        }
    }
发现InputMonitor这个类里面实现此方法,根据注释可知输入设备配置发生改变时调用次方法,可以看到这里事件接受器刷新了。

到此我们知道了事件的接受与分发,但他到底怎么传递到视图的呢这个时候我们回头看下ViewRootImpl类?

 / **
     *我们有一个孩子
     * /
    public void setView(View view,WindowManager.LayoutParams attrs,View panelParentView){
        同步(this){
               if(mInputChannel!= null){
                    if(mInputQueueCallback!= null){
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
  }
}
可以看到这里WindowinputEventReceiver就是事件接收器,进去看看:

final类WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel,Looper looper){
            超级(inputChannel,looper);
        }

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

        @覆盖
        public void onBatchedInputEventPending(){
            if(mUnbufferedInputDispatch){
                super.onBatchedInputEventPending();
            } else {
                scheduleConsumeBatchedInput();
            }
        }

        @覆盖
        public void dispose(){
            unscheduleConsumeBatchedInput();
            super.dispose();
        }
    }
这里看下onInputEvent里面的方法:

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();
        }
    }
可以看到这里接受了事件,如果立即处理调用doProcessInputEvents,否则调用scheduleProcessInputEvents
看下doProcessInputEvents:
void doProcessInputEvents(){
        //传递队列中所有待处理的输入事件。
        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);
        }

        // We are done processing all input events that we can process right now
        // so we can clear the pending flag immediately.
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }
这里看下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阶段;
        if(q.shouldSendToSynthesizer()){
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme()?mFirstPostImeInputStage:mFirstInputStage;
        }

        if(stage!= null){
            stage.deliver(Q);
        } else {
            finishInputEvent(Q);
        }
    }
可以看到这里通过InputStage来判断到底执行那个方法,而阶段所要赋的值在的setView方法中可以找到:

//设置输入管道。
                CharSequence counterSuffix = attrs.getTitle();
                mSyntheticInputStage = new SyntheticInputStage();
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        “aq:native-pre-ime:”+ counterSuffix);

                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;
所以回调用提供方法:

/ **
         *提供要处理的事件。
         * /
        public final void deliver(QueuedInputEvent q){
            ((q.mFlags&QueuedInputEvent.FLAG_FINISHED)!= 0){
                向前(Q);
            } else if(shouldDropInputEvent(q)){
                完成(q,错误);
            } else {
                适用(q,onProcess(q));
            }
        }
所以可以看到最终调用InputStage类中申请方法,在ViewRootImpl类中有很多实现了InputStage内部类,我们找到了ViewPostImeInputStage这个类:

/ **
     *将后期输入事件传递给视图层次结构。
     * /
    最终课程ViewPostImeInputStage扩展InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }

        @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(处理指针事件(翻译)),processTrackballEvent(处理轨迹球事件(翻译))和processGenericMotionEvent这三个方法,根据方法名可知processGenericMotionEvent这个方法是处理一般手势:

private int processGenericMotionEvent(QueuedInputEvent q){
            最终的MotionEvent事件=(MotionEvent)q.mEvent;

            //将事件传递给视图。
            if(mView.dispatchGenericMotionEvent(event)){
                返回FINISH_HANDLED;
            }
            回到FORWARD;
        }
这里可以看出MView的调用了dispatchGenericMotionEvent,继续往下看:

/ **
     *派遣一般运动事件。
     * <p>
     *带源类的通用运动事件{@Link InputDevice#SOURCE_CLASS_POINTER}
     *被传递到指针下的视图。所有其他通用运动事件都是
     * delivered to the focused view.  Hover events are handled specially and are delivered
     * to {@link #onHoverEvent(MotionEvent)}.
     * </p>
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    public boolean dispatchGenericMotionEvent(MotionEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
        }

        final int source = event.getSource();
        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            final int action = event.getAction();
            if (action == MotionEvent.ACTION_HOVER_ENTER
                    || action == MotionEvent.ACTION_HOVER_MOVE
                    || action == MotionEvent.ACTION_HOVER_EXIT) {
                if(dispatchHoverEvent(event)){
                    返回true;
                }
            } else if(dispatchGenericPointerEvent(event)){
                返回true;
            }
        } else if(dispatchGenericFocusedEvent(event)){
            返回true;
        }

        if(dispatchGenericMotionEventInternal(event)){
            返回true;
        }

        if(mInputEventConsistencyVerifier!= null){
            mInputEventConsistencyVerifier.onUnhandledEvent(event,0);
        }
        返回false;
    }
并没有发现我们所熟知的dispatchTouch方法,我们再去processPointerEvent方法里看看:

private int processPointerEvent(QueuedInputEvent q){
            最终的MotionEvent事件=(MotionEvent)q.mEvent;

            mAttachInfo.mUnbufferedDispatchRequested = false;
            mAttachInfo.mHandlingPointerEvent = true;
            boolean handled = mView.dispatchPointerEvent(event);
            maybeUpdatePointerIcon(事件);
            maybeUpdateTooltip(事件);
            mAttachInfo.mHandlingPointerEvent = false;
            if(mAttachInfo.mUnbufferedDispatchRequested &&!!mUnbufferedInputDispatch){
                mUnbufferedInputDispatch = true;
                if(mConsumeBatchedInputScheduled){
                    scheduleConsumeBatchedInputImmediately();
                }
            }
            退货处理?FINISH_HANDLED:FORWARD;
        }
可以看到也是MView的调用了:

/ **
     *分派指针事件。
     * <p>
     *将触摸相关的指针事件分派给{@link #onTouchEvent(MotionEvent)}和全部
     *其他事件{@link #onGenericMotionEvent(MotionEvent)}。这种关注的分离
     *强化了{@link #onTouchEvent(MotionEvent)}真正关于触摸的不变性
     *并且不应该期望处理其他指向设备功能。
     * </ p>
     *
     * @param event要发送的运动事件。
     * @return如果事件由视图处理,则返回true,否则返回false。
     * @隐藏
     * /
    public final boolean dispatchPointerEvent(MotionEvent event){
        if(event.isTouchEvent()){
            返回dispatchTouchEvent(event);
        } else {
            返回dispatchGenericMotionEvent(event);
        }
    }
可以看到这里调用了dispatchTouchEvent,根据注释可以确定就是这里传递到视图的。(吐槽下翻译)
其实其他事件也是通过类似的方法传递的,比如软键盘事件,有兴趣的可以在ViewRootImpl里寻找相应的InputStage

好了到此我们知道了事件是如何传递到view的了,下一篇继续

如有问题,请一定要告知















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值