「java事件分发」java消息发送

博主:adminadmin 2022-12-26 12:09:08 77

今天给各位分享java事件分发的知识,其中也会对java消息发送进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

Android TV 按键焦点事件分发流程详解

DecorView →PhoneWindow →Activity→ViewGroup→view

下面我们根据按键事件的分发流程,抽丝剥茧,逐一分析。

private int processKeyEvent(QueuedInputEvent q)

1、DecorView.java

2、Activity.java

3、ViewGroup.java

4、View.java

通过该方法,接收器receiver的onKeyDown、onKeyUp、onKeyLongPress、onKeyMultiple等方法将被回调。

在上述按键事件的入口中提到的ViewRootImpl中

如果mView.dispatchKeyEvent(event)返回true,则结束事件分发;

如果返回false,则调用如下方法

继续执行后续的焦点导航流程。

焦点导航的总体流程就是:

1、View focused = mView.findFocus();//从视图树的顶层,即DecorView一层一层的递归查找当前获得焦点的view

2、View v = focused.focusSearch(direction);根据导航的方向查找下一个可获取焦点的view

3、v.requestFocus(direction, mTempRect)请求获取焦点

4、v.requestFocus(direction,mTempRect)内部,调用mParent.requestChildFocus(this, focused)逐层递归向上级通知

ViewRootImpl.java

mView即DecorView,从DecorView开始,一层一层的向下递归查找当前获得焦点的view

找到了当前获得焦点的focused,调用该焦点view的focusSearch(direction)方法查找direction方向上下一个将要获取焦点的view。

focused.focusSearch(direction)实际上会调用mParent.focusSearch(this, direction)方法,层层递归,直到调用到DecorView的focusSearch(this, direction)方法。

而DecorView继承ViewGroup,实际上最终会调用到FocusFinder.getInstance().findNextFocus(this, focused, direction),this 就是DecorView对象。

最终会调用到DecorView父类ViewGroup中的FocusFinder.getInstance().findNextFocus(this, focused, direction);

ViewGroup.java

FocusFinder.java

搜索到下一个获取焦点的view后,调用该view.requestFocus(direction, mTempRect)方法

注意:调用requestFocus(direction, mTempRect)需要区分调用者。

如果是ViewGroup,则会更加焦点获取策略,实现父View和子View之间获取焦点的优先级。

如下是ViewGroup.java 和View.java 中requestFocus方法是实现:

ViewGroup.java

View.java

View获取到焦点后,会调用mParent.requestChildFocus(this, focused)逐层递归向上级通知

ViewGroup.java

flush是异步吗java

一、基本概念

同步和异步:

同步和异步是针对应用程序和内核的交互而言的。

同步指的是用户进程触发IO 操作并等待或者轮询的去查看IO 操作是否就绪;

而异步是指用户进程触发IO 操作以后便开始做自己的事情,而当IO 操作已经完成的时候会得到IO 完成的通知。

以银行取款为例:

同步 : 自己亲自出马持银行卡到银行取钱(使用同步 IO 时,Java 自己处理IO 读写);

异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO 时,Java 将 IO 读写委托给OS 处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS 需要支持异步IO操作API);

阻塞和非阻塞:阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作方法的实现方式。

阻塞方式下,读取或者写入函数将一直等待;

而非阻塞方式下,读取或者写入方法会立即返回一个状态值。

以银行取款为例:

阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回);

非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器通知可读写时再继续进行读写,不断循环直到读写完成)

二、应用方式

2.1 BIO 编程

Blocking IO: 同步阻塞的编程方式。

BIO编程方式通常是在JDK1.4版本之前常用的编程方式。编程实现过程为:

首先,在服务端启动一个ServerSocket来监听网络请求,客户端启动Socket发起网络请求。

默认情况下ServerSocket回建立一个线程来处理此请求,如果服务端没有线程可用,客户端则会阻塞等待或遭到拒绝。

且建立好的连接,在通讯过程中,是同步的。在并发处理效率上比较低。大致结构如下:

同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

使用线程池机制改善后的BIO模型图如下:

2.NIO 编程

Unblocking IO(New IO): 同步非阻塞的编程方式。

NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题。NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。

也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

NIO的最重要的地方是,当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面。所以,所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。

在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题

3.AIO编程

Asynchronous IO: 异步非阻塞的编程方式。

与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道:AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileChannel、AsynchronousDatagramChannel。

北大青鸟java培训:map和flatmap的共同点和区别?

在函数式语言中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。

由于命令式编程语言也可以通过类似函数指针的方式来实现高阶函数,函数式的最主要的好处主要是不可变性带来的。

没有可变的状态,函数就是引用透明(Referentialtransparency)的和没有副作用(NoSideEffect)。

IT培训就来为大家介绍介绍。

任何一种函数式语言中,都有map函数与faltMap这两个函数,比如python虽然不是纯函数式语言,也有这两个函数。

再比如在jdk1.8之后,也加入了Lambda表达式,自然也支持map函数。

map和faltMap的共同点和区别1、共同点都是依赖FuncX(入参,返回值)进行转换(将一个类型依据程序逻辑转换成另一种类型,根据入参和返回值)都能在转换后直接被subscribe2、区别map返回的是结果集,flatmap返回的是包含结果集的Observable(返回结果不同)map被订阅时每传递一个事件执行一次onNext方法,flatmap多用于多对多,一对多,再被转化为多个时,一般利用from/just进行一一分发,被订阅时将所有数据传递完毕汇总到一个Observable然后一一执行onNext方法(执行顺序不同)(如单纯用于一对一转换则和map相同)map只能单一转换,单一只的是只能一对一进行转换,指一个对象可以转化为另一个对象但是不能转换成对象数组(map返回结果集不能直接使用from/just再次进行事件分发,一旦转换成对象数组的话,再处理集合/数组的结果时需要利用for一一遍历取出,而使用RxJava就是为了剔除这样的嵌套结构,使得整体的逻辑性更强。

)flatmap既可以单一转换也可以一对多/多对多转换,flatmap要求返回Observable,因此可以再内部进行from/just的再次事件分发,一一取出单一对象(转换对象的能力不同)map函数的用法,顾名思义,将一个函数传入map中,然后利用传入的这个函数,将集合中的每个元素处理,并将处理后的结果返回。

而flatMap与map唯一不一样的地方就是传入的函数在处理完后返回值必须是List,其实这也不难理解,既然是flatMap,那除了map以外必然还有flat的操作,所以需要返回值是List才能执行flat这一步。

java的paint方法是怎么调用的,是自动调用的吗?

Java的paint方法是事件分发线程调用的,并不在编程者的控制范围之内。

参考:网页链接

什么是Java NIO,它的工作原理是什么

Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。

1. 由一个专门的线程来处理所有的 IO 事件,并负责分发。

2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。

3. 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。

Android重学系列 IMS与事件分发(下)

上一篇文章和大家聊到了IMS在SystemServer进程native层中的原理,本文来聊聊App进程是怎么监听IMS分发出来的输入信号的.

还记得我写过WMS系列文章 WMS在Activity启动中的职责 添加窗体(三) 中,提到了App第一次渲染的时候会通过ViewRootImpl的addWindow方法,在WMS中为当前的Activity中的PhoneWindow添加一个对应的WindowState进行管理。

让我们先看看ViewRootImpl中做了什么。

如果遇到什么问题,欢迎来到本文 下讨论

文件:/ frameworks / base / core / java / android / view / ViewRootImpl.java

在这个过程中,我们可以把它视作三大部分的逻辑

核心就是第二和第三点。先来看看第二点,Session的addToDisplay最后是调用到了WMS的addWindow中。

文件:/ frameworks / base / services / core / java / com / android / server / wm / WindowManagerService.java

我们把InputChannel相关的逻辑抽离出来:

我们来看看WindowState的openInputChannel方法。

文件:/ frameworks / base / services / core / java / com / android / server / wm / WindowState.java

能看到实际上这个过程诞生了一个很重要的对象InputWindowHandle,输入窗口的句柄。这个句柄最核心的对象就是通过WindowToken获取AppToken的InputApplicationHandle。

能看到这个过程,实际上和上一篇文章十分相似的monitorInput一节中的内容十分相似。

依次执行了如下的逻辑:

这样通过socketpair创建的一对socket对象,注册了一个新的发送端到IMS的native层中,就能被App端的InputChannel监听到。

从这里就可以知道,0号位置的InputChannel对应的socket就是服务端(发送端)。关于如何创建InputChannel,以及如何注册到IMS。这里就不多赘述,请阅读 IMS与事件分发(上) 。

这个对象很简单,他继承于InputEventReceiver。InputEventReceiver对象就是专门监听IMS输入事件的基类。每当IMS发送信号来了就会调用子类的onInputEvent方法,onBatchedInputEventPending。

我们先来看看InputEventReceiver的初始化。

核心实际上就是调用native方法在native层初始化了IMS事件监听器。

文件:/ frameworks / base / core / jni / android_view_InputEventReceiver.cpp

这里只是简单的生成一个NativeInputEventReceiver对象,并调用了NativeInputEventReceiver的initialize方法。为全局的clazz对象新增一个强引用计数。

从NativeInputEventReceiver的申明能看到实际上他是实现了LooperCallback。LooperCallback这个对象,可以阅读 Handler与相关系统调用的剖析(上) ,里面有讲解到LooperCallback实际上就是native层Looper回调后的监听对象,回调的方法就是虚函数handleEvent。

在NativeInputEventReceiver有一个十分重要的对象InputConsumer。当IMS回调了输入事件后,NativeInputEventReceiver使用InputConsumer在native层中进行处理。

构造函数没什么好看的,直接看看initialize初始化的方法。

能看到这里面实际上很简单,就是获取InputConsumer中的InputChannel中的fd,这里fd就是上面初始化好的接收端的InputChannel。因此就是获取主线程的Looper并使用Looper监听客户端的InputChannel。

一旦IMS有信号发送过来则立即回调LooperCallback中的handleEvent。

当输入信号从native层传送过来了,则会开始回调handleEvent方法。关于IMS如果读取输入事件,处理后传输过来,可以阅读我写的 IMS与事件分发(上) 。

大致上可以分为两种情况,分别对象Looper注册的事件类型ALOOPER_EVENT_INPUT和ALOOPER_EVENT_OUTPUT。

很多地方没解析清楚:

在NativeInputEventReceiver中,ALOOPER_EVENT_INPUT代表从驱动读取到的输入事件传递过来;ALOOPER_EVENT_OUTPUT代表此时需要关闭输入事件的监听,而传递过去的后返回的事件处理。

我们先来看看ALOOPER_EVENT_INPUT对应的事件处理。

核心处理方法是consumeEvents。

文件:/ frameworks / native / libs / input / InputTransport.cpp

先从InputChannel的recv系统调用获取socket里面的InputMessage数据。

虽然此时consumeBatches为false,但是result正常情况下不会是WOULD_BLOCK,会先执行consumeBatch批量处理触点事件。

在这个方法中分为两个类型处理:

能看到实际上Batch就是一个InputMessage的集合。每当检测到AMOTION_EVENT_ACTION_MOVE或者AMOTION_EVENT_ACTION_HOVER_MOVE的触点类型,则会添加到mBatches集合中,等待下一次的更新。

当下一次触点触发了回调,在这个outEvent链表不为空的循环前提下,canAddSample判断到当前PointerCount和之前的一致,会把InputMessage不断的添加到Batch的samples集合中。如果出现了不一致则需要consumeSamples进行更新Batch中记录的InputMessage。

这样就能跟踪到了这一批次的触点的轨迹,以及新增的触点。

如果只有单个触点则生成MotionEvent对象赋值给指针返回。

我们来看看InputEventReceiver是通过InputConsumer消费后是怎么触发接下来的逻辑。我们只看单点触发的逻辑。

实际上对应的是:

而onInputEvent这个方法实际上就是对应WindowInputEventReceiver。

可以看到最后回调到了enqueueInputEvent方法中。

能看到整个很久爱都难,就是生成一个obtainQueuedInputEvent对象,添加到mPendingInputEventTail链表的末端,调用scheduleProcessInputEvents方法分发。如果是需要立即响应则调用doProcessInputEvents方法。

能看到此时发送了一个MSG_PROCESS_INPUT_EVENTS一个Asynchronous异步消息。其实就是一个能在同步屏障内优先执行的消息。

核心还是调用了doProcessInputEvents。

Choreographer.mFrameInfo 更新了分发时间后,整个过程最核心的逻辑就是循环遍历mPendingInputEventHead调用deliverInputEvent进行事件的分发QueuedInputEvent。

逻辑分为如下几个步骤:

能看到这里面构建很多InputStage对象。这些对象都是通过责任链设计全部嵌套到一起。

我们简单的看看它的UML图,来区分他们的直接的关系:

先来看看InputStage的deliver

deliver的入口会判断当前QueuedInputEvent的状态。

我们来看看对整个链路从NativePreImeInputStage开始逆推回去,关键还是看apply中的方法。

在所有的InputStage中分为两类,一类是直接继承InputStage,一类是继承AsyncInputStage,我们优先看看AsyncInputStage。

在AsyncInputStage存储了一个QueuedInputEvent链表。当判断到事件打开了FLAG_FINISHED,其在核心方法forward做了如下的事情:

能看到这个过程中很简单,如果QueuedInputEvent持有了InputEventReceiver对象则会InputEventReceiver.finishInputEvent进行native方法的调用,告诉native层销毁了当前的事件。

能看到很简单就是调用InputConsumer的sendFinishedSignal方法发送该输入事件的序列号处理对应在InputDispatcher中事件。

当InputStage需要开始分发事件,就会调用apply方法,而apply中就会调用onProcess方法。每一个子类InputStage的onProcess其实就是意味着这个InputStage做了什么事情。

接下来我们就按照责任链的嵌套顺序来看看InputStage,每一个输入阶段都做了什么。

NativePreImeInputStage实际上就是就是处理InputQueue。

ViewPreImeInputStage 这个InputStage是预处理KeyEvent,把键盘等事件通过DecorView的dispatchKeyEventPreIme进行预处理分发。

ImeInputStage专门处理软键盘

关于java事件分发和java消息发送的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

The End

发布于:2022-12-26,除非注明,否则均为首码项目网原创文章,转载请注明出处。