文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Andriod事件分发事件怎么来的

2023-07-05 12:32

关注

本篇内容主要讲解“Andriod事件分发事件怎么来的”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“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.javapublic 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处理的事件。

到此,相信大家对“Andriod事件分发事件怎么来的”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯