Android中的RxJava入门及常用操作符

news2024/12/23 23:32:24

文章目录

    • 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 ->
        //创建观察者consumer
        println(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? = null
    override 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 {    //Consumer
        Thread.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 buffer
        for (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.coloradmin.cn/o/1066813.html

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

相关文章

服务器文件备份

服务器上&#xff0c;做好跟应用程序有关的文件备份&#xff08;一般备份到远程的盘符&#xff09;&#xff0c;有助于当服务器发生硬件等故障时&#xff0c;可以对系统进行进行快速恢复。 下面以Windows服务器为例&#xff0c;记录如何做文件的备份操作。 具体操作如下&#…

贷款行业,教你如何直接沟通客户

信贷行业拓展业务的人力与时间成本非常高。如是做小微贷款业务的公司可能在寻找贷款客户、筛选客户资质这两项初始工作上花掉超过50%的精力。 并且由于行业特殊性&#xff0c;金融信贷受政策的影响比较大&#xff0c;没法形成固定的推广渠道&#xff0c;线上营销不好做&#x…

什么是站内搜索引擎?如何在网站中加入站内搜索功能?

在当今数字时代&#xff0c;用户体验对于网站的成功起着至关重要的作用。提升用户体验和改善整体网站性能的一种方法是引入站内搜索引擎。站内搜索引擎是一种强大的工具&#xff0c;它的功能类似于Google或Bing等流行搜索引擎&#xff0c;但它专注于实施自己网站上的内容。用户…

工业路由器项目应用(4g+5g两种工业路由器项目介绍)

引言&#xff1a; 随着工业智能化的不断发展&#xff0c;工业路由器在各个领域的应用越来越广泛。本文将介绍两个工业路由器项目的应用案例&#xff0c;一个是使用SR500 4g工业路由器&#xff0c;另一个是使用SR800 5g工业路由器。 详情&#xff1a;https://www.key-iot.com/i…

IPO观察丨重新启动上市,“小而美”能让科迪乳业再次出圈吗?

如今&#xff0c;乳制品市场俨然是一片红海&#xff0c;尽管市场竞争激烈&#xff0c;但对于一些企业而言&#xff0c;发展机会仍然相当可观。 近日举办的2023年中工作会议上&#xff0c;科迪乳业母公司科迪集团对外表示&#xff0c;要部署好下个阶段的重点工作&#xff0c;为…

Jenkins 添加节点Node报错JNI error has occurred UnsupportedClassVersionError

节点日志 报错信息如下 Error: A JNI error has occurred, please check your installation and try again Exception in thread “main” java.lang.UnsupportedClassVersionError: hudson/remoting/Launcher has been compiled by a more recent version of the Java Runtime…

阿里云服务器通用算力型、经济型、七代云服务器实例、倚天云服务器实例区别参考

目前阿里云服务器的实例规格中&#xff0c;既有五代六代实例规格&#xff0c;也有七代和八代倚天云服务器&#xff0c;同时还有通用算力型及经济型这些刚推出不久的新品云服务器实例&#xff0c;其中第五代实例规格已经不是主推的实例规格了&#xff0c;现在主售的实例规格是七…

windows server 2012 R2的C盘空间满了,但是找不到大文件的两种原因

目录 一、第一种原因&#xff1a;windows server backup备份导致C盘空间耗尽 二、第二种原因&#xff1a;超级桌管软件生成的文件放在C盘被隐藏 最近经历了两次C盘满了&#xff0c;但是又找不到大文件的问题&#xff0c;定位了许久&#xff0c;以下是两种原因。 一、第一种原…

python实现UI自动化配置谷歌浏览器驱动

web端UI自动化执行在哪个浏览器&#xff0c;需要对应哪个浏览器的驱动。以谷歌浏览器为例&#xff0c;进行配置。一、查看谷歌浏览器版本 如下截图&#xff1a;我的谷歌浏览器版本是&#xff1a; 117.0.5938.150 二、下载对应版本谷歌浏览器驱动 首先可以从其他版本驱动地址中…

超详细 | 鲸鱼优化算法原理及其实现(Matlab/Python)

鲸鱼优化算法(whale optimization algorithm,WOA)是由Mirjalili和Lewis[1]于2016年提出的一种新型群体智能优化搜索方法,它源于对自然界中座头鲸群体狩猎行为的模拟&#xff0c;该算法整个过程包含搜索觅食、收缩包围和螺旋更新位置三个阶段。 鲸鱼优化算法的三个种群更新机制…

为什么亚马逊速卖通等跨境卖家都选择自养号测评,有哪些优势

自养号测评是一种对于跨境电商卖家来说非常重要的运营手段。通过自养号测评&#xff0c;卖家可以快速增加产品的销量、评论数量&#xff0c;并提升在平台中的排名&#xff0c;从而促进产品的流量转化和订单增长。自养号测评与传统的机刷方式不同&#xff0c;它通过伪装设备参数…

Linux TCP 通信并发

多进程 客户端 #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main() {//创建套接字int lfd socket(AF_INET, SOCK_STREAM, 0);if(lfd -1) {perror("socket");exi…

突发,美国再将42家中企列入实体名单 | 百能云芯

2023年10月6日&#xff0c;美国商务部工业和安全局&#xff08;BIS&#xff09;宣布对实体名单进行了更新&#xff0c;涉及到新增了49个实体。这些实体来自七个不同的国家&#xff0c;其中42家位于中国&#xff0c;其余七家分别位于爱沙尼亚、芬兰、德国、印度、土耳其、阿拉伯…

知识图谱系列Paper 1:Open-CyKG: An Open Cyber Threat Intelligence Knowledge Graph

向前进&#xff01; 一、摘要 Instant analysis of cybersecurity reports is a fundamental challenge for security experts as an immeasurable amount of cyber information is generated on a daily basis, which necessitates automated information extraction tools t…

英文论文实例赏析——如何写前言?

写作与实验、统计一样重要 研究生的学习往往会遵循这样的过程&#xff1a;实验——数据分析——写作。虽然写作是最后进行的&#xff0c;但写作的学习这应该和实验的学习、数据分析的学习保持同步&#xff0c;因为写作与统计和实验技能一样&#xff0c;是科研工具箱的必…

接口测试总结

一、了解一下HTTP与RPC 1. HTTP&#xff08;HyperText Transfer Protocol) 说明&#xff1a;超文本传输协议&#xff0c;是互联网上应用最为广泛的一种网络协议。 优点&#xff1a;就是简单、直接、开发方便&#xff0c;利用现成的http协议进行传输。 流程图&#xff1a; 2. R…

C++学习Day3:面向对象OOP、抽象

OOP特点&#xff1a;封装、继承、duotai 接口的好处&#xff1a;Communication & Protection&#xff08;向外界隐藏实现&#xff09; 抽象 类的析构函数 析构函数的名称与类的名称是完全相同的&#xff0c;只是在前面加了个波浪号&#xff08;~&#xff09;作为前缀&am…

C++ 提示并输入一个字符串,统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数

#include <iostream>using namespace std;int main() {string str;cout << "请输入一个字符串>>>" << endl;getline(cin, str);int size str.size();int xiaoxie 0;int daxie 0;int shuzi 0;int kongge 0;int qita 0;for(int i0;i < …

厂商大盘点,让我们来看看国内有哪些RPA厂商?RPA厂商有没有开箱即用的产品呢?

一、什么是RPA&#xff1f; RPA(Robotic Process Automation)机器人流程自动化&#xff0c;指用软件机器人实现业务流程的自动化&#xff0c;以“模拟人工操作”的方式进行业务操作帮助企业处理重复的、规则固定的、繁琐的流程作业。 二、国内有哪些RPA厂商 我这边根据IDC最新…