Kotlin, RxJava study notes

About Kotlin

  • kotlin keywords:

     object:定义静态类
    
     lazy:懒属性(延迟加载)
    
     when:用于判断,相当于java中的switch()语句
    
     try{…}catch(){…}:用于捕捉异常
    
     let:默认当前这个对象作为闭包的it参数,返回值是函数里面最后一行,或者指定return
    
     apply:调用某对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象
    
     with:一个单独的函数,并不是Kotlin中的extension,所以调用方式有点不一样,返回是最后一行,  然后可以直接调用对象的方法,感觉像是let和apply的结合
    
     constructor:用于标识次级构造函数,解决重载
    
     init:因为kotlin中的类定义同时也是构造函数,这个时候是不能进行操作的, 所以kotlin增加了一个新的关键字init用来处理类的初始化问题,init模块中的内容可以直接使用构造函数的参数
    
     open:由于kotlin中所有类和方法默认都是final的,不能直接继承或重写,需要继承的类或类中要重写的方法都应当在定义时添加open关键字
    
     abstract:描述一个抽象类,抽象类里的方法如果不提供实现则需要用abstract关键字来描述抽象方法.抽象的方法默认是open的
    
     companion object: 伴随对象, 修饰静态方法
    
     is:等同于Java中的instanceof
    
    • Kotlin static code block writing (lambda expression)
    companion object {
        private val TAG = MainActivity.javaClass.name
        init {
            RxJavaPlugins.setErrorHandler { throwable ->
                if (throwable is InterruptedIOException) {
                    Log.d(TAG, "Io interrupted")
                }
            }
        }
    }

About RxJava

  • Event Streaming Mechanism

    Use two water pipes to replace the observer (upstream of generating events) and the observed (downstream of receiving events), and establish a connection in a certain way

    /**
     * RxJava链式操作
     */
    fun connect() {
        //创建一个上游Observable(lambda表达式写法)
        Observable.create(ObservableOnSubscribe<Int> { emitter ->
            //ObservableEmitter:用来发出事件的,它可以分别发出next、complete和error三种类型的事件
            /**
             * 发送规则:
             * <ol>
             * <li> 上游可以发送无限个onNext, 下游也可以接收无限个onNext.
             * <li> 当上游发送了一个onComplete后, 上游onComplete之后的事件将会继续发送, 而下游收到onComplete事件之后将不再继续接收事件.
             * <li> 当上游发送了一个onError后, 上游onError之后的事件将继续发送, 而下游收到onError事件之后将不再继续接收事件.
             * <li> 上游可以不发送onComplete或onError.
             * <li> 最为关键的是onComplete和onError必须唯一并且互斥(需要代码自行控制)
             * </ol>
             */
            emitter.onNext(1)
            emitter.onNext(2)
            emitter.onNext(3)
            emitter.onComplete()
            //建立连接
            /**
             * subscribe()有多个重载的方法:
             * <ol>
             * <li> public final Disposable subscribe() {}
             * <li> public final Disposable subscribe(Consumer<? super T> onNext) {}
             * <li> public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {}
             * <li> public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) {}
             * <li> public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {}
             * <li> public final void subscribe(Observer<? super T> observer) {}
             * <li> 不带任何参数的subscribe() 表示下游不关心任何事件,你上游尽管发你的数据去吧, 老子可不管你发什么.
             * <li> 带有一个Consumer参数的方法表示下游只关心onNext事件, 其他的事件我假装没看见.
             * </ol>
             */
        }).subscribe(object : Observer<Int> {
            //创建一个下游 Observer

            //两根水管之间的一个开关
            private var mDisposable: Disposable? = null
            private var i: Int = 0
            override fun onSubscribe(d: Disposable) {
                Log.d(TAG, "subscribe")
                mDisposable = d
            }

            override fun onNext(value: Int?) {
                Log.d(TAG, "next value = $value")
                i++
                if (i == 2) {
                    Log.d(TAG, "dispose")
                    //调用dispose()并不会导致上游不再继续发送事件, 上游会继续发送剩余的事件
                    mDisposable!!.dispose()
                    Log.d(TAG, "isDisposed : " + mDisposable!!.isDisposed)
                }
            }

            override fun onError(e: Throwable) {
                Log.d(TAG, "error")
            }

            override fun onComplete() {
                Log.d(TAG, "complete")
            }
        })
    }
  • Multi-threaded implementation of event asynchronous operation

    In RxJava, many threading options are built in for us to choose from, for example:

    1. Schedulers.io() represents the thread of io operation, usually used for io-intensive operations such as network, reading and writing files, etc.
    2. Schedulers.computation() represents CPU-intensive operations, such as operations that require a lot of computation
    3. Schedulers.newThread() represents a regular new thread
    4. AndroidSchedulers.mainThread() represents the main thread of Android
    fun switchThread() {

        val observable = Observable.create(ObservableOnSubscribe<Int> { emitter ->
            Log.d(TAG, "Observable thread is : " + Thread.currentThread().name)
            Log.d(TAG, "emitter 1")
            emitter.onNext(1)
        })

        val consumer = Consumer<Int> { integer ->
            Log.d(TAG, "Observer thread is :" + Thread.currentThread().name)
            Log.d(TAG, "onNext: " + integer!!)
        }
        /**
         * 1. subscribeOn() 指定的是上游发送事件的线程
         * 2. observeOn() 指定的是下游接收事件的线程
         * 3. 多次指定上游的线程只有第一次指定的有效
         * 4. 多次指定下游的线程是可以的, 每调用一次observeOn(), 下游的线程就会切换一次
         *
         */
        observable.subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(consumer)
    }
  • Map operator

    Map is the simplest transformation operator in RxJava. Its function is to apply a function to each event sent upstream, so that each event changes according to the specified function.

    1. Through Map, events sent from upstream can be converted into any type, which can be an Object or a collection
    2. FlatMap transforms an upstream Observable that emits events into multiple Observables that emit events, and then merges their emitted events into a single Observable, but does not guarantee the order of events
    3. concatMap has almost the same function as flatMap, except that its results are sent in strict accordance with the order sent by the upstream
    /**
     * 嵌套的网络请求, 首先需要去请求注册, 待注册成功回调了再去请求登录的接口
     * 登录和注册返回的都是一个上游Observable, flatMap操作符的作用就是把一个Observable转换为另一个Observable
     */
    fun nestedNetWork(context: Context) {
        val api = RetrofitProvider.get().create(Api::class.java)
        api.register(RegisterRequestBean("zhangsan", 0)) //发起注册请求
                .subscribeOn(Schedulers.io())                      //在IO线程进行网络请求
                .observeOn(AndroidSchedulers.mainThread())         //回到主线程去处理请求注册结果
                .doOnNext {
                    //先根据注册的响应结果去做一些操作
                }
                .observeOn(Schedulers.io())                        //回到IO线程去发起登录请求
                .flatMap { api.login(LoginRequestBean("zhangsan", 0)) }
                .observeOn(AndroidSchedulers.mainThread())         //回到主线程去处理请求登录的结果
                .subscribe({
                    Toast.makeText(context, "登录成功", Toast.LENGTH_SHORT).show()
                }, {
                    Toast.makeText(context, "登录失败", Toast.LENGTH_SHORT).show()
                })
    }
  • Zip operator

    Zip combines the events emitted by multiple Observables together through a function, and then emits these combined events. It applies the function in strict order. It only emits as much data as the Observable that emits the fewest items.

    /**
     * 一个界面需要展示用户的一些信息, 而这些信息分别要从两个服务器接口中获取, 而只有当两个都获取到了之后才能进行展示
     */
    fun getUserInfo() {
        val api = RetrofitProvider.get().create(Api::class.java)
        val observable1 = api.getUserBaseInfo(UserBaseInfoRequest("zhangsan", 0)).subscribeOn(Schedulers.io())

        val observable2 = api.getUserExtraInfo(UserExtraInfoRequest(24, "man")).subscribeOn(Schedulers.io())

        Observable.zip(observable1, observable2,
                BiFunction<UserBaseInfoResponse, UserExtraInfoResponse, UserInfo> { baseInfo, extraInfo ->
                    UserInfo(baseInfo, extraInfo)
                }).observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    //do something
                }
    }
  • Flowable operator

    1. Conventional ideas for solving the imbalance of upstream and downstream flow rates:

      1) Governing in terms of quantity, reducing the events sent into the water tank, but filtering events will cause events to be lost

      2) Governing from speed, slowing down the speed at which events are sent into the water tank, but slowing down may lead to performance loss

    2. The difference between synchronous and asynchronous is only whether there is a buffer pool

    3. Use Flowable for big data flow and Observable for small data flow
    4. responsive pull
    5. The request is regarded as the ability of the downstream to process events. If the downstream can handle a few, just tell the upstream how many I want.
    6. There is a tank with a size of 128 in Flowable by default. When the upstream and downstream work in different threads, the upstream will first send events to this tank
    7. BackpressureStrategy.BUFFER has no size limit
    8. BackpressureStrategy.ERROR will directly throw a MissingBackpressureException when the upstream and downstream flow rates are not balanced
    9. BackpressureStrategy.DROP directly discards events that cannot be saved
    10. BackpressureStrategy.LATEST only keeps the latest events
    11. Flowable not created by yourself:
      1) onBackpressureBuffer()
      2) onBackpressureDrop()
      3) onBackpressureLatest()
    12. When the upstream and downstream are in the same thread, calling request(n) downstream will directly change the value of requested in the upstream. Multiple calls will superimpose this value, and the upstream will reduce this value every time an event is sent. value, when this value is reduced to 0, continuing to send events will throw an exception
    13. When the upstream and downstream work in different threads, each thread has a requested, and when we call request(), what actually changes is the requested in the downstream main thread, and the value of the requested in the upstream is determined by RxJava Internally call request(n) to set, this call will be automatically triggered when appropriate
object FlowableUse {

    private val TAG = FlowableUse.javaClass.name

    var mSubscription: Subscription? = null

    /**
     * 读取一个文本文件,需要一行一行读取,然后处理并输出,
     * 如果文本文件很大的时候,比如几十M的时候,全部先读入内存肯定不是明智的做法,因此我们可以一边读取一边处理
     */
    fun readFile() {
        Flowable.create(FlowableOnSubscribe<String> { emitter ->
            try {
                val reader = FileReader("test.txt")
                val br = BufferedReader(reader)

                val str = br.readLine()
                while (str != null && !emitter.isCancelled) {
                    while (emitter.requested() == 0L) {
                        if (emitter.isCancelled) {
                            break
                        }
                    }
                    emitter.onNext(str)
                }

                br.close()
                reader.close()

                emitter.onComplete()
            } catch (e: Exception) {
                emitter.onError(e)
            }
        }, BackpressureStrategy.ERROR)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.newThread())
                .subscribe(object : Subscriber<String> {

                    override fun onSubscribe(s: Subscription) {
                        mSubscription = s
                        s.request(1)
                    }

                    override fun onNext(string: String) {
                        Log.d(TAG, string)
                        try {
                            Thread.sleep(2000)
                            mSubscription?.request(1)
                        } catch (e: InterruptedException) {
                            e.printStackTrace()
                        }

                    }

                    override fun onError(t: Throwable) {}

                    override fun onComplete() {}
                })
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325768378&siteId=291194637