我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Andriod事件分发事件怎么来的

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

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Andriod事件分发事件怎么来的

本篇内容主要讲解“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

Andriod事件分发事件怎么来的

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Andriod事件分发事件怎么来的

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

Andriod事件分发事件由来初识

这篇文章主要为大家讲解了Andriod事件分发事件由来的初步认识,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-15

Android事件分发中事件是怎么来的

本文小编为大家详细介绍“Android事件分发中事件是怎么来的”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android事件分发中事件是怎么来的”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Andriod事件
2023-07-05

Android事件分发机制(上) ViewGroup的事件分发

综述Android中的事件分发机制也就是View与ViewGroup的对事件的分发与处理。在ViewGroup的内部包含了许多View,而ViewGroup继承自View,所以ViewGroup本身也是一个View。对于事件可以通过View
2022-06-06

Android事件分发机制(下) View的事件处理

综述在上篇文章Android中的事件分发机制(上)——ViewGroup的事件分发中,对ViewGroup的事件分发进行了详细的分析。在文章的最后ViewGroup的dispatchTouchEvent方法调用dispatchTransfo
2022-06-06

Android 点击事件分发

Android 点击事件分发Activity中对事件的处理ViewGroup是如何进行事件处理的View的dispatchTouchEvent相当重要,让我们继续look总结 Activity中对事件的处理 Activity事件分发方法,返
2022-06-06

Android中怎么实现 View事件分发

这篇文章给大家介绍Android中怎么实现 View事件分发,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。(1)ViewGroup.dispatchTouchEvent(event)boolean dispatchTo
2023-05-30

Android事件分发机制

事件分发流程相关 一个事件发生后,首先从Acrtivity开始传递,然后一层一层往下传,从上往下调用dispatchTouchEvent方法传递事件: Activity——>PhoneWindow——>DecorView——>ViewGro
2022-06-06

Android View的事件分发机制

一.Android View框架提供了3个对事件的主要操作概念。 1、事件的分发机制,dispatchTouchEvent。主要是parent根据触摸事件的产生位置,以及child是否愿意负责处理该系列事件等状态,向其child分发事件的机
2022-06-06

Flex事件机制中Flex事件分发和监听的示例分析

这篇文章给大家分享的是有关Flex事件机制中Flex事件分发和监听的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。什么是Flex事件机制Flex事件可以看作是一种触发机制,当满足了一定的条件后,会触发这个
2023-06-17

Android事件分发的流程是什么

Android事件分发的流程如下:事件发生:用户在屏幕上进行触摸或其他操作。事件捕获:事件首先被传递给顶级父视图(通常是Activity或Window)的dispatchTouchEvent方法。事件分发:顶级父视图将事件传递给其子视图的d
2023-10-24

android事件分发流程是什么

Android事件分发流程主要包括以下几个步骤:1. 事件产生:用户在屏幕上进行触摸、点击、滑动等操作时,会产生相应的事件。2. 事件传递:事件首先由顶层的ViewGroup接收,然后按照View树的层次结构依次传递给各个View,直到找到
2023-08-15

Android事件分发之View事件处理关键及示例分析

这篇文章主要为大家介绍了Android事件分发之View事件处理关键及示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-14

Android怎么开发Input系统触摸事件分发

本篇内容介绍了“Android怎么开发Input系统触摸事件分发”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!引言Input系统: Inpu
2023-07-05

js事件流、事件委托与事件阶段的示例分析

这篇文章主要介绍了js事件流、事件委托与事件阶段的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1、事件流HTML 中与 javascript 交互是通过事件驱动来实
2023-06-29

Android事件的分发机制详解

在分析Android事件分发机制前,明确android的两大基础控件类型:View和ViewGroup。View即普通的控件,没有子布局的,如Button、TextView. ViewGroup继承自View,表示可以有子控件,如Linea
2022-06-06

Android事件分发机制的详解

Android事件分发机制我们只考虑最重要的四个触摸事件,即:DOWN,MOVE,UP和CANCEL。一个手势(gesture)是一个事件列,以一个DOWN事件开始(当用户触摸屏幕时产生),后跟0个或多个MOVE事件(当用户四处移动手指时产
2023-05-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录