Andriod事件分发事件怎么来的

其他教程   发布日期:2023年08月31日   浏览次数:392

本篇内容主要讲解“Andriod事件分发事件怎么来的”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Andriod事件分发事件怎么来的”吧!

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

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

Activity的事件分发

事件分发一般情况都会讲view的分发过程,他的过程缩略起来就可以这样表示。

  1. public boolean diapatchTouchEvent(MotionEvent ev) {
  2. boolean consume = false;
  3. if (onInterceptTouchEvent(ev)) {
  4. consume = onTouchEvent(ev);
  5. } else {
  6. consume = child.dispatchTouchEvent(ev);
  7. }
  8. return consume;
  9. }

这里就有一个问题,最早的事件是从哪里来的。根据Android的视图模型知道最外层的view就是DecorView ,而它的外面是一个PhoneWindow。所以最初的事件就是从PhoneWindow进入了view的事件分发,而PhoneWindow的事件又是Activity中来的.

  1. //frameworks/base/core/java/android/app/Activity.java
  2. public boolean dispatchTouchEvent(MotionEvent ev) {
  3. if (ev.getAction() == MotionEvent.ACTION_DOWN) {
  4. onUserInteraction();
  5. }
  6. if (getWindow().superDispatchTouchEvent(ev)) {//这里获取的PhoneWindow
  7. return true;
  8. }
  9. return onTouchEvent(ev);
  10. }

那么问题又来了,activity的事件是哪里来的呢。

ViewRootImpl事件分发

熟悉Android的Window创建流程的话就知道ViewRootImpl是所有view的最顶层。也是ViewRootImpl在setView中实现了View和WindowManager之间的交互。这个方法里有一个在Window创建流程的时候没有关注的InputChannel,事件真正的来源就是它,在

  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
  2. int userId) {
  3. synchronized (this) {
  4. if (mView == null) {
  5. mView = view;
  6. .........
  7. InputChannel inputChannel = null;//创建InputChannel
  8. if ((mWindowAttributes.inputFeatures
  9. & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
  10. inputChannel = new InputChannel();
  11. }
  12. res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
  13. getHostVisibility(), mDisplay.getDisplayId(), userId,
  14. mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls, attachedFrame, sizeCompatScale);//将InputChannel传给WMS
  15. if (inputChannel != null) {
  16. if (mInputQueueCallback != null) {
  17. mInputQueue = new InputQueue();
  18. mInputQueueCallback.onInputQueueCreated(mInputQueue);
  19. }
  20. mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
  21. Looper.myLooper());//创建mInputEventReceiver
  22. }
  23. //这里创建了各种事件处理器
  24. // Set up the input pipeline.
  25. CharSequence counterSuffix = attrs.getTitle();
  26. mSyntheticInputStage = new SyntheticInputStage();
  27. InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
  28. InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
  29. "aq:native-post-ime:" + counterSuffix);
  30. InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
  31. InputStage imeStage = new ImeInputStage(earlyPostImeStage,
  32. "aq:ime:" + counterSuffix);
  33. InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
  34. InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
  35. "aq:native-pre-ime:" + counterSuffix);
  36. mFirstInputStage = nativePreImeStage;
  37. mFirstPostImeInputStage = earlyPostImeStage;
  38. mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
  39. AnimationHandler.requestAnimatorsEnabled(mAppVisible, this);
  40. }
  41. }
  42. }

从名字也能猜出mInputEventReceiver就是接收事件的对象了,这是一个ViewRootImpl的内部类看下它的实现。

  1. final class WindowInputEventReceiver extends InputEventReceiver {
  2. public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
  3. super(inputChannel, looper);
  4. }
  5. @Override
  6. public void onInputEvent(InputEvent event) {//通过名字就知道这应该是事件接收的回调
  7. List<InputEvent> processedEvents;
  8. try {
  9. processedEvents =
  10. mInputCompatProcessor.processInputEventForCompatibility(event);
  11. } finally {
  12. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  13. }
  14. if (processedEvents != null) {
  15. if (processedEvents.isEmpty()) {
  16. // InputEvent consumed by mInputCompatProcessor
  17. finishInputEvent(event, true);
  18. } else {
  19. for (int i = 0; i < processedEvents.size(); i++) {
  20. enqueueInputEvent(
  21. processedEvents.get(i), this,
  22. QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
  23. }
  24. }
  25. } else {
  26. enqueueInputEvent(event, this, 0, true);
  27. }
  28. }
  29. .......
  30. }

如果processedEvents不为空都是调用了enqueueInputEvent,不然就直接调用finishInputEvent。

  1. void enqueueInputEvent(InputEvent event,
  2. InputEventReceiver receiver, int flags, boolean processImmediately) {
  3. QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
  4. //这里做了区分是触摸事件还是按键事件
  5. if (event instanceof MotionEvent) {
  6. MotionEvent me = (MotionEvent) event;
  7. } else if (event instanceof KeyEvent) {
  8. KeyEvent ke = (KeyEvent) event;
  9. }
  10. QueuedInputEvent last = mPendingInputEventTail;
  11. if (last == null) {
  12. mPendingInputEventHead = q;
  13. mPendingInputEventTail = q;
  14. } else {
  15. last.mNext = q;
  16. mPendingInputEventTail = q;
  17. }
  18. mPendingInputEventCount += 1;
  19. if (processImmediately) {
  20. doProcessInputEvents();
  21. } else {
  22. scheduleProcessInputEvents();
  23. }
  24. }
  25. private void scheduleProcessInputEvents() {
  26. if (!mProcessInputEventsScheduled) {
  27. mProcessInputEventsScheduled = true;
  28. Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
  29. msg.setAsynchronous(true);
  30. mHandler.sendMessage(msg);
  31. }
  32. }
  33. private void handleMessageImpl(Message msg) {
  34. switch (msg.what) {
  35. case MSG_PROCESS_INPUT_EVENTS:
  36. mProcessInputEventsScheduled = false;
  37. doProcessInputEvents();
  38. }
  39. }

这里判断了是否要立即消费,如果立即消费doProcessInputEvents,不然调用scheduleProcessInputEvents。而scheduleProcessInputEvents很简单就是handle发送了一个异步消息。最后handle执行的时候还是会调用到doProcessInputEvents。所以就来详细看下doProcessInputEvents。

  1. void doProcessInputEvents() {
  2. // Deliver all pending input events in the queue.
  3. while (mPendingInputEventHead != null) {//循环获取InputEvent并处理
  4. QueuedInputEvent q = mPendingInputEventHead;
  5. mPendingInputEventHead = q.mNext;
  6. if (mPendingInputEventHead == null) {
  7. mPendingInputEventTail = null;
  8. }
  9. q.mNext = null;
  10. mPendingInputEventCount -= 1;
  11. mViewFrameInfo.setInputEvent(mInputEventAssigner.processEvent(q.mEvent));
  12. deliverInputEvent(q);
  13. }
  14. //移除异步消息
  15. if (mProcessInputEventsScheduled) {
  16. mProcessInputEventsScheduled = false;
  17. mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
  18. }
  19. }

可以看到真实的处理都是deliverInputEvent来处理。

  1. private void deliverInputEvent(QueuedInputEvent q) {
  2. try {
  3. if (mInputEventConsistencyVerifier != null) {
  4. InputStage stage;//在ViewRootImpl的setView中初始化的处理器
  5. if (q.shouldSendToSynthesizer()) {
  6. stage = mSyntheticInputStage;
  7. } else {
  8. stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
  9. }
  10. if (q.mEvent instanceof KeyEvent) {
  11. try {
  12. mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
  13. } finally {
  14. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  15. }
  16. }
  17. if (stage != null) {
  18. handleWindowFocusChanged();
  19. stage.deliver(q);
  20. } else {
  21. finishInputEvent(q);
  22. }
  23. } finally {
  24. }
  25. }

在deliverInputEvent中出现了stage,这就是在setView初始化的那些处理器,处理通过stage.deliver(q)来实现。 InputStage 还是ViewRootImpl的一个内部类。

  1. abstract class InputStage {
  2. private final InputStage mNext;
  3. protected static final int FORWARD = 0;
  4. protected static final int FINISH_HANDLED = 1;
  5. protected static final int FINISH_NOT_HANDLED = 2;
  6. private String mTracePrefix;
  7. public InputStage(InputStage next) {
  8. mNext = next;
  9. }
  10. public final void deliver(QueuedInputEvent q) {
  11. //分发事件
  12. if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
  13. forward(q);
  14. } else if (shouldDropInputEvent(q)) {
  15. finish(q, false);
  16. } else {
  17. traceEvent(q, Trace.TRACE_TAG_VIEW);
  18. final int result;
  19. try {
  20. result = onProcess(q);
  21. } finally {
  22. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  23. }
  24. apply(q, result);
  25. }
  26. }
  27. //处理事件由子类改写
  28. protected int onProcess(QueuedInputEvent q) {
  29. return FORWARD;
  30. }
  31. protected void finish(QueuedInputEvent q, boolean handled) {
  32. q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
  33. if (handled) {
  34. q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
  35. }
  36. forward(q);
  37. }
  38. protected void forward(QueuedInputEvent q) {
  39. onDeliverToNext(q);
  40. }
  41. protected void onDeliverToNext(QueuedInputEvent q) {
  42. //向后一个 InputStage 传递事件
  43. if (mNext != null) {
  44. mNext.deliver(q);
  45. } else {
  46. finishInputEvent(q);
  47. }
  48. }
  49. }

熟悉okhttp的话很容易就发现这里也是一个责任链模式。从setView中 InputStage 子类的初始化也能看到,其中和view相关的是ViewPostImeInputStage。

  1. final class ViewPostImeInputStage extends InputStage {
  2. public ViewPostImeInputStage(InputStage next) {
  3. super(next);
  4. }
  5. @Override
  6. protected int onProcess(QueuedInputEvent q) {
  7. if (q.mEvent instanceof KeyEvent) {
  8. return processKeyEvent(q);
  9. } else {
  10. final int source = q.mEvent.getSource();
  11. //判断事件类型,触摸事件会进入processPointerEvent
  12. if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
  13. return processPointerEvent(q);
  14. } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
  15. return processTrackballEvent(q);
  16. } else {
  17. return processGenericMotionEvent(q);
  18. }
  19. }
  20. }
  21. private int processPointerEvent(QueuedInputEvent q) {
  22. final MotionEvent event = (MotionEvent)q.mEvent;
  23. mHandwritingInitiator.onTouchEvent(event);
  24. mAttachInfo.mUnbufferedDispatchRequested = false;
  25. mAttachInfo.mHandlingPointerEvent = true;
  26. //通过mView的dispatchPointerEvent来分发事件
  27. boolean handled = mView.dispatchPointerEvent(event);
  28. maybeUpdatePointerIcon(event);
  29. maybeUpdateTooltip(event);
  30. mAttachInfo.mHandlingPointerEvent = false;
  31. if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
  32. mUnbufferedInputDispatch = true;
  33. if (mConsumeBatchedInputScheduled) {
  34. scheduleConsumeBatchedInputImmediately();
  35. }
  36. }
  37. return handled ? FINISH_HANDLED : FORWARD;
  38. }

ViewRootImpl的事件就交给mView来继续分发了,这里mView是DecorView,也是在setView中传进来的。

DecorView事件处理

  1. //frameworks/base/core/java/android/view/View.java
  2. @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
  3. public final boolean dispatchPointerEvent(MotionEvent event) {
  4. if (event.isTouchEvent()) {
  5. return dispatchTouchEvent(event);
  6. } else {
  7. return dispatchGenericMotionEvent(event);
  8. }
  9. }
  10. //frameworks/base/core/java/com/android/internal/policy/DecorView.java
  11. @Override
  12. public boolean dispatchTouchEvent(MotionEvent ev) {
  13. final Window.Callback cb = mWindow.getCallback();
  14. return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
  15. ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
  16. }

这里通过dispatchTouchEvent将事件交给了Window.Callback,而这里的Window.Callback就是Activity,兜兜转转终于回到了Activity的dispatchTouchEvent中。

通过这个流程可以知道,事件的流程是WMS->ViewRootImpl->DecorView->Activity->PhoneWindow->DecorView,这里有一个疑问就是为什么不直接从DecorView开始分发。我猜测是为了方便在应用层重写Activity中的onTouch来消费没有view处理的事件。

以上就是Andriod事件分发事件怎么来的的详细内容,更多关于Andriod事件分发事件怎么来的的资料请关注九品源码其它相关文章!