您好,登錄后才能下訂單哦!
DecorView、PhoneWindow和Activity/Dialog之間傳遞的順序是什么,下面我們來看看Input系統(tǒng)、Framework層、DecorView和Activity的相關(guān)內(nèi)容,相信大家就能理解事件先到DecorView的本質(zhì)原因了。
1、Input系統(tǒng)
當(dāng)用戶觸摸屏幕或者按鍵操作,首次觸發(fā)的是硬件驅(qū)動(dòng),驅(qū)動(dòng)收到事件后,將該相應(yīng)事件寫入到輸入設(shè)備節(jié)點(diǎn),這便產(chǎn)生了最原生態(tài)的內(nèi)核事件。接著,輸入系統(tǒng)取出原生態(tài)的事件,經(jīng)過層層封裝后成為KeyEvent或者MotionEvent ;最后,交付給相應(yīng)的目標(biāo)窗口(Window)來消費(fèi)該輸入事件。
(1)當(dāng)屏幕被觸摸,Linux內(nèi)核會(huì)將硬件產(chǎn)生的觸摸事件包裝為Event存到/dev/input/event[x]目錄下。
(2)Input系統(tǒng)—InputReader線程:loop起來讓EventHub調(diào)用getEvent()不斷的從/dev/input/文件夾下讀取輸入事件。然后轉(zhuǎn)換成EventEntry事件加入到InputDispatcher的mInboundQueue。
(3)Input系統(tǒng)—InputDispatcher線程:從mInboundQueue隊(duì)列取出事件,轉(zhuǎn)換成DispatchEntry事件加入到connection的outboundQueue隊(duì)列。再然后開始處理分發(fā)事件 (比如分發(fā)到ViewRootImpl的WindowInputEventReceiver中),取出outbound隊(duì)列,放入waitQueue.
(4)Input系統(tǒng)—UI線程:創(chuàng)建socket pair,分別位于”InputDispatcher”線程和focused窗口所在進(jìn)程的UI主線程,可相互通信。
2、Framework層
//InputEventReceiver.dispachInputEvent()
private void dispatchInputEvent(int seq, InputEvent event)
mSeqMap.put(event.getSequenceNumber(), seq)
onInputEvent(event);
}
Native層通過JNI執(zhí)行Framework層的InputEventReceiver.dispachInputEvent(),而真正調(diào)用的是繼承了InputEventReceiver的ViewRootImpl.WindowInputEventReceiver。所以這里執(zhí)行的WindowInputEventReceiver的dispachInputEvent():
final class WindowInputEventReceiver extends InputEventReceiver {
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
}
ViewRootImpl
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
if (processImmediately) {
//關(guān)鍵點(diǎn):執(zhí)行Input事件
doProcessInputEvents();
} else {
//走一遍Handler延遲處理事件
scheduleProcessInputEvents();
}
}
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);
//關(guān)鍵點(diǎn):進(jìn)一步派發(fā)事件處理
deliverInputEvent(q);
}
}
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 (stage != null) {
//關(guān)鍵點(diǎn):上面決定將事件派發(fā)到那個(gè)InputStage中處理
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
ViewRootImpl.ViewPostImeInputStage
前面事件會(huì)派發(fā)到ViewRootImpl.ViewPostImeInputStage中處理,它的父類InputStage.deliver()方法會(huì)調(diào)用apply()來處理Touch事件:
@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) {
//關(guān)鍵點(diǎn):執(zhí)行分發(fā)touch事件
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;
//關(guān)鍵點(diǎn):mView分發(fā)Touch事件,mView就是DecorView
boolean handled = mView.dispatchPointerEvent(event);
maybeUpdatePointerIcon(event);
maybeUpdateTooltip(event);
}
3、DecorView
如果你熟悉安卓的Window,Activity和Dialog對(duì)應(yīng)的ViewRootImpl成員mView就是DecorView,View的dispatchPointerEvent()代碼如下:
//View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent())
//分發(fā)Touch事件
return dispatchTouchEvent(event)
} else {
return dispatchGenericMotionEvent(event);
}
}
因?yàn)?span>DecorView繼承FrameLayout,上面所以會(huì)調(diào)用DecorView的dispatchTouchEvent():
@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都被Activity和Dialog實(shí)現(xiàn),所以變量cb可能就是Activity和Dialog。
4、Activity
當(dāng)上面cb是Activity時(shí),執(zhí)行Activity的dispatchTouchEvent():
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {//關(guān)鍵點(diǎn):getWindow().superDispatchTouchEvent(ev)
return true;
}
return onTouchEvent(ev);
}
如果你熟悉安卓的Window,Activity的getWindow()拿到的就是PhoneWindow,下面是PhoneWindow的代碼:
//PhoneWindow.java
@Override
public boolean superDispatchTouchEvent(MotionEvent event)
//調(diào)用DecorView的superDispatchTouchEvent
return mDecor.superDispatchTouchEvent(event);
}
下面是DecorView.superDispatchTouchEvent()代碼:
//DecorView.java
public boolean superDispatchTouchEvent(MotionEvent event)
//調(diào)用ViewGroup的dispatchTouchEvent()開始我們常見的分發(fā)Touch事件
return super.dispatchTouchEvent(event);
}
因?yàn)榻怦畹脑?,所以?span>DecorView -> Activity -> PhoneWindow -> DecorView傳遞事件。ViewRootImpl并不知道有Activity這種東西存在!它只是持有了DecorView。所以,不能直接把觸摸事件送到Activity.dispatchTouchEvent();不直接分發(fā)給DecorView,而是要通過PhoneWindow來間接發(fā)送也是因?yàn)?span>Activity不知道有DecorView!但是,Activity持有PhoneWindow ,而PhoneWindow當(dāng)然知道自己的窗口里有些什么了,所以能夠把事件派發(fā)給DecorView。在Android中,Activity并不知道自己的Window中有些什么,這樣耦合性就很低了。不管Window里面的內(nèi)容如何,只要Window仍然符合Activity制定的標(biāo)準(zhǔn),那么它就能在Activity中很好的工作。當(dāng)然,這就是解耦所帶來的擴(kuò)展性的好處。
看完上訴內(nèi)容,你們對(duì)DecorView以及Window的傳遞順序大概了解了嗎?如果想了解更多,歡迎關(guān)注億速云行業(yè)資訊頻道哦!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。