前面講到View與WindowManager與ViewRootImpl中只講到了ViewRootImpl是如何觸發View的繪製的,但ViewRootImpl的功能可不僅是繪製而已,本篇文章最主要介紹ViewRootImpl的事件分發功能。固然,對於事件分發,你們確定很熟悉,可是你們日常所看到的事件分發機制則是在Activity獲得觸摸事件後的,而後再傳遞給View處理,那麼究竟是誰傳遞給Activity觸摸事件的呢?app
前面只是介紹到了ViewRootImpl的繪製功能,固然,你在看ViewRootImpl源碼的時候會看到不少不少Event之類的,沒錯ViewRootImpl也是有事件分發的。要知道,當用戶點擊屏幕產生一個觸摸行爲,這個觸摸行爲則是經過底層硬件來傳遞捕獲,而後交給ViewRootImpl,接着將事件傳遞給DecorView,而DecorView再交給PhoneWindow,PhoneWindow再交給Activity,而後接下來就是咱們常見的View事件分發了。異步
**硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity **ide
咱們從ViewRootImpl開始看下具體的事件分發過程
首先會到ViewRootImpl的dispatchInputEventthis
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和InputEventReceiverspa
InputEvent:輸入事件的基類,它有兩個子類,分別是KeyEvent,MotionEvent對應鍵盤輸入事件和屏幕觸摸事件,這兩個也是咱們比較熟悉的了。線程
InputEventReceiver:爲應用程序提供了一個低級的機制來接收輸入事件。也就是用來接收輸入事件的,而後交給ViewRootImpl的dispatchInputEvent去分發。code
上面代碼能夠看到輸入事件接收器經過Handler切換到UI線程中。繼承
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線程後,調用到enqueueInputEventtoken
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); } }
Handler中
case MSG_PROCESS_INPUT_EVENTS: mProcessInputEventsScheduled = false; 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); } }
這裏InputStage則是一個實現處理輸入事件責任的階段,它是一個基類,也就是說InputStage提供一系列處理輸入事件的方法,也能夠轉發給其餘事件處理,而具體的處理則是看它的實現類。每種InputStage能夠處理必定的事件類型,好比AsyncInputStage、ViewPreImeInputStage、ViewPostImeInputStage等。當一個InputEvent到來時,ViewRootImpl會尋找合適它的InputStage來處理。
InputStage的處理狀況爲,會先調用deliver開始處理
InputState
最終的事件分發處理則是在apply方法裏的onProcess方法。
對於點擊事件來講,InputState的子類ViewPostImeInputStage能夠處理它,咱們看下ViewPostImeInputStage的onProcess
@Override protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); } else { // If delivering a new non-key event, make sure the window is // now allowed to start updating. handleDispatchWindowAnimationStopped(); 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); } } }
在ViewPostImeInputStage裏面有判斷是鍵盤事件仍是觸摸事件,這裏咱們只看觸摸事件,調用到了
processPointerEvent(q)方法
private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; mAttachInfo.mUnbufferedDispatchRequested = false; boolean handled = mView.dispatchPointerEvent(event); ... return handled ? FINISH_HANDLED : FORWARD; }
而這裏調用到了mView.dispatchPointerEvent,這裏的mView就是DecorView,細心的話可能就知道mView是在setView的時候賦值的
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ... } }
再看View的dispatchPointerEvent
public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } }
能夠看到,這裏會再判斷當前事件是不是觸摸事件,若是是則調用了dispatchTouchEvent進行分發,
而咱們的DecorView是繼承於View的,根據多態,咱們看下DecorView對dispatchTouchEvent的重寫
@Override public boolean dispatchTouchEvent(MotionEvent ev) { final Callback cb = getCallback(); return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }
能夠看到,這裏調用了callback.dispatchTouchEvent,Callback是Window裏面的一個接口
public interface Callback { ... public boolean dispatchKeyEvent(KeyEvent event); ... public boolean dispatchTouchEvent(MotionEvent event); }
既然是callback調用了,那麼究竟是誰實現了Callback接口去調用的?
正是Activity
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback { .. }
而在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) { ... mWindow = new PhoneWindow(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); }
PhoneWindow經過設置setCallback將Callback設置爲this也就是Activity。到這裏,點擊事件就傳到了Activity,以後的事件分發就是你們比較熟悉的了。
ViewRootImpl事件分發流程圖
ViewRootImpl事件分發
做者:Hohohong 連接:https://www.jianshu.com/p/9e6c54739217 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。