我们都知道View事件的分发顺序是Activity—>Window—>View。

//Activitypublic boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            onUserInteraction();        }        //调用PhoneWindow.superDispatchTouchEvent()        if (getWindow().superDispatchTouchEvent(ev)) {            return true;        }        return onTouchEvent(ev);    }//PhoneWindow@Override    public boolean superDispatchTouchEvent(MotionEvent event) {    //mDecor即为DecorView        return mDecor.superDispatchTouchEvent(event);    }//DecorViewpublic boolean superDispatchTouchEvent(MotionEvent event) {//调用父类ViewGroup的dispatchTouchEvent进行事件分发        return super.dispatchTouchEvent(event);    }

由于DecorView extends FrameLayout,而DrameLayout extends ViewGroup,所以最终事件分发从ViewGroup.dispatchTouchEvent开始,如果对以上跳转不清楚可以参考Android窗口机制(一)——Window,PhoneWindow,DecorView理解。

但是Activity的事件来源在哪里呢?答案和ViewRootImpl有很大的关系。

首先,事件的根本来源是来自于Native层的嵌入式硬件,然后会经过InputEventReceiver接受事件交给ViewRootImpl,然后将事件传递给DecorView,最终DecorView再交给Activity,整个体系的事件分发顺序为:
Android事件分发机制——ViewRootImpl篇(前传)_第1张图片
我们从ViewRootImpl的dispatchInputEvent方法入手。

    public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {        SomeArgs args = SomeArgs.obtain();        args.arg1 = event;        args.arg2 = receiver;        Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args);        msg.setAsynchronous(true);        mHandler.sendMessage(msg);    }

这里有两个参数,InputEvent和InputEventReceiver:

  • InputEvent:输入事件的基类,它有两个子类,分别是KeyEvent和MotionEvent对应键盘输入事件和屏幕触摸事件;
  • InputEventReceiver:为应用程序提供了一个低级的机制来接收输入事件,也就是用来接收输入事件的,然后交给ViewRootImpl的dispatchInputEvent去分发。
final class ViewRootHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_DISPATCH_INPUT_EVENT: {                    SomeArgs args = (SomeArgs)msg.obj;                    InputEvent event = (InputEvent)args.arg1;                    InputEventReceiver receiver = (InputEventReceiver)args.arg2;                    enqueueInputEvent(event, receiver, 0, true);                    args.recycle();                } break;            }        }

转发到UI线程后,调用到enqueueInputEvent:

void enqueueInputEvent(InputEvent event,            InputEventReceiver receiver, int flags, boolean processImmediately) {        adjustInputEventForCompatibility(event);        //将当前输入事件加入队列中排列等候执行        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);        //输入事件添加进队列后,加入输入事件的默认尾部        QueuedInputEvent last = mPendingInputEventTail;        if (last == null) {            mPendingInputEventHead = q;            mPendingInputEventTail = q;        } else {            last.mNext = q;            mPendingInputEventTail = q;        }        //队列计数        mPendingInputEventCount += 1;        ...        //processImmediately则是判断是同步还是异步,前面我们在handler中调用的,因为是在UI线程,肯定是同步的,所以传递了参数是true,如果是异步,则调用到scheduleProcessInputEvents()        if (processImmediately) {            doProcessInputEvents();        } else {            scheduleProcessInputEvents();        }    }

可以看到enqueueInputEvent将当前的输入事件加入队列中,QueuedInputEvent相当于一个链表,可以看到里面成员变量有next,用来链接下一个成员。

private static final class QueuedInputEvent {        public QueuedInputEvent mNext;        public InputEvent mEvent;        public InputEventReceiver mReceiver;}

而obtainQueuedInputEvent则是为当前的输入事件构建一个链表结构,然后链接到之前队列的尾部。

private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,            InputEventReceiver receiver, int flags) {        QueuedInputEvent q = mQueuedInputEventPool;        if (q != null) {            mQueuedInputEventPoolSize -= 1;            mQueuedInputEventPool = q.mNext;            q.mNext = null;        } else {            q = new QueuedInputEvent();        }        q.mEvent = event;        q.mReceiver = receiver;        q.mFlags = flags;        return q;    }

紧接着我们继续分析:

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

processImmediately则是判断是同步还是异步,前面我们在handler中调用的,因为是在UI线程,肯定是同步的,所以传递了参数是true,如果是异步,则调用到scheduleProcessInputEvents()。

private void scheduleProcessInputEvents() {        if (!mProcessInputEventsScheduled) {            mProcessInputEventsScheduled = true;            Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);            msg.setAsynchronous(true);            mHandler.sendMessage(msg);        }    }case MSG_PROCESS_INPUT_EVENTS:mProcessInputEventsScheduled = false;//最终还是调用doProcessInputEvents()处理doProcessInputEvents();break;

最终还是调用doProcessInputEvents()处理:

void doProcessInputEvents() {        //循环取出队列中的输入事件        while (mPendingInputEventHead != null) {            QueuedInputEvent q = mPendingInputEventHead;            mPendingInputEventHead = q.mNext;            ...            mPendingInputEventCount -= 1;            ...            //分发处理            deliverInputEvent(q);        }        //处理完所有输入事件,清楚标志        if (mProcessInputEventsScheduled) {            mProcessInputEventsScheduled = false;            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);        }    }

可以看到该方法是用来循环获取队列中的输入事件,接着进行分发处理deliverInputEvent(q)。

private void deliverInputEvent(QueuedInputEvent q) {        //校验输入事件        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);        }    }

在ViewRootImpl中,有一系列类似于InputStage的子类,它是一个抽象类,其deliver方法会处理一个输入事件,处理完成之后会调用finishInputEvent方法。

它有很多子类,对应具体的InputStage,每种InputStage可以处理一定的事件类型,比如AsyncInputStage、SyntheticInputStage、NativePostImeInputStage、ViewPreImeInputStage、ViewPostImeInputStage等。

其子类实现了InputStage的一些抽象方法,比如onProcess、onDeliverToNext、processKeyEvent、processPointerEvent、processTrackballEvent、processGenericMotionEvent,在不同的情况下onProcess、onDeliverToNext等方法就会被回调。

当一个InputEvent到来时,ViewRootImpl会寻找合适它的InputStage来处理,InputStage会先调用deliver()开始处理。
Android事件分发机制——ViewRootImpl篇(前传)_第2张图片
最终的事件分发处理则是在apply方法里的onProcess方法中。

对于点击事件来说,InputState的子类ViewPostImeInputStage可以处理它,我们看下ViewPostImeInputStage的onProcess。

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);}}}private int processPointerEvent(QueuedInputEvent q) {final MotionEvent event = (MotionEvent)q.mEvent;mAttachInfo.mUnbufferedDispatchRequested = false;//调用mView.dispatchPointerEvent()boolean handled = mView.dispatchPointerEvent(event);//...return handled ? FINISH_HANDLED : FORWARD;}

其实在processKeyEvent、processPointerEvent、processTrackballEvent、processGenericMotionEvent方法中都有一句很关键的一句代码。

View mView;mView.dispatchKeyEvent(event)//按键事件mView.dispatchPointerEvent(event)mView.dispatchTrackballEvent(event)mView.dispatchGenericMotionEvent(event)

那这里的mView到底是什么呢?如果大家不是很清楚,可以看一下我之前的一篇博文
Window窗口机制(三)——WindowManager,ViewRootImpl,View理解就了解了,其实这里的mView就是我们的DecorView,因为ViewRootImpl通过setView方法将DecorView添加到PhoneWindow中。

由于DecorView没有上述的mView.的几个方法,所以向其父类寻找,最终在View中可以看到其身影。

//Viewpublic final boolean dispatchPointerEvent(MotionEvent event) {        if (event.isTouchEvent()) {        //判断是触摸方法,调用dispatchTouchEvent()            return dispatchTouchEvent(event);        } else {            return dispatchGenericMotionEvent(event);        }    }

由于这里多态的存在,会调用到View的子类DecorView.dispatchTouchEvent。

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

可以看出DecorView最终会调用cb.dispatchTouchEvent方法,那么这个Callback是什么?其实这个Callback就是当前的Activity。

//Activitypublic class Activity extends ContextThemeWrapper        implements LayoutInflater.Factory2,        Window.Callback, KeyEvent.Callback,        OnCreateContextMenuListener, ComponentCallbacks2,        Window.OnWindowDismissedCallback {public interface Callback {        public boolean dispatchKeyEvent(KeyEvent event);        ...        public boolean dispatchTouchEvent(MotionEvent event);   }//Activity.attachfinal 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) {        attachBaseContext(context);         mFragments.attachHost(null /*parent*/);         mWindow = new PhoneWindow(this);        //setCallback        mWindow.setCallback(this);        mWindow.setOnWindowDismissedCallback(this);

到这里,点击事件就传入了Activity,之后的流程也就是文章一开始分析的一样啦。

总结

ViewRootImpl事件分发流程图
Android事件分发机制——ViewRootImpl篇(前传)_第3张图片

接下来事件在View和ViewGroup中是如何传递的,可以参考以下博文Android事件分发机制——View(一)。

更多相关文章

  1. Android Studio集成Library时报错,终极解决方法
  2. Android从服务器获取图片的实例方法
  3. android屏幕休眠和唤醒两种方法(newWakeLock)
  4. Android onTouchEvent事件
  5. android添加图片到本地,无法即使刷新的解决方法
  6. Android每天定时任务启动方法
  7. android Timer使用方法
  8. Android 使用git下载源码报错解决方法
  9. Android中的onCreateOptionsMenu()方法和onOptionsItemSelected()方

随机推荐

  1. android Bitmap如何保存成为一个bmp文件
  2. 使用mysql数据库,遇到重复数据怎么处理?
  3. 设计模式之状态模式
  4. 客户端请求服务器时的状态码讲解
  5. 深入分析java中的System类
  6. 设计模式之责任链模式
  7. java多线程(1)入门知识和基础案例
  8. 设计模式之备忘录模式
  9. 面试官:手写一个选择排序并对其改进
  10. java网络编程(3)UDP协议编程(单播多播广播)