點(diǎn)擊一下告訴你什么是事件分發(fā)包蓝,知其然知其所以然彪置,所以這邊我篇我們就對源碼展開分析县耽!
我們知道事件都是由Activity往下面分發(fā)的!但是Activity.dispatchTouchEvent()又是從被哪里調(diào)用的呢匈勋?好奇不好奇监憎?
1.首先從手指觸摸到屏幕開始
我們知道Android是基于Linux系統(tǒng)的绞呈。當(dāng)輸入設(shè)備可用時(shí)(這里的輸入設(shè)備包括很多設(shè)備贸人,比如觸摸屏和鍵盤是Android最普遍也是最標(biāo)準(zhǔn)的輸入設(shè)備,另外它還包括外接的游戲手柄佃声、鼠標(biāo)等)艺智,Linux內(nèi)核會為輸入設(shè)置創(chuàng)建對應(yīng)的設(shè)備節(jié)點(diǎn)。當(dāng)輸入設(shè)備不可用時(shí)圾亏,就把對應(yīng)的設(shè)備節(jié)點(diǎn)刪除十拣,這也是如果我們的屏幕意外摔碎了或者其他原因?qū)е掠|摸屏幕不可用時(shí)觸摸沒有反應(yīng)的根本原因封拧。當(dāng)我們的輸入設(shè)備可用時(shí)(我們這里只來講解觸摸屏),我們對觸摸屏進(jìn)行操作時(shí)夭问,Linux就會收到相應(yīng)的硬件中斷泽西,然后將中斷加工成原始的輸入事件并寫入相應(yīng)的設(shè)備節(jié)點(diǎn)中。而我們的Android 輸入系統(tǒng)所做的事情概括起來說就是**監(jiān)控這些設(shè)備節(jié)點(diǎn)缰趋,當(dāng)某個(gè)設(shè)備節(jié)點(diǎn)有數(shù)據(jù)可讀時(shí)捧杉,將數(shù)據(jù)讀出并進(jìn)行一系列的翻譯加工,然后在所有的窗口中找到合適的事件接收者秘血,并派發(fā)給它味抖。
手指進(jìn)行一系列操作(這里指的是手指的移動,這一步可能沒有)
手指抬起或者因其他其他原因(突然間來了個(gè)電話之類的)導(dǎo)致事件結(jié)束
上面我們說到了Android 輸入系統(tǒng)所做的事情概括起來說就是監(jiān)控設(shè)備節(jié)點(diǎn)灰粮,當(dāng)某個(gè)設(shè)備節(jié)點(diǎn)有數(shù)據(jù)可讀時(shí)仔涩,將數(shù)據(jù)讀出并進(jìn)行一系列的翻譯加工,然后在所有的窗口中找到合適的事件接收者谋竖,并派發(fā)給它红柱。那么它是如何做的呢,蓖乘,我們來具體分析一下锤悄。Android 的輸入系統(tǒng)InputManagerService(以下簡稱為IMS)作為系統(tǒng)服務(wù),它像其他系統(tǒng)服務(wù)一樣在SystemServer進(jìn)程中創(chuàng)建嘉抒。
Linux會為所有可用的輸入設(shè)備在/dev/input目錄在建立event0~n或者其他名稱的設(shè)備節(jié)點(diǎn)零聚,Android輸入系統(tǒng)會監(jiān)控這些設(shè)備節(jié)點(diǎn),具體是通過INotify和Epoll機(jī)制來進(jìn)行監(jiān)控些侍。而不是通過一個(gè)線程進(jìn)行輪詢查詢隶症。
INotify機(jī)制
INotify是Linux內(nèi)核提供的一種文件系統(tǒng)變化通知機(jī)制。它可以為應(yīng)用程序監(jiān)控文件系統(tǒng)的變化岗宣,如文件的新建蚂会,刪除等。
//創(chuàng)建INotify對象耗式,并用描述符inotifyFd 描述它
int inotifyFd = inotify_init();
/*
添加監(jiān)聽
inotify_add_watch函數(shù)參數(shù)說明
inotifyFd:上面建立的INotify對象的描述符胁住,當(dāng)監(jiān)聽的目錄或文件發(fā)生變化時(shí)記錄在INotify對象
“/dev/input”:被監(jiān)聽的文件或者目錄
IN_CREATE | IN_DELETE:事件類型
綜合起來下面的代碼表示的意思就是當(dāng)“/dev/input”下發(fā)生IN_CREATE | IN_DELETE(創(chuàng)建或者刪除)時(shí)即把這個(gè)事件寫入到INotify對象中
*/
int wd = inotify_add_watch(inotifyFd, "/dev/input", IN_CREATE|IN_DELETE )
Epoll機(jī)制
在上述INotify機(jī)制中我們知道了我們只需關(guān)心inotifyFd這個(gè)描述符就行了,可是事件是隨機(jī)發(fā)生的刊咳,我們也不會本末倒置的采用輪詢的方式輪詢這個(gè)描述符彪见,因?yàn)槿绻@樣做的話會浪費(fèi)大量系統(tǒng)資源。這時(shí)候我們Linux的另一個(gè)機(jī)制就派上用場了娱挨,即Epoll機(jī)制余指。Epoll機(jī)制簡單的說就是使用一次等待來獲取多個(gè)描述的可讀或者可寫狀態(tài)。這樣我們不必對每一個(gè)描述符創(chuàng)建獨(dú)立的線程進(jìn)行阻塞讀取跷坝,在避免了資源浪費(fèi)的同時(shí)獲得較快的相應(yīng)速度酵镜。
至此原始輸入事件已經(jīng)讀取完畢碉碉,Android輸入系統(tǒng)對原始輸入事件進(jìn)行翻譯加工以及派發(fā)的詳細(xì)過程很復(fù)雜。我們這里只分析其中一部分——IMS與窗口笋婿。
Avtivity,Window,PhoneWindow,以及ViewRootImpl之間的聯(lián)系
上文中我們也說到了IMS會在所有的窗口中找到合適的事件接收者誉裆。IMS是運(yùn)行在SystemServer進(jìn)程中,而我們的窗口呢缸濒,是在我們的應(yīng)用進(jìn)程中。這就引出了我們在上文中留下的懸念
// ② 初始化mInputChanel粱腻。InputChannel是窗口接收來自InputDispatcher的輸入事件的管道庇配。這部分內(nèi)容我們將在下一篇介紹。
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
...
...
// ③ 如果mInputChannel不為空绍些,則創(chuàng)建mInputEventReceiver用于接收輸入事件捞慌。
if (mInputChannel != null) {
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
InputChannel
InputChannel的本質(zhì)是一對SocketPair(非網(wǎng)絡(luò)套接字)。套接字可以用于網(wǎng)絡(luò)通信柬批,也可以用于本機(jī)內(nèi)的進(jìn)程通信啸澡。進(jìn)程間通信的一種方式
public final class InputChannel implements Parcelable {
private static final String TAG = "InputChannel";
private static final boolean DEBUG = false;
public static final Parcelable.Creator<InputChannel> CREATOR
= new Parcelable.Creator<InputChannel>() {
public InputChannel createFromParcel(Parcel source) {
InputChannel result = new InputChannel();
result.readFromParcel(source);
return result;
}
public InputChannel[] newArray(int size) {
return new InputChannel[size];
}
};
@SuppressWarnings("unused")
private long mPtr; // used by native code
private static native InputChannel[] nativeOpenInputChannelPair(String name);
private native void nativeDispose(boolean finalized);
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
private native void nativeDup(InputChannel target);
private native String nativeGetName();
}
WindowInputEventReceiver
得到InputChannel后,便用它創(chuàng)建WindowInputEventReceiver氮帐,WindowInputEventReceiver繼承于InputEventReceiver嗅虏,InputEventReceiver對象可以接收來自InputChannel的輸入事件,并觸發(fā)其onInputEvent方法的回調(diào)上沐。我們這里的是WindowInputEventReceiver皮服,所以我們來看一下這個(gè)類
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event, int displayId) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
enqueueInputEvent
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
...
//① 將InputEvent對應(yīng)的InputEventReceiver封裝為一個(gè)QueuedInputEvent
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
//② 將新建的QueuedInputEvent 追加到mPendingInputEventTail所表示的一個(gè)單向鏈表中
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
if (processImmediately) {
//③ 如果第三個(gè)參數(shù)為true,則直接在當(dāng)前線程中開始對輸入事件的處理工作
doProcessInputEvents();
} else {
//④ 否則將處理事件的請求發(fā)送給主線程的Handler参咙,隨后進(jìn)行處理
scheduleProcessInputEvents();
}
doProcessInputEvents
void doProcessInputEvents() {
//遍歷整個(gè)輸入事件隊(duì)列龄广,并逐一處理
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()//方法會將完成單個(gè)事件的整個(gè)處理流程
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 (q.mEvent instanceof KeyEvent) {
mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
}
if (stage != null) {
handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
new ViewPostImeInputStage(mSyntheticInputStage);
從這個(gè)方法processPointerEvent將事件傳遞給了View樹的根節(jié)點(diǎn),調(diào)用了mView.dispatchPointerEvent(event);
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
mAttachInfo.mHandlingPointerEvent = true;
// 此時(shí)ViewRootImpl會將事件的處理權(quán)移交給View樹的根節(jié)點(diǎn)蕴侧,調(diào)用dispatchPointerEvent函數(shù)
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
mAttachInfo.mHandlingPointerEvent = false;
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
這里肯定有疑問了择同,為什么會傳到mView.dispatchPointerEvent(event),不應(yīng)該先到Activity.dispatchPointerEvent(event)?
DecorView.dispatchTouchEvent(MotionEvent ev)
@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);
}
Window.Callback cb = mWindow.getCallback();這個(gè)是Activity里面實(shí)現(xiàn)的
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, ActivityConfigCallback activityConfigCallback) {
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
}
Activity在這里實(shí)現(xiàn)了Window.Callback
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback,
}
public interface Callback {
public boolean dispatchTouchEvent(MotionEvent event);
}
簡單暴力查看
總結(jié)下!
1.當(dāng)我們觸摸屏幕時(shí)净宵,通過INotify機(jī)制&Epoll機(jī)制改變和發(fā)送給WindowInputEventReceiver敲才。
2.WindowInputEventReceiver是ViewRootImpl的內(nèi)部類,通過enqueueInputEvent方法塘娶,將輸入事件加入輸入事件隊(duì)列中归斤,并進(jìn)行處理和轉(zhuǎn)發(fā)。
3.ViewPostImeInputStage收到輸入事件刁岸,將事件傳遞給DecorView的dispatchPointerEvent()方法(是View的方法)
4.dispatchPointerEvent()方法通過DecorView中的dispatchTouchEvent()方法脏里,調(diào)用了Activity的dispatchTouchEvent()方法。