您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Andriod事件分發(fā)事件怎么來的”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Andriod事件分發(fā)事件怎么來的”吧!
事件分發(fā)一直以來都是一個android知識的重點。從應(yīng)用開發(fā)角度和用戶的交互就是在處理事件。
事件分發(fā)一般情況都會講view的分發(fā)過程,他的過程縮略起來就可以這樣表示。
public boolean diapatchTouchEvent(MotionEvent ev) { boolean consume = false; if (onInterceptTouchEvent(ev)) { consume = onTouchEvent(ev); } else { consume = child.dispatchTouchEvent(ev); } return consume; }
這里就有一個問題,最早的事件是從哪里來的。根據(jù)Android的視圖模型知道最外層的view就是DecorView ,而它的外面是一個PhoneWindow。所以最初的事件就是從PhoneWindow進入了view的事件分發(fā),而PhoneWindow的事件又是Activity中來的.
//frameworks/base/core/java/android/app/Activity.java public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) {//這里獲取的PhoneWindow return true; } return onTouchEvent(ev); }
那么問題又來了,activity的事件是哪里來的呢。
熟悉Android的Window創(chuàng)建流程的話就知道ViewRootImpl是所有view的最頂層。也是ViewRootImpl在setView中實現(xiàn)了View和WindowManager之間的交互。這個方法里有一個在Window創(chuàng)建流程的時候沒有關(guān)注的InputChannel,事件真正的來源就是它,在
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) { synchronized (this) { if (mView == null) { mView = view; ......... InputChannel inputChannel = null;//創(chuàng)建InputChannel if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { inputChannel = new InputChannel(); } res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls, attachedFrame, sizeCompatScale);//將InputChannel傳給WMS if (inputChannel != null) { if (mInputQueueCallback != null) { mInputQueue = new InputQueue(); mInputQueueCallback.onInputQueueCreated(mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver(inputChannel, Looper.myLooper());//創(chuàng)建mInputEventReceiver } //這里創(chuàng)建了各種事件處理器 // Set up the input pipeline. CharSequence counterSuffix = attrs.getTitle(); mSyntheticInputStage = new SyntheticInputStage(); InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, "aq:native-post-ime:" + counterSuffix); InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); InputStage imeStage = new ImeInputStage(earlyPostImeStage, "aq:ime:" + counterSuffix); InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage); InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage, "aq:native-pre-ime:" + counterSuffix); mFirstInputStage = nativePreImeStage; mFirstPostImeInputStage = earlyPostImeStage; mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix; AnimationHandler.requestAnimatorsEnabled(mAppVisible, this); } } }
從名字也能猜出mInputEventReceiver就是接收事件的對象了,這是一個ViewRootImpl的內(nèi)部類看下它的實現(xiàn)。
final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } @Override public void onInputEvent(InputEvent event) {//通過名字就知道這應(yīng)該是事件接收的回調(diào) List<InputEvent> processedEvents; try { processedEvents = mInputCompatProcessor.processInputEventForCompatibility(event); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (processedEvents != null) { if (processedEvents.isEmpty()) { // InputEvent consumed by mInputCompatProcessor finishInputEvent(event, true); } else { for (int i = 0; i < processedEvents.size(); i++) { enqueueInputEvent( processedEvents.get(i), this, QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true); } } } else { enqueueInputEvent(event, this, 0, true); } } ....... }
如果processedEvents不為空都是調(diào)用了enqueueInputEvent,不然就直接調(diào)用finishInputEvent。
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); //這里做了區(qū)分是觸摸事件還是按鍵事件 if (event instanceof MotionEvent) { MotionEvent me = (MotionEvent) event; } else if (event instanceof KeyEvent) { KeyEvent ke = (KeyEvent) event; } QueuedInputEvent last = mPendingInputEventTail; if (last == null) { mPendingInputEventHead = q; mPendingInputEventTail = q; } else { last.mNext = q; mPendingInputEventTail = q; } mPendingInputEventCount += 1; if (processImmediately) { doProcessInputEvents(); } else { scheduleProcessInputEvents(); } } private void scheduleProcessInputEvents() { if (!mProcessInputEventsScheduled) { mProcessInputEventsScheduled = true; Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS); msg.setAsynchronous(true); mHandler.sendMessage(msg); } } private void handleMessageImpl(Message msg) { switch (msg.what) { case MSG_PROCESS_INPUT_EVENTS: mProcessInputEventsScheduled = false; doProcessInputEvents(); } }
這里判斷了是否要立即消費,如果立即消費doProcessInputEvents,不然調(diào)用scheduleProcessInputEvents。而scheduleProcessInputEvents很簡單就是handle發(fā)送了一個異步消息。最后handle執(zhí)行的時候還是會調(diào)用到doProcessInputEvents。所以就來詳細看下doProcessInputEvents。
void doProcessInputEvents() { // Deliver all pending input events in the queue. while (mPendingInputEventHead != null) {//循環(huán)獲取InputEvent并處理 QueuedInputEvent q = mPendingInputEventHead; mPendingInputEventHead = q.mNext; if (mPendingInputEventHead == null) { mPendingInputEventTail = null; } q.mNext = null; mPendingInputEventCount -= 1; mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent)); deliverInputEvent(q); } //移除異步消息 if (mProcessInputEventsScheduled) { mProcessInputEventsScheduled = false; mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); } }
可以看到真實的處理都是deliverInputEvent來處理。
private void deliverInputEvent(QueuedInputEvent q) { try { if (mInputEventConsistencyVerifier != null) { InputStage stage;//在ViewRootImpl的setView中初始化的處理器 if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } if (q.mEvent instanceof KeyEvent) { try { mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); } else { finishInputEvent(q); } } finally { } }
在deliverInputEvent中出現(xiàn)了stage,這就是在setView初始化的那些處理器,處理通過stage.deliver(q)來實現(xiàn)。 InputStage 還是ViewRootImpl的一個內(nèi)部類。
abstract class InputStage { private final InputStage mNext; protected static final int FORWARD = 0; protected static final int FINISH_HANDLED = 1; protected static final int FINISH_NOT_HANDLED = 2; private String mTracePrefix; public InputStage(InputStage next) { mNext = next; } public final void deliver(QueuedInputEvent q) { //分發(fā)事件 if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) { forward(q); } else if (shouldDropInputEvent(q)) { finish(q, false); } else { traceEvent(q, Trace.TRACE_TAG_VIEW); final int result; try { result = onProcess(q); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } apply(q, result); } } //處理事件由子類改寫 protected int onProcess(QueuedInputEvent q) { return FORWARD; } protected void finish(QueuedInputEvent q, boolean handled) { q.mFlags |= QueuedInputEvent.FLAG_FINISHED; if (handled) { q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED; } forward(q); } protected void forward(QueuedInputEvent q) { onDeliverToNext(q); } protected void onDeliverToNext(QueuedInputEvent q) { //向后一個 InputStage 傳遞事件 if (mNext != null) { mNext.deliver(q); } else { finishInputEvent(q); } } }
熟悉okhttp的話很容易就發(fā)現(xiàn)這里也是一個責(zé)任鏈模式。從setView中 InputStage 子類的初始化也能看到,其中和view相關(guān)的是ViewPostImeInputStage。
final class ViewPostImeInputStage extends InputStage { public ViewPostImeInputStage(InputStage next) { super(next); } @Override protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); } else { final int source = q.mEvent.getSource(); //判斷事件類型,觸摸事件會進入processPointerEvent 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; mHandwritingInitiator.onTouchEvent(event); mAttachInfo.mUnbufferedDispatchRequested = false; mAttachInfo.mHandlingPointerEvent = true; //通過mView的dispatchPointerEvent來分發(fā)事件 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; }
ViewRootImpl的事件就交給mView來繼續(xù)分發(fā)了,這里mView是DecorView,也是在setView中傳進來的。
//frameworks/base/core/java/android/view/View.java @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public final boolean dispatchPointerEvent(MotionEvent event) { if (event.isTouchEvent()) { return dispatchTouchEvent(event); } else { return dispatchGenericMotionEvent(event); } } //frameworks/base/core/java/com/android/internal/policy/DecorView.java @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); }
這里通過dispatchTouchEvent將事件交給了Window.Callback,而這里的Window.Callback就是Activity,兜兜轉(zhuǎn)轉(zhuǎn)終于回到了Activity的dispatchTouchEvent中。
通過這個流程可以知道,事件的流程是WMS->ViewRootImpl->DecorView->Activity->PhoneWindow->DecorView,這里有一個疑問就是為什么不直接從DecorView開始分發(fā)。我猜測是為了方便在應(yīng)用層重寫Activity中的onTouch來消費沒有view處理的事件。
到此,相信大家對“Andriod事件分發(fā)事件怎么來的”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。