返回顶部
首页 > 资讯 > 移动开发 >Andriod事件分发事件由来初识
  • 830
分享到

Andriod事件分发事件由来初识

Android事件分发Android事件由来 2023-03-15 11:03:47 830人浏览 泡泡鱼
摘要

目录Android事件分发的事件从何而来Activity的事件分发ViewRootImpl事件分发DecorView事件处理Android事件分发的事件从何而来 事件分发一直以来都

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的事件是怎么来的,这个留着后续再分析。

以上就是Android事件分发事件由来初识的详细内容,更多关于Android事件分发事件由来的资料请关注编程网其它相关文章!

--结束END--

本文标题: Andriod事件分发事件由来初识

本文链接: https://lsjlt.com/news/199917.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Andriod事件分发事件由来初识
    目录Android事件分发的事件从何而来Activity的事件分发ViewRootImpl事件分发DecorView事件处理Android事件分发的事件从何而来 事件分发一直以来都...
    99+
    2023-03-15
    Android事件分发 Android事件由来
  • Andriod事件分发事件怎么来的
    本篇内容主要讲解“Andriod事件分发事件怎么来的”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Andriod事件分发事件怎么来的”吧!Android事件分发的事件从何而来事件分发一直以来都是...
    99+
    2023-07-05
  • Android事件分发中事件是怎么来的
    本文小编为大家详细介绍“Android事件分发中事件是怎么来的”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android事件分发中事件是怎么来的”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Andriod事件...
    99+
    2023-07-05
  • Android事件分发机制(上) ViewGroup的事件分发
    综述   Android中的事件分发机制也就是View与ViewGroup的对事件的分发与处理。在ViewGroup的内部包含了许多View,而ViewGroup继承自View...
    99+
    2022-06-06
    Android
  • Android触摸事件传递机制初识
    前言今天总结的一个知识点是Andorid中View事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View的另外一个难题滑动冲突,比如在ScrollView中嵌套ListView,都是上下滑动,这该如何解决呢...
    99+
    2023-05-31
    android 触摸 roi
  • Android 点击事件分发
    Android 点击事件分发Activity中对事件的处理ViewGroup是如何进行事件处理的View的dispatchTouchEvent相...
    99+
    2022-06-06
    事件 Android
  • Android事件分发机制
    事件分发流程相关 一个事件发生后,首先从Acrtivity开始传递,然后一层一层往下传,从上往下调用dispatchTouchEvent方法传递...
    99+
    2022-06-06
    Android
  • vue如何分发事件
    在vue中分发事件的方法:1.新建vue.js项目;2.使用@click属性添加事件;3.使用$emit分发事件;具体步骤如下:首先,在vue-cli中创建一个vue.js项目;vue create project-namevue.js项目...
    99+
    2024-04-02
  • Android事件分发机制(下) View的事件处理
    综述   在上篇文章Android中的事件分发机制(上)——ViewGroup的事件分发中,对ViewGroup的事件分发进行了详细的分析。在文章的最后ViewGroup的di...
    99+
    2022-06-06
    view Android
  • javascript事件由哪些部分组成
    这篇文章主要介绍“javascript事件由哪些部分组成”,在日常操作中,相信很多人在javascript事件由哪些部分组成问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”ja...
    99+
    2024-04-02
  • Android事件分发之View事件处理关键及示例分析
    目录目的View处理事件的关键View事件处理分析View.onTouchEvent()分析处理长按事件处理点击事件处理tap事件总结目的 网上已经有很多关于事件分发的优秀文章,为...
    99+
    2023-02-14
    Android事件分发View事件处理 Android View事件
  • Android事件分发机制 ViewGroup分析
    目录整体流程源码分析前言: 事件分发从手指触摸屏幕开始,即产生了触摸信息,被底层系统捕获后会传递给Android的输入系统服务IMS,通过Binder把消息发送到activity,a...
    99+
    2024-04-02
  • Flex事件机制中Flex事件分发和监听的示例分析
    这篇文章给大家分享的是有关Flex事件机制中Flex事件分发和监听的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。什么是Flex事件机制Flex事件可以看作是一种触发机制,当满足了一定的条件后,会触发这个...
    99+
    2023-06-17
  • Android 事件分发机制 讲解
    1、分发事件的组件 分发事件的组件,也称为分发事件者,包括Activity、ViewGroup和View。它们三者的一般结构为: 从上图中可以...
    99+
    2022-06-06
    事件 Android
  • Android View的事件分发机制
    一.Android View框架提供了3个对事件的主要操作概念。 1、事件的分发机制,dispatchTouchEvent。主要是parent根据触摸事件的产生位置,以及chi...
    99+
    2022-06-06
    view 事件 Android
  • WPF自定义路由事件
    与依赖项属性类似,WPF也为路由事件提供了WPF事件系统这一组成。为一个类型添加一个路由事件的方式与为类型添加依赖项属性的方法类似,添加一个自定义路由事件的步骤: 一、声明路由事件变...
    99+
    2024-04-02
  • js事件流、事件委托与事件阶段的示例分析
    这篇文章主要介绍了js事件流、事件委托与事件阶段的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1、事件流HTML 中与 javascript 交互是通过事件驱动来实...
    99+
    2023-06-29
  • Android事件分发机制示例分析
    Android事件类型 public boolean onTouchEvent(MotionEvent event) { switch (event.getActio...
    99+
    2024-04-02
  • mysql事件之修改事件、禁用事件、启用事件、事件重命名及数据库事件迁移操作的示例分析
    这篇文章主要为大家展示了“mysql事件之修改事件、禁用事件、启用事件、事件重命名及数据库事件迁移操作的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“m...
    99+
    2024-04-02
  • js的onload事件及初始化按钮事件示例代码
    大家常见的肯定都是<body onload=""> 这种用法.其实,不仅可以在body 上用它还支持多个HTML标签.用法如下: onload事件当一个页面或是一张图片加...
    99+
    2022-11-15
    onload事件 初始化按钮
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作