响应式编程(Reactive Programming)介绍
什么是响应式编程?
在互联网上有着一大堆糟糕的解释与定义。Wikipedia 一如既往的空泛与理论化。Stackoverflow 的权威答案明显不适合初学者。Reactive Manifesto 看起来是你展示给你公司的项目经理或者老板们看的东西。微软的 Rx terminology"Rx = Observables + LINQ + Schedulers" 过于重量级且微软味十足,只会让大部分人困惑。相对于你所使用的 MV* 框架以及钟爱的编程语言,“Reactive” 和 “Propagation of change” 这些术语并没有传达任何有意义的概念。框架的 Views 层当然要对 Models 层作出反应,改变当然会传播。如果没有这些,就没有东西会被渲染了。
所以不要再扯这些废话了。
响应式编程是使用异步数据流进行编程
一方面,这并不是什么新东西。Event buses 或者 Click events 本质上就是异步事件流,你可以监听并处理这些事件。响应式编程的思路大概如下:你可以用包括 Click 和 Hover 事件在内的任何东西创建 Data stream。Stream 廉价且常见,任何东西都可以是一个 Stream:变量、用户输入、属性、Cache、数据结构等等。举个例子,想像一下你的 Twitter feed 就像是 Click events 那样的 Data stream,你可以监听它并相应的作出响应。
在这个基础上,你还有令人惊艳的函数去组合、创建、过滤这些 Streams,这就是函数式魔法的用武之地。Stream 能接受一个,甚至多个 Stream 为输入,你可以融合两个 Stream,也可以从一个 Stream 中过滤出你感兴趣的 Events 以生成一个新的 Stream,还可以把一个 Stream 中的数据值 映射到一个新的 Stream 中。
既然 Stream 在响应式编程中如此重要,那么我们就应该好好的了解它们,就从我们熟悉的"Clicks on a button" Event stream 开始。
Stream 就是一个按时间排序的 Events 序列,它可以放射三种不同的 Events:(某种类型的)Value、Error 或者一个" Completed" Signal。考虑一下"Completed"发生的时机,例如,当包含这个按钮的窗口或者视图被关闭时。
通过分别为 Value、Error、“Completed"定义事件处理函数,我们将会异步地捕获这些 Events。有时可以忽略 Error 与"Completed”,你只需要定义 Value 的事件处理函数就行。监听一个 Stream 也被称作是订阅 ,而我们所定义的函数就是观察者,Stream则是被观察者,其实就是 Observer Design Pattern。
上面的示意图也可以使用ASCII重画为下图,在下面的部分教程中我们会使用这幅图:
--a---b-c---d---X---|->a, b, c, d are emitted values
X is an error
| is the 'completed' signal
---> is the timeline
既然已经开始对响应式编程感到熟悉,为了不让你觉得无聊,我们可以尝试做一些新东西:我们将会把一个 Click event stream 转为新的 Click event stream。
首先,让我们做一个能记录一个按钮点击了多少次的计数器 Stream。在常见的响应式编程库中,每个Stream都会有多个方法,如 map, filter, scan, 等等。当你调用其中一个方法时,例如 clickStream.map(f),它就会基于原来的 Click stream 返回一个新的 Stream 。它不会对原来的 Click steam 作任何修改。这个特性称为不可变性,它对于响应式编程 Stream,就如果汁对于薄煎饼。我们也可以对方法进行链式调用,如 clickStream.map(f).scan(g):
clickStream: ---c----c--c----c------c-->vvvvv map(c becomes 1) vvvv---1----1--1----1------1-->vvvvvvvvv scan(+) vvvvvvvvv
counterStream: ---1----2--3----4------5-->
map(f) 会根据你提供的 f 函数把原 Stream 中的 Value 分别映射到新的 Stream 中。在我们的例子中,我们把每一次 Click 都映射为数字 1。scan(g) 会根据你提供的 g 函数把 Stream 中的所有 Value 聚合成一个 Value x = g(accumulated, current) ,这个示例中 g 只是一个简单的添加函数。然后,每 Click 一次, counterStream 就会把点击的总次数发给它的观察者。
为了展示响应式编程真正的实力,让我们假设你想得到一个包含“双击”事件的 Stream。为了让它更加有趣,假设我们想要的这个 Stream 要同时考虑三击(Triple clicks),或者更加宽泛,连击(两次或更多)。深呼吸一下,然后想像一下在传统的命令式且带状态的方式中你会怎么实现。我敢打赌代码会像一堆乱麻,并且会使用一些变量保存状态,同时也有一些计算时间间隔的代码。
而在响应式编程中,这个功能的实现就非常简单。事实上,这逻辑只有 4 行代码。但现在我们先不管那些代码。用图表的方式思考是理解怎样构建Stream的最好方法,无论你是初学者还是专家。
灰色的方框是用来转换 Stream 函数的。首先,简而言之,我们把连续 250 ms 内的 Click 都积累到一个列表中(就是 buffer(stream.throttle(250ms) 做的事。不要在意这些细节,我们只是展示一下响应式编程而已)。结果是一个列表的 Stream ,然后我们使用 map() 把每个列表映射为一个整数,即它的长度。最终,我们使用 filter(x >= 2) 把整数 1 给过滤掉。就这样,3 个操作就生成了我们想要的 Stream。然后我们就可以订阅(“监听”)这个 Stream,并以我们所希望的方式作出反应。
我希望你能感受到这个示例的优美之处。这个示例只是冰山一角:你可以把同样的操作应用到不同种类的 Stream 上,例如,一个 API 响应的 Stream;另一方面,还有很多其它可用的函数。
为什么我要使用响应式编程(RP)?
响应式编程提高了代码的抽象层级,所以你可以只关注定义了业务逻辑的那些相互依赖的事件,而非纠缠于大量的实现细节。RP 的代码往往会更加简明。
特别是在开发现在这些有着大量与数据事件相关的 UI events 的高互动性 Webapps、手机 apps 的时候,RP 的优势就更加明显。10年前,网页的交互就只是提交一个很长的表单到后端,而在前端只产生简单的渲染。Apps 就表现得更加的实时了:修改一个表单域就能自动地把修改后的值保存到后端,为一些内容"点赞"时,会实时的反应到其它在线用户那里等等。
现在的 Apps 有着大量各种各样的实时 Events,以给用户提供一个交互性较高的体验。我们需要工具去应对这个变化,而响应式编程就是一个答案。
响应式编程特点
传统的编程方式,是顺序执行的:上一个任务没有完成,需要等待,直至完成之后,才会执行下一个任务。无论是提升机器的性能还是代码的性能,本质上都需要依赖上一个任务的完成。如果需要响应迅速,就得把同步执行的方式换成异步,方法执行变成消息发送。这变成了异步编程的方式,它是响应式编程的重要特性之一。
- 异步编程:提供了合适的异步编程模型,能够挖掘多核 CPU 的能力、提高效率、降低延迟和阻塞等。
- 数据流:基于数据流模型,响应式编程提供一套统一的 Stream 风格的数据处理接口。和 Java 8 中的 Stream 相比,响应式编程除了支持静态数据流,还支持动态数据流,并且允许复用和同时接入多个订阅者。
- 变化传播:简单来说就是以一个「数据流」为输入,经过一连串操作转化为另一个数据流,然后分发给各个订阅者的过程。这就有点像函数式编程中的组合函数(compose),将多个函数串联起来,把一组输入数据转化为格式迥异的输出数据。
响应式编程优点
响应式编程在执行过程中是异「步非阻塞」,可以充分利用多核 CPU 的性能,并且可以由底层的执行模型,负责通过数据流来自动传播变化,可以做到更实时的响应;使用响应式编程风格开发的程序,能支持更大的 吞吐量(备注:支持更大的吞吐量,不代表程序跑的更快;因为响应式编程只是让执行的主线程不会因为某些耗时操作而阻塞,导致其阻塞后面的请求,但实际执行耗时操作的子线程消耗的时间依然是没有变化)。
响应式编程缺点
- 响应式编程风格,与传统的命令式编程风格差异大,且响应式编程主要是异步的,相较于同步的编程方式在思维上有所变化,需要一定的学习成本;
- 因为其基于「事件」驱动,每次触发事件会使用新线程执行;如果在一个不会阻塞的程序中使用,线程切换可能比程序本身执行耗时要多;
- 对于异常的处理,因为基于回调或者声明式的原因,在使用匿名回调时,没有寻址能力,意味着在整个数据处理链中,每个处理节点的处理成功或错误的状态,不会向外界发送相应信号,其中某个节点出错异常可能会被吞掉,或者进行了相关处理,但得到的异常信息少,不好定位。
具体实现
ReactiveX ,是响应式编程原则的一种实现,通过使用可观察序列,组成异步和基于事件的程序。使用 RX,您的代码创建并订阅名为 Observables 的数据流。虽然反应式编程是关于概念的,但 RX 为您提供了一个惊人的工具箱。通过结观察者和迭代器模式以及函数式习语,RX 为您提供了超能力。您拥有一系列功能来组合、合并、过滤、转换和创建数据流。
值得一提的是,ReactiveX 可以说无处不在,对前端、跨平台、后端都进行了适配,并提供相对应用的工具。在 Web 上可以使用 RxJS (A reactive programming library for JavaScript. 已有 27.5K Star),在移动设备上使用 Rx.NET 和 RxJava 操作 UI 事件和 API 响应。在 Java 平台,支持响应式编程的流行库有 Reactor , RxJava (Reactive Extensions for the JVM), Vert.x 等,而在Java9中,实现了响应式流规范(Reactive Streams)所定义的相关接口,让Java本身支持了响应式编程,不需要通过其他三方库实现。
此外,前端领域还有很多其他更进一步的实现,诸如 Cycle.js :用于可预测代码的功能性和反应式 JavaScript 框架。
RxJS:JS 的反应式编程库
RxJS JavaScript 的反应式(响应式)编程库,它通过使用可观察(Observable)序列,来编写异步和基于事件的程序。它提供了一个核心类型 Observable,附属类型 (Observer、 Schedulers、 Subjects) 和受 Array 启发的操作符 (map、filter、reduce、every 等等),这些数组操作符可以把异步事件作为集合来处理。🉑️将 RxJS 视为事件的 Lodash。
基础知识
响应式代码的基本组成部分是Observables和Subscribers(事实上Observer才是最小的构建块,但实践中使用最多的是Subscriber,因为Subscriber才是和Observables的对应的。)。Observable发送消息,而Subscriber则用于消费消息。
消息的发送是有固定模式的。Observable可以发送任意数量的消息(包括空消息),当消息被成功处理或者出错时,流程结束。Observable会调用它的每个Subscriber的Subscriber.onNext()函数,并最终以Subscriber.onComplete()或者Subscriber.onError()结束。
这看起来像标准的观察者模式, 但不同的一个关键点是:Observables一般只有等到有Subscriber订阅它,才会开始发送消息(术语上讲就是热启动Observable和冷启动Observable。热启动Observable任何时候都会发送消息,即使没有任何观察者监听它。冷启动Observable只有在至少有一个订阅者的时候才会发送消息(我的例子中都是只有一个订阅者)。这个区别对于开始学习RxJava来说并不重要。)。换句话说,如果没有订阅者观察它,那么将不会起什么作用。
Hello, World!
让我们以一个具体例子来实际看看这个框架。首先,我们创建一个基本的Observable:
Observable<String> myObservable = Observable.create(new Observable.OnSubscribe<String>() {@Overridepublic void call(Subscriber<? super String> sub) {sub.onNext("Hello, world!");sub.onCompleted();}}
);
我们的Observable发送“Hello,world!”消息然后完成。现在让我们创建Subscriber来消费这个数据:
Subscriber<String> mySubscriber = new Subscriber<String>() {@Overridepublic void onNext(String s) { System.out.println(s); }@Overridepublic void onCompleted() { }@Overridepublic void onError(Throwable e) { }
};
上面代码所做的工作就是打印由Observable发送的字符串。现在我们有了myObservable和mySubscriber,就可以通过subscribe()函数把两者关联起来:
myObservable.subscribe(mySubscriber);
// Outputs "Hello, world!"
当订阅完成,myObservable将调用subscriber的onNext()和onComplete()函数,最终mySubscriber打印“Hello, world!”然后终止。
RxJava2 的核心原理
1、简单的链式调用(无线程切换)
先来看一段示例代码:
Observable.create(object : ObservableOnSubscribe<String> {override fun subscribe(emitter: ObservableEmitter<String>) {Log.d("solart", "subscribe > ${Thread.currentThread().name}")emitter.onNext("test")emitter.onComplete()}}).flatMap(object : Function<String, Observable<String>> {override fun apply(t: String): Observable<String> {return Observable.just(t)}}).map(object : Function<String, Int> {override fun apply(t: String): Int {return 0}}).subscribe(object : Observer<Int> {override fun onSubscribe(d: Disposable) {Log.d("solart", "onSubscribe > ${Thread.currentThread().name}")}override fun onNext(t: Int) {Log.d("solart", "onNext > ${Thread.currentThread().name}")}override fun onComplete() {Log.d("solart", "onComplete > ${Thread.currentThread().name}")}override fun onError(e: Throwable) {Log.d("solart", "onError > ${Thread.currentThread().name}")}})
这段代码中我们简单用了 create、flatMap、map等操作符,进行了流式的数据转换,最后我们通过 subscribe 订阅了数据流,其实通过查看源码我们不难发现, RxJava 本身是个逆向订阅的过程,话不多说先看图
2、异步事件流编程(线程切换)
相信有了上面的分析,大家对 RxJava 的逆向订阅以及数据流转有了一定的认识,但是 RxJava 的强大之处在于它的异步事件流编程方式,随心所欲的切换工作线程,下面我们来分析它是如何做到的。
同样的我们还是先给出一个简单的示例:
Observable.create(object : ObservableOnSubscribe<String> {override fun subscribe(emitter: ObservableEmitter<String>) {Log.d("solart", "subscribe > ${Thread.currentThread().name}")emitter.onNext("test")emitter.onComplete()}}).subscribeOn(Schedulers.io()).map(object : Function<String, Int> {override fun apply(t: String): Int {return 0}}).observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer<Int> {override fun onSubscribe(d: Disposable) {Log.d("solart", "onSubscribe > ${Thread.currentThread().name}")}override fun onNext(t: Int) {Log.d("solart", "onNext > ${Thread.currentThread().name}")}override fun onComplete() {Log.d("solart", "onComplete > ${Thread.currentThread().name}")}override fun onError(e: Throwable) {Log.d("solart", "onError > ${Thread.currentThread().name}")}})
这里简化了操作符的调用,以切换线程为示例,根据这段代码,我画出了这个过程的流程图(灵魂画手有没有?)如下:
图中不同颜色(红、绿、紫)的实线表示流程所属不同线程,体现在不同线程中的过程,且标上了对应的序号,方便大家观看,这个图已经能够揭示 RxJava 运转的核心原理。
相关文章:
响应式编程(Reactive Programming)介绍
什么是响应式编程? 在互联网上有着一大堆糟糕的解释与定义。Wikipedia 一如既往的空泛与理论化。Stackoverflow 的权威答案明显不适合初学者。Reactive Manifesto 看起来是你展示给你公司的项目经理或者老板们看的东西。微软的 Rx terminology"Rx Observables LINQ S…...
你不知道的美化列表的两种方案-<ul/><ol/>
大家好,我是半夏👴,一个励志更文1000篇沙雕程序员.如果喜欢我的文章,可以关注➕ 点赞 一起学习交流前端,成为更优秀的工程师~ CSS为什么这么难学?一定是你方法不对!!! 只要一杯奶茶,CSS任你学。学透CSS,拒绝切图仔!!! 学透CSS传送门 文章目录 学透CSS传送门前言li…...
2023年浙江理工大学MBA招生考试初试成绩查询及复查的通知
根据往年的情况,2023浙江理工大学MBA考试初试成绩可能将于2月21日下午两点公布,为了广大考生可以及时查询到自己的分数,杭州达立易考教育为大家汇总了信息。 一、成绩查询考生可登录中国研究生招生信息网“全国硕士研究生招生考试初试成绩查询…...
SVNH数据(.mat格式)转为图像(.png)matlab代码
一、获取SVNH数据数据集集地址-http://ufldl.stanford.edu/housenumbers/提供两种格式的数据:1.Format 1,图像形式,压缩包2.Format 2, .mat格式的数据10 classes, 1 for each digit. Digit 1 has label 1, 9 has label 9 and 0 ha…...
【总结】vim教程与详细命令总结,该来的躲不掉啊晕
B站|公众号:啥都会一点的研究生 目录写在前面vim的工作模式普通模式编辑模式命令模式命令大全,最详细(建议收藏)光标的移动插入模式 - 插入/追加文本编辑文本选择文本(可视化模式)可视化模式命令剪切, 复制…...
git基础使用
Git安装 去安装>> 正式开始 进入要管理的目录,执行命令 git init 查看管理目录下的状态 git status 注:新增文件和修改过后的文件都是红色 管理指定文件(红变绿) 指定文件:git add 文件名 当前目录下所有&…...
基于 RANSAC 的地面分割与聚类算法
文章目录 前言 一、算法原理 参考文献 二、代码实现 1.头文件 2.源文件...
JVM内存模型深度剖析与优化
1. Java语言的跨平台特性 2. JVM整体结构及内存模型 堆存放着对象信息每个线程都会分配一块属于自己的内存空间(栈空间) 每个方法都会分配一块内存空间(栈桢),上图 compute()方法 和 main()方法 都会分配到各自的栈桢空…...
软件性能测试定义中文
From Wiki软件性能测试在软件质量保证中,性能测试通常是一种测试实践,用于确定系统在特定工作负载下的响应能力和稳定性方面的表现。它还可以用于调查、测量、验证或验证系统的其他质量 属性,例如可扩展性、可靠性和资源使用。性能测试是性能…...
2023情人节正经性生活调研报告
省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2023年1月份热门报告合集ChatGPT的发展历程、原理、技术架构及未来方向2023年,如何科学制定年度规划?《底层逻辑》高清配图今天给大家带来丁香医生最新…...
22- 隐马尔科夫HMM (NLP自然语言算法) (算法)
HMM模型 : from hmmlearn.hmm import GaussianHMM model GaussianHMM(n_components3,n_iter100000, covariance_type diag) model.fit(X) 1、马尔科夫链 有向图模型(贝叶斯网络):用有向图表示变量间的依赖关系; 无向图模型&…...
gRPC是什么,怎么用
RPC是什么 RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传…...
linux基本功系列之fdisk命令实战
文章目录前言一. fdisk命令介绍二. 语法格式及常用选项三. 参考案例3.1 列出每个分区的大小3.2 分区操作3.2.1 添加硬盘3.2.2 开启虚拟机并分区3.3.3 分区完成后进行格式化挂载四 . 设置分区自动挂载前言 大家好,又见面了,我是沐风晓月,本文…...
Mysql UDF提权复现Raven2
Raven2通关过程 主要通过Raven2靶机进行复现Mysql UDF提权,以下为通关过程。 靶机镜像:https://www.vulnhub.com/entry/raven-2,269/ 信息收集 拿到靶机ip:192.168.112.129 nmap -sP 192.168.112.0/24探测开放端口,nmap用烂了…...
枚举类(enum)
定义:在某些情况下,一个类的实例对象是有限且固定的,可将该类称为“枚举类”。枚举类是JDK 1.5 之后提出来的。例如:四季只有春夏秋冬4个季节,性别只有男女2个,故四季类和性别类均可称为“枚举类”。 在自…...
腾讯云架构师亲码“redis深度笔记”,从入门到精通,面面俱到
前言 作为这个时代码代码的秃头人员,对Redis肯定是不陌生的,如果连Redis都没用过,还真不好意思出去面试,指不定被面试官吊打多少次。 毕竟现在互联网公司和一些创业公司都要用到Redis,像亚马逊、谷歌、阿里、腾讯都要…...
萌新应该如何开始学习走向自动化测试高薪岗位?
对于测试人员来说,不管进行功能测试还是自动化测试,还是性能测试,都是需要编写测试用例,所以我们必须先要了解清楚手工测试用例与自动化测试用例的一些特点,才能更好的开展自动化测试工作。1.1手工测试用例和自动化测试…...
-bash: pip: command not found
背景 这个错误的原因就是,我们的服务器上没有安装pip,装上就可以了,下面我们看一下centos中的解决方案 下载 wget https://bootstrap.pypa.io/get-pip.py 下载完成后如下图: 安装 安装的时候首先需要看一下自己的python是什…...
使用HTTP隧道代理,请求超过频率要怎么办?
在网上,经常会看到有人说使用隧道代理经常遇到429错误(请求超过频率),我们要如何解决这一问题呢?通常情况,优质的HTTP代理厂商隧道代理服务器采用的是高性能主机构建的动态IP代理服务器,是可以支…...
paddle 49 ODConv的可部署调整
ODConv是一种适用于轻量化模型的conv结构,可以在较少的参数下训练出多参数模型才能达到的精度,在相同的flop下可以稳定的涨2-3%个点。但是在paddle下部署ODConv动态卷积模型时会报出各种异常,导致模型无法转静态图或onnx格式(可能在pytorch下也是无法转换的)。为此研究ODC…...
C++ STL 学习之【string】
✨个人主页: Yohifo 🎉所属专栏: C修行之路 🎊每篇一句: 图片来源 The key is to keep company only with people who uplift you, whose presence calls forth your best. 关键是只与那些提升你的人在一起,…...
使用开源 MaxKey 与 APISIX 网关保护你的 API
1. Apache APISIX介绍 Apache APISIX 是 Apache 软件基金会下的云原生 API 网关,它兼具动态、实时、高性能等特点,提供了负载均衡、动态上游、灰度发布(金丝雀发布)、服务熔断、身份认证、可观测性等丰富的流量管理功能。我们可以…...
Linux之Xshell工具使用
shell简介Xshell是一个远程工具,可以远程连接linux系统 ,SSH,远程管理 Xshell来远程访问Linux系统的终端 。shell的英文含义是“壳”;它是相对于内核来说的,因为它是建立在内核的基础上,面向于用户的一种表…...
【数据结构与算法】时间复杂度与空间复杂度
目录 一.前言 二.时间复杂度 1.概念 二.大O的渐进表示法 概念: 总结: 三.常见时间复杂度计算举例 例1 例2 例3 例4 例5.计算冒泡排序的时间复杂度 例6.二分算法的时间复杂度 例7.阶乘递归Fac的时间复杂度 例8.斐波那契递归的时间复杂度 …...
Nginx如何配置Http、Https、WS、WSS的方法步骤
这篇文章主要介绍了Nginx如何配置Http、Https、WS、WSS的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 写在前面 当今互联网领域,Nginx是使…...
【博客621】iptables -J动作总结
iptables -J动作总结 1、iptables常见动作 ACCEPTDROPREJECTLOGSNATDNATMASQUERADEREDIRECT 2、iptables常见动作用法 2-1、ACCEPT: 作用:用于接收匹配的流量,使得流量继续往后面的规则和链路去匹配 2-2、DROP 作用:用于丢弃匹…...
Chrome开发者工具:利用网络面板做性能分析
Chrome 开发者工具(简称 DevTools)是一组网页制作和调试的工具,内嵌于 Google Chrome 浏览器中。 Chrome 开发者工具有很多重要的面板,比如与性能相关的有网络面板、Performance 面板、内存面板等,与调试页面相关的有…...
SpringCloud系列(十三)[分布式搜索引擎篇] - ElasticSearch 的概念及 Centos 7 下详细安装步骤
打开淘宝, 搜索 狂飙 会出现各种价格有关狂飙的书籍, 当然也有高启强同款的孙子兵法!!! 如下图所示: 那么面对海量的数据, 如何快速且准确的找到我们想要的内容呢? 淘宝界面已经可以按照综合排序 / 销量 / 信用 / 价格等进行筛选, 是如何做到的呢? ElasticSearch 11 Elastic…...
04_Docker 镜像和仓库
04_Docker 镜像和仓库 文章目录04_Docker 镜像和仓库4.1 什么是 Docker 镜像4.2 列出 Docker 镜像4.3 拉取镜像4.4 查找镜像4.5 构建镜像4.5.1 创建 Docker Hub 账号4.5.2 用 Docker 的 commit 命令创建镜像4.5.3 用 Dockerfile 构建镜像4.5.5 基于 Dockerfile 构建新镜像4.5.5…...
postman-enterprise-API
Postman 是一个用于构建和使用 API 的 API 平台。Postman 简化了 API 生命周期的每个步骤并简化了协作,因此您可以更快地创建更好的 API。 API存储库 在一个中央平台上围绕您的所有 API 工件轻松存储、编目和协作。Postman 可以存储和管理 API 规范、文档、工作流配…...
手机做网站服务器吗/短视频获客系统
在子线程中new一个Handler为什么会报以下错误? java.lang.RuntimeException: Cant create handler inside thread that has not called Looper.prepare() 这是因为Handler对象与其调用者在同一线程中,如果在Handler中设置了延时操作,则调用…...
版本设计网站/北京全网营销推广
数据倾斜是怎么发生的? 数据倾斜:shuffle过程数据分布不均匀。 shuffle:洗牌。 分步式计算,相同key的数据会到一个节点去处理。 举个例子:某宝想统计各个商家的一年销售额,有的商家卖了很多产品࿰…...
武汉网站推广公司招聘/公司网站设计的内容有哪些
前言 上节讲到qt for android开发百度地图,已经可以打开地图了,里面的一些功能,如自定义搜索栏,添加控件等等,这些百度地图官方开发文档都提供了例子,可以自定义开发。但是问题也来了,我们开发百…...
wordpress 当前分类id/2023b站推广大全
众所周知,Android 11 正式版已经正式上线,各大厂商也都纷纷加快了自家系统的适配进度。近日,盖乐世社区也官宣三星 S20 系列基于 Android 11 的 One UI 3 开启内测。不得不说,一直以来三星对于安卓大版本的适配还是非常迅速的。不…...
企业网站seo策略/baidu com百度一下
一般我们用短草丛被角色经过或平常的飘动效果都是用3d实现的,3d实现起来就比较简单,就是对模型上的顶点做与角色距离的相反方向偏移就好了,平常的飘动效果一般是用sin做弧度变化或者甚至就直接uv的来回移动就好。 我们是3渲2的画面俯四十五度…...
单页型网站/百度一下百度搜索入口
文档介绍:中央广播电视大学2002—2003学年度第一学期“开放专科”期末考试计算机专业微机接口技术试题中央广播电视大学2002—2003学年度第一学期“开放专科”期末考试计算机(控)专业微机接口技术试题2003年1月一、单项选择题(本题共20分,每小题2分)1.查询输入方式的…...