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基类生命周期进行绑定,在销毁时取消订阅。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/97885.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

四位十进制数字频率计VHDL,仿真视频、代码

名称&#xff1a;四位十进制数字频率计VHDL&#xff0c;quartus仿真 软件&#xff1a;Quartus 语言&#xff1a;VHDL 代码功能&#xff1a; 使用直接测频法测量信号频率&#xff0c;测频范围为1~9999Hz&#xff0c;具有超量程报警功能 演示视频&#xff1a;四位十进制数字频…

SpringBoot结合dev-tool 实现IDEA项目热部署

什么是热部署&#xff1f; 应用正在运行的时候升级功能, 不需要重新启动应用对于Java应用程序来说, 热部署就是在运行时更新Java类文件 通俗的来讲&#xff0c;应用在运行状态下&#xff0c;修改项目源码后&#xff0c;不用重启应用&#xff0c;会把编译的内容部署到服务器上…

李宏毅 2022机器学习 HW3 boss baseline 上分记录

作业数据是所有数据都有标签的版本。 李宏毅 2022机器学习 HW3 boss baseline 上分记录 1. 训练数据增强, private 0.760562. cross validation&ensemble, private 0.816473. test dataset augmentation, private 0.824584. resnet, private 0.865555. Image Normalizatio…

1024 画跳动的爱心#程序代码 #编程语言 #计算机

废话不多说 直接开干! 用到库 random time tkinter 快速镜像 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tkinter 上代码 import random import time from math import sin, cos, pi, log from tkinter import *CANVAS_WIDTH 640 # 画布的宽 CANVAS_HEIGH…

vue启动项目,npm run dev出现error:0308010C:digital envelope routines::unsupported

运行vue项目&#xff0c;npm run dev的时候出现不支持错误error:0308010C:digital envelope routines::unsupported。 在网上找了很多&#xff0c;大部分都是因为版本问题&#xff0c;修改环境之类的&#xff0c;原因是对的但是大多还是没能解决。经过摸索终于解决了。 方法如…

LLMs与外部应用程序交互 Interacting with external applications

在上一节中&#xff0c;您看到了LLM如何与外部数据集进行交互。现在让我们看看它们如何与外部应用程序进行交互。为了激发需要这种LLM增强的问题和用例的类型&#xff0c;您将重新审视之前在课程中看到的客户服务机器人示例。在这次浏览中&#xff0c;您将查看需要的集成&#…

传输层协议——TCP、UDP

目录 1、UDP 协议&#xff08;用户数据报协议&#xff09; 协议特点 报文首部格式 2、TCP 协议&#xff08;传输控制协议&#xff09; 协议特点 报文首部格式 TCP连接建立时的三次握手 TCP拆除连接的四次挥手 TCP的流量控制 TCP的拥塞控制 3、传输层端口号 三类端口…

自动驾驶学习笔记(二)——Apollo入门

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《2023星火培训【感知专项营】》免费课程—>传送门 文章目录 前言 Ubuntu Linux文件系统 Linux指令…

Apache Tomcat安装、运行

介绍 Apache Tomcat是下面多个规范的一个开源实现&#xff1a;Jakarta Servlet、Jakarta Server Pages、Jakarta Expression Language、Jakarta WebSocket、Jakarta Annotations 和 Jakarta Authentication。这些规范是 Jakarta EE 平台的一部分。 Jakarta EE 平台是Java EE平…

Springboot项目log4j与logback的Jar包冲突问题

异常信息关键词&#xff1a; SLF4J: Class path contains multiple SLF4J bindings. ERROR in ch.qos.logback.core.joran.spi.Interpreter24:14 - no applicable action for [properties], current ElementPath is [[configuration][properties]] 详细异常信息&#xff1a…

C/C++ 进程间通信system V IPC对象超详细讲解(系统性学习day9)

目录 前言 一、system V IPC对象图解 1.流程图解&#xff1a; ​编辑 2.查看linux内核中的ipc对象&#xff1a; 二、消息队列 1.消息队列的原理 2.消息队列相关的API 2.1 获取或创建消息队列&#xff08;msgget&#xff09; 实例代码如下&#xff1a; 2.2 发送消息到消…

c++视觉图像线性混合

图像线性混合 使用 cv::addWeighted() 函数对两幅图像进行线性混合。alpha 和 beta 是两幅图像的权重&#xff0c;它们之和应该等于1。gamma 是一个可选的增益&#xff0c;这里设置为0。 你可以通过调整 alpha 的值来改变混合比例。如果 alpha0.5&#xff0c;则两幅图像等权重…

最短路径专题8 交通枢纽 (Floyd求最短路 )

题目&#xff1a; 样例&#xff1a; 输入 4 5 2 0 1 1 0 2 5 0 3 3 1 2 2 2 3 4 0 2 输出 0 7 思路&#xff1a; 由题意&#xff0c;绘制了该城市的地图之后&#xff0c;由给出的 k 个编号作为起点&#xff0c;求该点到各个点之间的最短距离之和最小的点是哪个&#xff0c;并…

C语言学生成绩录入系统

一、系统概述 该系统是一个由链表创建主菜单的框架&#xff0c;旨在快速创建学生成绩录入系统的主菜单结构。其主要任务包括&#xff1a; 实现链表的创建、插入和遍历功能&#xff0c;用于存储和展示学生成绩录入系统各个模块的菜单项。 2. 提供用户友好的主菜单界面&#xf…

Redis的五种常用数据类型

1.字符串 String的数据结构是简单的Key-Value模型&#xff0c;Value可以是字符串&#xff0c;也可以是数字。 String是Redis最基本的类型&#xff0c;是二进制安全的&#xff0c;意味着Redis的string可以包含任何数据&#xff0c;比如jpg图片。 一个redis中字符串value最大是…

AT9110H-单通道低压 H桥电机驱动芯片

AT9110H能够驱动一个直流有刷电机或其它诸如螺线管的器件。输出驱动模块由PMOSNMOS功率管构成的H桥组成&#xff0c;以驱动电机绕组。AT9110H能够提供高达12V1A的驱动输出。 AT9110H是SOP8封装&#xff0c;且是无铅产品&#xff0c;符合环保标准。 AT9110H具有一个PWM (IN1/IN2…

SpringBoot-黑马程序员-学习笔记(一)

8.pom文件中的parent 我们使用普通maven项目导入依赖时&#xff0c;通常需要在导入依赖的时候指定版本号&#xff0c;而springboot项目不需要指定版本号&#xff0c;会根据当前springboot的版本来下载对应的最稳定的依赖版本。 点开pom文件会看到这个&#xff1a; 继承了一个…

arm-三盏灯流水

.text .global _start _start: 1.设置GPIOE寄存器的时钟使能 RCC_MP_AHB4ENSETR[4]->1 0x50000a28 LDR R0,0x50000A28 LDR R1,[R0] ORR R1,R1,#(0x3<<4) 第四位第五位都设置为1 STR R1,[R0] 写回2.设置PE10管脚为输出模式 GPIOE_MODER[21:20]->01 0x5000…

扭线机控制

扭线机属于线缆加工设备&#xff0c;线缆加工设备种类非常多。有用于网线绞合的单绞&#xff0c;双绞机等&#xff0c;有关单绞机相关算法介绍&#xff0c;大家可以查看专栏相关文章&#xff0c;有详细介绍&#xff0c;常用链接如下&#xff1a; 线缆行业单绞机控制算法&#…

【软考】9.1 顺序表/链表/栈和队列

《线性结构》 顺序存储和链表存储 每个元素最多只有一个出度和一个入度&#xff0c;表现为一条线状链表存储结构&#xff1a;每个节点有两个域&#xff0c;即数据&#xff0c;指针域&#xff08;指向下一个逻辑上相邻的节点&#xff09; 时间复杂度&#xff1a;与其数量级成正…