Android中的RxJava入门及常用操作符
文章目录
- 1.定义
- 2.作用
- 3.特点
- 4.使用
- 4.1创建被观察者(Observable)
- 4.2创建观察者(Observer)
- 4.3订阅(Subscribe)
- 4.4Dispose
- 5.操作符
- 5.1操作符类型
- 5.2just操作符
- 5.2链式调用
- 5.3 fromArray操作符
- 5.4 fromIterable操作符
- 5.5map操作符
- 5.6flatMap操作符
- 5.7concatMap操作符
- 5.8buffer操作符
- 5.9concat操作符
- 6.异步
- 7.subscribeOn
- 8.observeOn
- 9.背压
- 9.1Flowable
- 9.2背压策略
- 9.3另一种调用背压策略的方式
- 10.RxBus
- 11.RxBinding
- 12.内存泄露
1.定义
RxJava在GitHub的介绍
RxJava:a library for composing asynchronous and event-based programs using observable sequences for the Java VM
// 翻译:RxJava 是一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库
也就是说:RxJava是一个基于事件流,实现异步操作的库
2.作用
类似于Android中的AsyncTask,Handler作用用于实现异步操作
3.特点
由于RxJava的使用方式是:基于事件流的链式调用,所以使得RxJava:
- 逻辑简单
- 实现优雅
- 使用简单
RxJava原理:基于一种扩展的观察者模式
RxJava的扩展观察者模式中有4个角色:
角色 | 作用 |
---|---|
被观察者(Observable) | 产生事件 |
观察者(Observer) | 接收事件,并给出响应动作 |
订阅(Subscribe) | 连接被观察者&观察者 |
事件(Event) | 被观察者&观察者沟通的载体 |
可以总结为:被观察者(Observable)通过订阅(Subscribe)按顺序发送事件给观察者(Observer),观察者(Observer)按顺序接收事件&作出对应的响应动作
4.使用
添加依赖:
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
4.1创建被观察者(Observable)
val ohThisIsObservable = Observable.create<String>{it.onNext("Hello") //发送"事件"it.onNext("rx")it.onNext("world")it.onComplete() //发送完成"事件"
}
这里采用了create()创建被观察者,但并非只有create()能创建,其余操作符也可以达成此效果(后面介绍)。
4.2创建观察者(Observer)
val observer: Observer<String> = object : Observer<String> {override fun onSubscribe(d: Disposable) { System.out.println(" onSubscribe ") }override fun onNext(string: String) { System.out.println(" onNext : "+string) }override fun onError(e: Throwable) { System.out.println(e) }override fun onComplete() { System.out.println(" on Complete ") }
}
可看见这里响应事件分别有以下:
onSubscribe():准备监听,最先调用的方法;
onNext():用来发送数据,调用一次发送一条;
onError():发送异常通知,只发送一次,多次调用也只会发送第一条;
onComplete():发送完成通知,只发送一次,多次调用也只会发送第一条。
PS:onError()和onComplete()互斥,俩方法同时只能调用一个,要么发生异常onError()不会回调onComplete(),要么正常回调onComplete(),不回调onError()。
4.3订阅(Subscribe)
ohThisIsObservable.subscribe(observer)
运行代码,会发现如下结果
日志中可发现,当被观察者(ohThisIsObservable)通过调用onNext()发射数据的时候,观察者(observer)调用onNext()接收数据;当被观察者(ohThisIsObservable)调用onComplete()时,观察者(observer)调用onComplete(),其他事件将不会继续发送(onError同此理)。
RxJava中,观察者不仅仅只有observer才能实现,下面是个简单版示例:
val consumer: Consumer<String> =Consumer { s ->//创建观察者consumerprintln(s)}
val stringObservable = Observable.create { emitter ->emitter.onNext("Hello")emitter.onNext("~~~rx~~~")emitter.onNext("world")emitter.onComplete()
}
//被观察者发出一连串字符并指定consumer订阅被观察者
stringObservable.subscribe(consumer)
对应输出结果如图:
由以上代码可见,Observer相对于Consumer在接口方法上要多onSubscribe、onNext、onError、onComplete这些接口,在一次事件中,可操作程度更精细。
4.4Dispose
在onSubscribe()中会接收到一个Disposable对象,该对象相当于一个开关,如果开关关闭,则观察者不会收到任何事件和数据。例如:
val observer: Observer<String> = object : Observer<String> {var mDisposeable: Disposable? = nulloverride fun onSubscribe(d: Disposable) {println(" onSubscribe ")mDisposeable = d}override fun onNext(s: String) {println(" onNext : $s")if (s == "stop") {mDisposeable!!.dispose()}}override fun onError(e: Throwable) {println(" onError ")}override fun onComplete() {println(" onComplete ")}
}
Observable.just("Hello", "world", "stop", "coding").subscribe(observer)
在上述代码中我们使用一个变量来保存Disposable对象,在onNext方法中如果传过来的字符串是“stop”,则调用dispose关闭事件的接收,后续字符串不在发射,甚至onComplete()也不会执行了。结果如下图:
5.操作符
Rxjava提供大量操作符来完成对数据处理,这些操作符也可以理解成函数。如果把Rxjava比喻成一道数据流水线,那么一个操作符就是一道工序,数据通过这些工序加工变换、组装,最后生产出我们想要的数据。
5.1操作符类型
创建型
转换型
组合型
功能型
过滤型
条件型
5.2just操作符
用于创建一个被观察者,并发送事件,发送的事件不可以超过10个以上(从其构造函数就可以看出,如下图):
简单写个示例:
val justObservable = Observable.just("Hello", "rx", "world~!")
val observer: Observer<String> = object : Observer<String> {override fun onSubscribe(d: Disposable) { System.out.println(" onSubscribe ") }override fun onNext(string: String) { System.out.println(" onNext : "+string) }override fun onError(e: Throwable) { System.out.println(e) }override fun onComplete() { System.out.println(" on Complete ") }
}
justObservable.subscribe(observer)
对应输出结果为:
5.2链式调用
RxJava最方便的一个特征就是链式调用,上述代码可以修改为:
Observable.just("Hello", "rx", "world").subscribe(object : Observer<String> {override fun onSubscribe(d: Disposable) { System.out.println(" onSubscribe ") }override fun onNext(string: String) { System.out.println(" onNext : "+string) }override fun onError(e: Throwable) { System.out.println(e) }override fun onComplete() { System.out.println(" on Complete ") }
})
效果一样(Java代码在这里的表现形式则是lamba表达式),但跟之前看起来给人感觉完全不一样,如无特殊说明,后续例子都会如此调用。
5.3 fromArray操作符
类似于just,但是可以传入无限个参数,无数量限制
5.4 fromIterable操作符
可直接传一个List给观察者发射(List extends Collection接口,而Collection extends Iterable接口,所以可以直接传进去)。例如:
val arrayList = ArrayList<String>()
arrayList.add("111")
arrayList.add("222")
Observable.fromIterable(arrayList).subscribe(object : Observer<String> {override fun onSubscribe(d: Disposable) { System.out.println(" onSubscribe ") }override fun onNext(string: String) { System.out.println(" onNext : "+string) }override fun onError(e: Throwable) { System.out.println(e) }override fun onComplete() { System.out.println(" on Complete ") }
})
对应结果:
5.5map操作符
map操作符能直接对发射出来的事件进行处理并且产生新的事件,然后再次发射。例如下述例子:
Observable.just("Hello").map<Any> { "get it!" }.subscribe(object : Observer<Any> {override fun onSubscribe(d: Disposable) {println(" onSubscribe ")}override fun onNext(o: Any) {println(" onNext : "+o)}override fun onError(e: Throwable) {println(" onError ")}override fun onComplete() {println(" onComplete ")}})
这里我们本来传入参数是"Hello",通过map()拦截后发射出去的参数变成了"get it!",拦截修改成功。
5.6flatMap操作符
flat,英语翻译过来的意思是“使变平”的意思,跟map()一样,都能直接对发射出来的事件进行处理并且产生新的事件。但其内部方法参数不同。二者都是传参进Function()中并在apply()中进行数据修改,但二者传入参数不同。
map()是两个泛型,而flatMap()第二个参数填Observable被观察者,再将这个被观察者发射出去,这一下灵活度就增大了,这也是网络请求场景中最常用的操作符。下述简单示例:
Observable.just("注册").flatMap<Any> { s ->println(s + "成功")Observable.just("进行登陆")
}.subscribe(object : Observer<Any> {override fun onSubscribe(d: Disposable) {println(" onSubscribe ")}override fun onNext(o: Any) {println(" onNext :"+o)}override fun onError(e: Throwable) {println(" onError ")}override fun onComplete() {println(" onComplete ")}
})
对应的日志打印
5.7concatMap操作符
concatMap()与flatMap()使用方式完全一致,基本上是一样的。不过,concatMap()转发出来的数据是有序的,而flatMap()是无序的。
5.8buffer操作符
buffer()有多参数方法,这里介绍最常用的,单参数形式,buffer(x):根据个数来缓冲,每次缓冲x个数转换成数组,再发射出去,例如:
Observable.just("1","2","3","4","5","8","9","7","6","10").buffer(3).subscribe(object : Observer<Any> {override fun onSubscribe(d: Disposable) {println(" onSubscribe ")}override fun onNext(o: Any) {println(" onNext :"+o)}override fun onError(e: Throwable) {println(" onError ")}override fun onComplete() {println(" onComplete ")}
})
对应的输出结果为:
5.9concat操作符
可以将多个观察者组合在一起,然后按照之前发送顺序发送事件。需要注意的是,concat() 最多只可以发送4个事件。
示例如下:
Observable.concat(Observable.just("111"),Observable.just("222")).subscribe ( object : Observer<Any>{override fun onSubscribe(d: Disposable) {println(" onSubscribe ")}override fun onNext(t: Any) {println(" onNext : "+t)}override fun onError(e: Throwable) {println(" onError ")}override fun onComplete() {println(" onComplete ")}} )
对应输出结果为:
concatArray()和concat()作用一样,不过concatArray()可以发送多于4个被观察者。
6.异步
RxJava提供了非常方便的API来完成线程的调度,内置的线程调度器有以下几个:
- Schedule.single():单线程调度器,线程可复用;
- Schedule.newThread():为每个任务创建新的线程;
- Schedule.io():处理I/O密集任务,内部线程池实现,可根据需求增长;
- Schedulers.computation():处理计算任务,如事件循环和回调任务; Schedulers.immediate():默认指定的线程,也就是当前线程; AndroidSchedulers.mainThread():Android主线程调度器,属于RxAndroid。
线程调度器实际上是指派事件在什么样的线程中处理,所需应用场景就不难想象了,如果该事件是耗时操作,比如网络请求,但相应结果会先是在UI中,这时候在主线程执行网络请求就不合适了,但在子线程执行,结果同样要刷新UI,也不太合适,这里就凸显自由切换线程的好处了。Rxjava可通过调度器来制定被观察者和观察者分别可以在什么线程中执行自己的代码,而指定调度器的API则是:subscribeOn和observeOn。
7.subscribeOn
首先,我们不用线程调度器,我们先看观察者和被观察者默认情况下在什么线程中执行自己代码,如下:
Observable.create(object : ObservableOnSubscribe<Any>{override fun subscribe(emitter: ObservableEmitter<Any>) {println(" subscribe : "+ Thread.currentThread())emitter.onNext(" guess wich thread ")emitter.onComplete()}
}).subscribe ( object : Observer<Any>{override fun onSubscribe(d: Disposable) {println(" onSubscribe : "+ Thread.currentThread())}override fun onNext(t: Any) {println(" onNext : "+t+" : "+Thread.currentThread())}override fun onError(e: Throwable) {println(" onError : "+ Thread.currentThread())}override fun onComplete() {println(" onComplete : "+ Thread.currentThread())}} )
对应的结果为:
可见默认情况下,观察者和被观察者都是在主线程中执行。假设这个时候要执行耗时操作,Android程序必定崩溃,所以我们这时要切换线程。
subscribeOn()实际上是指定被观察者的代码在哪个线程中执行。例如:
Observable.create(object : ObservableOnSubscribe<Any>{override fun subscribe(emitter: ObservableEmitter<Any>) {println(" subscribe : "+ Thread.currentThread())emitter.onNext(" guess wich thread ")emitter.onComplete()}
}).subscribeOn(Schedulers.newThread()) //决定执行subscribe方法所处的线程,也就是产生事件或发射事件所处的线程.subscribe ( object : Observer<Any>{override fun onSubscribe(d: Disposable) {println(" onSubscribe : "+ Thread.currentThread())}override fun onNext(t: Any) {println(" onNext : "+t+" : "+Thread.currentThread())}override fun onError(e: Throwable) {println(" onError : "+ Thread.currentThread())}override fun onComplete() {println(" onComplete : "+ Thread.currentThread())}} )
这段代码中采用subscribeOn(Schedulers.newThread())来指定在新建线程中执行:
这时运行得到结果:
可见日志是不对的,onNext()、onComplete()都没有打印。原因很简单,我们在主线程创建观察者和被观察者之后,事件发送的执行转交给调度器Schedulers.newThread(),还没等来得及新线程发送出事件,主线程就直接退出了,所以后续日志看不到,鉴于此,我们使主线程休眠sleep2秒,在上述方法的后面调用如下代码:
try {Thread.sleep(2000) //这里sleep延时主线程
} catch (e: InterruptedException) {e.printStackTrace()
}
输出结果为:
8.observeOn
observeOn()指定后续的操作符以及观察者的代码在什么样的线程中执行。且observeOn()可以多次被调用,每次调用都生效。
Observable.create(object : ObservableOnSubscribe<Any> {override fun subscribe(emitter: ObservableEmitter<Any>) {Log.i("rxdemo", " subscribe : " + Thread.currentThread())emitter.onNext(" guess wich thread ")emitter.onComplete()}
}).subscribeOn(Schedulers.io()) //决定执行subscribe方法所处的线程,也就是产生事件或发射事件所处的线程.observeOn(AndroidSchedulers.mainThread()) //决定下游事件被处理时所处的线程.subscribe(object : Observer<Any> {override fun onSubscribe(d: Disposable) {Log.i("rxdemo", " onSubscribe : " + Thread.currentThread())}override fun onNext(t: Any) {Log.i("rxdemo", " onNext : " + t + " : " + Thread.currentThread())}override fun onError(e: Throwable) {Log.i("rxdemo", " onError : " + Thread.currentThread())}override fun onComplete() {Log.i("rxdemo", " onComplete : " + Thread.currentThread())}})
对应输出结果为:
9.背压
这个词是从backpressure直译过来,背压即来自背部的压力,指当被观察者发出很多的数据或事件时,观察者来不及处理,都积压在那,压的观察者喘不过气,有时候还会导致OOM。
如下述代码:
Observable.create(object : ObservableOnSubscribe<Any> {override fun subscribe(emitter: ObservableEmitter<Any>) {while (true){emitter.onNext(" subscribe : Hello ")}}
}).subscribeOn(Schedulers.io()) //被观察者在I/O线程执行.observeOn(Schedulers.newThread()) //观察者在新线程执行.subscribe { //ConsumerThread.sleep(9000);Log.i("rxdemo"," accept ~");}
观察者和被观察者在不同线程中执行,被观察者是个死循环不停发射,同时观察者处理数据的速度放缓一些,休眠9秒处理一次。这时我们可以在Profiler中可以看到:
内存随时间可见的上升,这种情况如果不处理,很大概率可能会出现OOM。究其原因是因为发送数据方和接收数据方不在一个线程内,两个线程步调不一致,发送数据太多处理不来就缓存起来,直到内存用完,这就是背压。针对背压,Rxjava提供了支持背压处理的观察者和被观察者,即Flowable和Subscriber。
9.1Flowable
Flowable是Observable(观察者)的一种新实现,但Flowable额外实现了非阻塞式背压策略。同时,用Flowable的时候观察者变为Subscriber。例如下面示例:
Flowable.create({ emitter ->Log.d("rxdemo", "send 1")emitter.onNext(1)Log.d("rxdemo", "send 2")emitter.onNext(2)Log.d("rxdemo", "finish")emitter.onComplete()},BackpressureStrategy.ERROR
).subscribe(object : Subscriber<Int> {override fun onSubscribe(s: Subscription) {Log.d("rxdemo", "onSubscribe")s.request(2)}override fun onNext(integer: Int) {Log.d("rxdemo", "get the $integer")}override fun onError(t: Throwable) {Log.w("rxdemo", "onError: ", t)}override fun onComplete() {Log.d("rxdemo", "onComplete")}
})
对应输出结果为:
看到这里,你会对两个地方产生疑问,一个是onSubscribe()中的s.request(2),这里是向观察者请求处理2条数据的意思,如果没有这行代码,则我们不请求处理数据,程序则会触发这里的背压策略:BackpressureStrategy.ERROR,直接报错。当然,背压策略不仅这一个,还有其余几个:
9.2背压策略
·BackpressureStrategy.ERROR:直接抛出MissingBackpressureException异常;
·BackpressureStratery.MISSING:不使用背压,没有缓存,仅提示:缓存区满了
·BackpressureStratery.BUFFER:缓存所有数据,直到观察者处理,如果观察者处理不及时也会出现OOM,被观察者可无限发送事件,但实际上是放在缓存区。
·BackpressureStratery.DROP:丢弃超过缓存区大小(128)的数据
·BackpressureStratery.LATEST:只保存最新的最后的事件,超过缓存区大小(128)时用新数据覆盖老数据。
到此,我们可以总结下,背压的出现是为了解决两个方面主要问题:
· 当发送数据速度 > 接受数据速度,数据堆叠缓存会撑满;
· 当缓存区大小存满,被观察者继续发送下一个事件时(还是相当于撑爆了缓存区)
到这里你会发现,这还是个缓存区问题,那么这个缓存区是否就是128呢?我们可以通过Flowable.bufferSize()来获取缓存的大小,例如:
Flowable.create({ emitter ->//发送128个Hello bufferfor (i in 0 until Flowable.bufferSize()) {Log.d("rxdemo", "Hello buffer $i")emitter.onNext("Hello buffer $i")}},BackpressureStrategy.ERROR
).subscribeOn(Schedulers.io()).observeOn(Schedulers.newThread()).subscribe(object : Subscriber<String> {override fun onSubscribe(s: Subscription) {Log.d("rxdemo", "onSubscribe")}override fun onNext(str: String) {Log.d("rxdemo", "get the $str")}override fun onError(t: Throwable) {Log.w("rxdemo", "onError: ", t)}override fun onComplete() {Log.d("rxdemo", "onComplete")}
})
对应的日志输出为:
由日志不难看出其发挥大小为128,也就是默认缓存数据为128个,上述代码发出了128个Hello buffer。如果这个时候我们多发出来一个会怎样?修改下for循环条件i in 0 until Flowable.bufferSize()+1。最后会得到结果:
毫无意外,Subscriber并没有请求处理数据,缓存已经爆满,外加配置的背压策略为BackpressureStrategy.ERROR,所以这里会在缓存撑爆的情况下通知Subscriber发生错误,调用ERROR,打印MissingBackpressureException。
9.3另一种调用背压策略的方式
看到这里你可能会想,如果不使用create方法创建Flowable,而是用range、interval这些操作符创建,那如何配置策略?对此,Rxjava提供了对应的方法来匹配相应的背压策略:onBackpressureBuffer()、onBackpressureDrop()、onBackpressureLatest()(看名字就知道对应的策略啦),例如:
Flowable.range(0,100).onBackpressureLatest().subscribeOn(Schedulers.io()).observeOn(Schedulers.newThread()).subscribe(object : Subscriber<Int> {override fun onSubscribe(s: Subscription) {Log.d("rxdemo", "onSubscribe")}override fun onNext(num: Int) {Log.d("rxdemo", "get the $num")}override fun onError(t: Throwable) {Log.w("rxdemo", "onError: ", t)}override fun onComplete() {Log.d("rxdemo", "onComplete")}
})
其实,到这里你会发现Rxjava的强大之处,能随意切换线程,跟retrofit结合做网络请求框架,能用timer做定时操作,用interval做周期性操作,甚至进行数组、list的遍历等。
10.RxBus
一种基于RxJava实现事件总线的一种思想,可完美替代EventBus,相关代码参考
11.RxBinding
主要与RxJava结合用于一些View的事件绑定,相关代码参考
12.内存泄露
Rxjava使用不当会造成内存泄露,在页面销毁后,Observable仍然还有事件等待发送和处理(比如interval做周期性操作而没有停下来),这个时候会导致Activity回收失败,从而致使内存泄露。
解决办法:
·使用Disposable,关闭页面时调用dispose()取消订阅;
·使用CompositeDisposable,添加一组Disposable,在关闭页面时同时取消订阅。
也可以将其与Activity基类生命周期进行绑定,在销毁时取消订阅。
相关文章:
Android中的RxJava入门及常用操作符
文章目录 1.定义2.作用3.特点4.使用4.1创建被观察者(Observable)4.2创建观察者(Observer)4.3订阅(Subscribe)4.4Dispose 5.操作符5.1操作符类型5.2just操作符5.2链式调用5.3 fromArray操作符5.4 fromIterab…...
【数字化转型】10大数字化转型能力成熟度模型03
一、前言 数字化转型是数据化能力建设的目标和价值,作为一个新兴的课题,目前为止并未出现一个统一的数字化转型成熟度模型。不同的企业和机构,根据自身的发展和认知,推出了自己的企业级或者准行业级标准。这些标准具有很强的参考意义,作者收集和整理了相关的标准和规范,整…...
【算法与数据结构】--前言
欢迎来到《算法与数据结构》专栏!这个专栏将引领您进入计算机科学领域中最重要、最精彩的领域之一:算法与数据结构。不管您是一名初学者,还是已经拥有一定编程经验的开发者,都可以从这里找到有益的知识和实践。 在计算机科学的世…...
R²决定系数
R 2 R^2 R2(决定系数)是一个用于衡量统计模型拟合数据的指标,通常用于线性回归分析。它表示模型所解释的因变量(目标变量)方差的比例,范围从0到1。 更具体地说, R 2 R^2 R2告诉我们模型能够解释…...
软件工程与计算总结(一)软件工程基础
国庆快乐,今天开始更新《软件工程与计算(卷二)》的重要知识点内容~ 一.软件 1.软件独立于硬件 早期的软件是为了计算机硬件在研究型项目中而开发制造的,人们使用专门针对于硬件的指令码和汇编语言编写,这也是最早软件…...
SpringBoot-黑马程序员-学习笔记(一)
8.pom文件中的parent 我们使用普通maven项目导入依赖时,通常需要在导入依赖的时候指定版本号,而springboot项目不需要指定版本号,会根据当前springboot的版本来下载对应的最稳定的依赖版本。 点开pom文件会看到这个: 继承了一个…...
Apache Tomcat安装、运行
介绍 Apache Tomcat是下面多个规范的一个开源实现:Jakarta Servlet、Jakarta Server Pages、Jakarta Expression Language、Jakarta WebSocket、Jakarta Annotations 和 Jakarta Authentication。这些规范是 Jakarta EE 平台的一部分。 Jakarta EE 平台是Java EE平…...
聊聊分布式架构05——[NIO基础]BIO到NIO的演进
目录 I/O I/O模型 BIO示例 BIO与NIO比较 NIO的三大核心 NIO核心之缓冲区 Buffer常用子类: Buffer常用API Buffer中的重要概念 NIO核心之通道 FileChannel 类 FileChannel常用方法 NIO核心之选择器 概述 应用 NIO非阻塞原理分析 服务端流程 客户端…...
聊天、会议、多媒体一体化:多平台支持的即时通讯系统 | 开源日报 No.44
harness/gitness Stars: 28.2k License: Apache-2.0 Gitness 是一个建立在 Drone 之上的新型开源开发者平台,具备代码托管和流水线功能。它提供了以下核心优势: 轻量级、超快速的代码托管和持续集成服务支持 Docker 容器化部署可以在本地环境中构建和…...
收录一些常见的算法题型
常用算法 字符串 s.trim():去掉字符串首尾的空格s.split("\\s"):按照空格对字符串分割 树 前中后序遍历 /*** 统一一下* param root* return*///前序public static List<Integer> preOrder(TreeNode root){List<Integer> list new ArrayList();Stac…...
Node-RED系列教程-25node-red获取天气
安装节点:node-red-contrib-weather 节点图标如下: 使用说明:node-red-contrib-weather (node) - Node-RED 流程图中填写经度和纬度即可。 演示: json内容: {...
Rust中的枚举和模式匹配
专栏简介:本专栏作为Rust语言的入门级的文章,目的是为了分享关于Rust语言的编程技巧和知识。对于Rust语言,虽然历史没有C、和python历史悠远,但是它的优点可以说是非常的多,既继承了C运行速度,还拥有了Java…...
好物周刊#19:开源指北
https://github.com/cunyu1943/JavaPark https://yuque.com/cunyu1943 村雨遥的好物周刊,记录每周看到的有价值的信息,主要针对计算机领域,每周五发布。 一、项目 1. Vditor 一款浏览器端的 Markdown 编辑器,支持所见即所得、…...
分布式数据库(林子雨慕课课程)
文章目录 4. 分布式数据库HBase4.1 HBase简介4.2 HBase数据模型4.3 HBase的实现原理4.4 HBase运行机制4.5 HBase的应用方案4.6 HBase安装和编程实战 4. 分布式数据库HBase 4.1 HBase简介 HBase是BigTable的开源实现 对于网页搜索主要分为两个阶段 1.建立整个网页索引…...
使用UiPath和AA构建的解决方案 3. CRM 自动化
您是否曾经从一个应用程序中查找数据并更新另一个系统? 在许多情况下,人们在系统之间复制和移动数据。有时,可能会发生“转椅活动”,从而导致人为失误。RPA可以帮助我们自动化这些活动,使其更快,同时还可以消除任何人为错误。 在这个项目中,我们将在客户服务中自动化一…...
【C++设计模式之状态模式:行为型】分析及示例
简介 状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变其行为,看起来就像是改变了其类。状态模式将对象的状态封装成不同的类,并使得对象在不同状态下有不同的行为。 描述 状态模式通过…...
微信小程序使用路由传参和传对象的方法
近期在做微信小程序开发,在页面跳转时,需要携带参数到下一个页面,尤其是将对象传入页面。为了方便重温,特此记录。 路由传字符串参数 原始页面 传递字符串参数比较简单。路由跳转有两种方式,一种是通过navigator组件…...
中国创可贴市场研究与未来预测报告(2023版)
内容简介: 创可贴由胶布(带)、吸水垫、防粘层等组成,胶布以弹性布、棉布、无纺布或PE、PVC、PU打孔膜、TPU等材料为常见基材,涂以氧化锌和橡胶为主要原料的胶浆或医用压敏胶黏剂或丙烯酸酯胶粘剂制成。 目前中国主要…...
水库安全监测方案(实时数据采集、高速数据传输)
一、引言 水库的安全监测对于防止水灾和保障人民生命财产安全至关重要。为了提高水库安全监测的效率和准确性,本文将介绍一种使用星创易联DTU200和SG800 5g工业路由器部署的水库安全监测方案。 二、方案概述 本方案主要通过使用星创易联DTU200和SG800 5g工业路…...
vue项目 ueditor使用示例
简介 UEditor是由百度Web前端研发部开发的所见即所得富文本web编辑器,具有轻量,功能丰富,易扩展等特点。UEditor支持常见的文本编辑功能,如字体、颜色、大小、加粗、斜体、下划线、删除线等,同时还支持超链接、图片上…...
深度学习笔记之优化算法(四)Nesterov动量方法的简单认识
机器学习笔记之优化算法——Nesterov动量方法的简单认识 引言回顾:梯度下降法与动量法Nesterov动量法Nesterov动量法的算法过程描述总结 引言 上一节对动量法进行了简单认识,本节将介绍 Nesterov \text{Nesterov} Nesterov动量方法。 回顾:…...
比 N 小的最大质数
系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…...
JavaScript 生成随机颜色
代码 function color(color) {return (color "0123456789abcdef"[Math.floor(Math.random() * 6)]) && (color.length 6 ? color : arguments.callee(color)); }使用 // 用法1:全部随机生成 "#" color(""); // #201050…...
Savepoints
语法 SAVEPOINT 名称 RELEASE SAVEPOINT 名称 ROLLBACK TRANSACTION TO SAVEPOINT 名称 Savepoints 与BEGIN和COMMIT类似的创建事务的方法,名称不要求唯一且可以嵌套使用。 可以用在BEGIN…COMMIT定义的事务内部或外部。当在外部时,最外层的savepoin…...
【MySQL】基本查询(二)
文章目录 一. 结果排序二. 筛选分页结果三. Update四. Delete五. 截断表六. 插入查询结果结束语 操作如下表 //创建表结构 mysql> create table exam_result(-> id int unsigned primary key auto_increment,-> name varchar(20) not null comment 同学姓名,-> chi…...
Qt:多语言支持,构建全面应用程序“
Qt:强大API、简化框架、多语言支持,构建全面应用程序" 强大的API:Qt提供了丰富的API,包括250多个C类,基于模板的集合、序列化、文件操作、IO设备、目录管理、日期/时间等功能。还包括正则表达式处理和支持2D/3D…...
性能监控-链路级监控工具
常见的链路监控工具,我们都称之为 APM 开源工具 几个开源的好用的工具,它们分别是 Pinpoint、SkyWalking、Zipkin、CAT 网络上也有人对这几个工具做过测试 比对,得到的结论是每个产品对性能的影响都在 10% 以下,其中 SkyWalking …...
clickonce 程序发布到ftp在使用cnd 加速https 支持下载,会不会报错
ClickOnce 是一种用于发布和部署.NET应用程序的技术,通常用于本地部署或通过网络分发应用程序。将 ClickOnce 程序发布到 FTP 服务器并使用 CDN(内容分发网络)进行加速是可能的,但要确保配置正确以避免出现错误。 在使用 CDN 加速…...
Nginx详细学习记录
1. Nginx概述 Nginx是一个轻量级的高性能HTTP反向代理服务器,同时它也是一个通用类型的代理服务器,支持绝大部分协议,如TCP、UDP、SMTP、HTTPS等。 1.1 Nginx基础架构 Nginx默认采用多进程工作方式,Nginx启动后,会运行…...
golang gin——中间件编程以及jwt认证和跨域配置中间件案例
中间件编程jwt认证 在不改变原有方法的基础上,添加自己的业务逻辑。相当于grpc中的拦截器一样,在不改变grpc请求的同时,插入自己的业务。 简单例子 func Sum(a, b int) int {return a b }func LoggerMiddleware(in func(a, b int) int) f…...
c2c网站开发策划/公众号软文是什么意思
Latex基本语法的备忘录 Latex基本语法前言一、常见的数学符号1. 数学上的“**属于**”、“**不属于**”符号。2. 数学的矩阵的**转置符号**书写。3. 数学中求和公式。4.数学中字母代上标5.数字或公式上添加方框6.数学分式7.数学公式中在某些符号下添加花括号8.数学中的垂直符号…...
网站建设全包公司推荐/seo是搜索引擎优化
重新安装了ubuntu12.04后,Ubuntu开机就出现:error:no such partitiongrub rescue >一般情况下,出现这类错误是引导文件出错或者系统找不到引导文件,而系统并没有坏,所以不用重新安装系统。需要进行如下的…...
泉州网站制作设计/个人网页免费域名注册入口
1.场景 参数验证功能 1 是基于 JSR303 实现的,用户只需标识 JSR303 标准的验证 annotation,并通过声明 filter 来实现验证 2。 2.maven以依赖 <dependency><groupId>javax.validation</groupId><artifactId>validation-api</…...
wordpress更新很慢/技能培训有哪些
今天遇到个小问题。 导出excel的时候报路径错误 。我的fileName中有一个部门名字是从session中获取的,部门名字中出现“/”字符。结果导出路径报错。 解决办法举例: String a "中国/北京"; String b a.replace(“/”,“-”)&…...
微信红包开发平台/百度seo优化方案
一、DLL文件常识DLL是Dynamic Link Library的缩写,意为动态链接库。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时&…...
摄影类全屏式展示的wordpress主题免费下载/热狗网站排名优化外包
let测试 <script>// var 声明的变量往往会越域// let 声明的变量有严格局部作用域// {// var a 1;// let b 2;// }// console.log(a); // 1// console.log(b); // ReferenceError: b is not defined// var 可以声明多次// let 只能声明一次var m 1var m 2le…...