本篇内容主要讲解“Andriod事件分发事件怎么来的”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Andriod事件分发事件怎么来的”吧!
事件分发一直以来都是一个android知识的重点。从应用开发角度和用户的交互就是在处理事件。
事件分发一般情况都会讲view的分发过程,他的过程缩略起来就可以这样表示。
public boolean diapatchTouchEvent(MotionEvent ev) { boolean consume = false; if (onInterceptTouchEvent(ev)) { consume = onTouchEvent(ev); } else { consume = child.dispatchTouchEvent(ev); } return consume; }
这里就有一个问题,最早的事件是从哪里来的。根据Android的视图模型知道最外层的view就是DecorView ,而它的外面是一个PhoneWindow。所以最初的事件就是从PhoneWindow进入了view的事件分发,而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创建流程的话就知道ViewRootImpl是所有view的最顶层。也是ViewRootImpl在setView中实现了View和WindowManager之间的交互。这个方法里有一个在Window创建流程的时候没有关注的InputChannel,事件真正的来源就是它,在
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) { synchronized (this) { if (mView == null) { mView = view; ......... InputChannel inputChannel = null;//创建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());//创建mInputEventReceiver } //这里创建了各种事件处理器 // 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的内部类看下它的实现。
final class WindowInputEventReceiver extends InputEventReceiver { public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); } @Override public void onInputEvent(InputEvent event) {//通过名字就知道这应该是事件接收的回调 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不为空都是调用了enqueueInputEvent,不然就直接调用finishInputEvent。
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); //这里做了区分是触摸事件还是按键事件 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,不然调用scheduleProcessInputEvents。而scheduleProcessInputEvents很简单就是handle发送了一个异步消息。最后handle执行的时候还是会调用到doProcessInputEvents。所以就来详细看下doProcessInputEvents。
void doProcessInputEvents() { // Deliver all pending input events in the queue. while (mPendingInputEventHead != null) {//循环获取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中出现了stage,这就是在setView初始化的那些处理器,处理通过stage.deliver(q)来实现。 InputStage 还是ViewRootImpl的一个内部类。
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) { //分发事件 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的话很容易就发现这里也是一个责任链模式。从setView中 InputStage 子类的初始化也能看到,其中和view相关的是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来分发事件 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来继续分发了,这里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,兜兜转转终于回到了Activity的dispatchTouchEvent中。
通过这个流程可以知道,事件的流程是WMS->ViewRootImpl->DecorView->Activity->PhoneWindow->DecorView,这里有一个疑问就是为什么不直接从DecorView开始分发。我猜测是为了方便在应用层重写Activity中的onTouch来消费没有view处理的事件。
到此,相信大家对“Andriod事件分发事件怎么来的”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。