Andriod事件分发事件由来初识

Android事件分发的事件从何而来

事件分发一直以来都是一个android知识的重点。从应用开发角度和用户的交互就是在处理事件。

Activity的事件分发

事件分发一般情况都会讲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的事件是哪里来的呢。

ViewRootImpl事件分发

熟悉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中传进来的。

DecorView事件处理

//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处理的事件。

现在还有一个疑问是WMS的事件是怎么来的,这个留着后续再分析。

作者:Arthas0v0

%s 个评论

要回复文章请先登录注册