前面講到View與WindowManager與ViewRootImpl中只講到了ViewRootImpl是如何觸發(fā)View的繪制的操软,但ViewRootImpl的功能可不只是繪制而已,本篇文章最主要介紹ViewRootImpl的事件分發(fā)功能。當(dāng)然,對(duì)于事件分發(fā)劈榨,大家肯定很熟悉销睁,但是大家平常所看到的事件分發(fā)機(jī)制則是在Activity得到觸摸事件后的,然后再傳遞給View處理浩习,那么到底是誰傳遞給Activity觸摸事件的呢?
ViewRootImpl事件分發(fā)
前面只是介紹到了ViewRootImpl的繪制功能济丘,當(dāng)然谱秽,你在看ViewRootImpl源碼的時(shí)候會(huì)看到很多很多Event之類的,沒錯(cuò)ViewRootImpl也是有事件分發(fā)的摹迷。要知道疟赊,當(dāng)用戶點(diǎn)擊屏幕產(chǎn)生一個(gè)觸摸行為,這個(gè)觸摸行為則是通過底層硬件來傳遞捕獲峡碉,然后交給ViewRootImpl近哟,接著將事件傳遞給DecorView,而DecorView再交給PhoneWindow鲫寄,PhoneWindow再交給Activity吉执,然后接下來就是我們常見的View事件分發(fā)了。
**硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity **
我們從ViewRootImpl開始看下具體的事件分發(fā)過程
首先會(huì)到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);
}
這里有兩個(gè)參數(shù)地来,InputEvent和InputEventReceiver
InputEvent:輸入事件的基類戳玫,它有兩個(gè)子類,分別是KeyEvent,MotionEvent對(duì)應(yīng)鍵盤輸入事件和屏幕觸摸事件未斑,這兩個(gè)也是我們比較熟悉的了量九。
InputEventReceiver:為應(yīng)用程序提供了一個(gè)低級(jí)的機(jī)制來接收輸入事件。也就是用來接收輸入事件的颂碧,然后交給ViewRootImpl的dispatchInputEvent去分發(fā)荠列。
上面代碼可以看到輸入事件接收器通過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;
...
}
}
轉(zhuǎn)發(fā)到UI線程后载城,調(diào)用到enqueueInputEvent
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(event);
//將當(dāng)前輸入事件加入隊(duì)列中排列等候執(zhí)行
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
//輸入事件添加進(jìn)隊(duì)列后肌似,加入輸入事件的默認(rèn)尾部
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
//隊(duì)列計(jì)數(shù)
mPendingInputEventCount += 1;
...
//processImmediately則是判斷是同步還是異步,前面我們?cè)趆andler中調(diào)用的诉瓦,因?yàn)槭窃赨I線程川队,肯定是同步的力细,所以傳遞了參數(shù)是true,如果是異步固额,則調(diào)用到scheduleProcessInputEvents()
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
可以看到enqueueInputEvent將當(dāng)前的輸入事件加入隊(duì)列中眠蚂,QueuedInputEvent相當(dāng)于一個(gè)鏈表,
可以看到里面成員變量有next,用來鏈接下一個(gè)成員
private static final class QueuedInputEvent {
...
public QueuedInputEvent mNext;
public InputEvent mEvent;
public InputEventReceiver mReceiver;
...
}
而obtainQueuedInputEvent則是為當(dāng)前的輸入事件構(gòu)建一個(gè)鏈表結(jié)構(gòu)斗躏,然后鏈接到之前隊(duì)列的尾部
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則是判斷是同步還是異步逝慧,前面我們?cè)趆andler中調(diào)用的,因?yàn)槭窃赨I線程啄糙,肯定是同步的笛臣,所以傳遞了參數(shù)是true,如果是異步隧饼,則調(diào)用到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;
可以看到最后還是調(diào)用到了doProcessInputEvents沈堡。
void doProcessInputEvents() {
//循環(huán)取出隊(duì)列中的輸入事件
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
...
mPendingInputEventCount -= 1;
...
//分發(fā)處理
deliverInputEvent(q);
}
//處理完所有輸入事件,清楚標(biāo)志
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
可以看到該方法是用來循環(huán)獲取隊(duì)列中的輸入事件
接著進(jìn)行分發(fā)處理deliverInputEvent(q)
private void deliverInputEvent(QueuedInputEvent q) {
//校驗(yàn)輸入事件
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則是一個(gè)實(shí)現(xiàn)處理輸入事件責(zé)任的階段燕雁,它是一個(gè)基類诞丽,也就是說InputStage提供一系列處理輸入事件的方法,也可以轉(zhuǎn)發(fā)給其他事件處理拐格,而具體的處理則是看它的實(shí)現(xiàn)類僧免。每種InputStage可以處理一定的事件類型,比如AsyncInputStage禁荒、ViewPreImeInputStage猬膨、ViewPostImeInputStage等角撞。當(dāng)一個(gè)InputEvent到來時(shí)呛伴,ViewRootImpl會(huì)尋找合適它的InputStage來處理。
InputStage的處理情況為谒所,會(huì)先調(diào)用deliver開始處理
最終的事件分發(fā)處理則是在apply方法里的onProcess方法热康。
對(duì)于點(diǎn)擊事件來說,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里面有判斷是鍵盤事件還是觸摸事件姐军,這里我們只看觸摸事件,調(diào)用到了
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;
}
而這里調(diào)用到了mView.dispatchPointerEvent尖淘,這里的mView就是DecorView奕锌,細(xì)心的話可能就知道m(xù)View是在setView的時(shí)候賦值的
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);
}
}
可以看到,這里會(huì)再判斷當(dāng)前事件是否是觸摸事件村生,如果是則調(diào)用了dispatchTouchEvent進(jìn)行分發(fā)惊暴,
而我們的DecorView是繼承于View的,根據(jù)多態(tài)趁桃,我們看下DecorView對(duì)dispatchTouchEvent的重寫
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
可以看到辽话,這里調(diào)用了callback.dispatchTouchEvent肄鸽,Callback是Window里面的一個(gè)接口
public interface Callback {
...
public boolean dispatchKeyEvent(KeyEvent event);
...
public boolean dispatchTouchEvent(MotionEvent event);
}
既然是callback調(diào)用了,那么到底是誰實(shí)現(xiàn)了Callback接口去調(diào)用的油啤?
正是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通過設(shè)置setCallback將Callback設(shè)置為this也就是Activity典徘。到這里,點(diǎn)擊事件就傳到了Activity益咬,之后的事件分發(fā)就是大家比較熟悉的了逮诲。
小結(jié)
ViewRootImpl事件分發(fā)流程圖