Kotlin、RxJava学习笔记

关于Kotlin

  • kotlin关键字:

     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静态代码块写法(lambda表达式)
    companion object {
        private val TAG = MainActivity.javaClass.name
        init {
            RxJavaPlugins.setErrorHandler { throwable ->
                if (throwable is InterruptedIOException) {
                    Log.d(TAG, "Io interrupted")
                }
            }
        }
    }

关于RxJava

  • 事件流机制

    用两根水管代替观察者(产生事件的上游)和被观察者(接收事件的下游),通过一定方式建立连接

    /**
     * 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")
            }
        })
    }
  • 多线程实现事件异步操作

    在RxJava中, 已经内置了很多线程选项供我们选择, 例如:

    1. Schedulers.io() 代表io操作的线程, 通常用于网络,读写文件等io密集型的操作
    2. Schedulers.computation() 代表CPU计算密集型的操作, 例如需要大量计算的操作
    3. Schedulers.newThread() 代表一个常规的新线程
    4. AndroidSchedulers.mainThread() 代表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操作符

    Map是RxJava中最简单的一个变换操作符,它的作用就是对上游发送的每一个事件应用一个函数, 使得每一个事件都按照指定的函数去变化。

    1. 通过Map,可以将上游发来的事件转换为任意的类型,可以是一个Object,也可以是一个集合
    2. FlatMap将一个发送事件的上游Observable变换为多个发送事件的Observables,然后将它们发射的事件合并后放进一个单独的Observable里,但是不保证事件的顺序
    3. concatMap它和flatMap的作用几乎一模一样, 只是它的结果是严格按照上游发送的顺序来发送的
    /**
     * 嵌套的网络请求, 首先需要去请求注册, 待注册成功回调了再去请求登录的接口
     * 登录和注册返回的都是一个上游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操作符

    Zip通过一个函数将多个Observable发送的事件结合到一起,然后发送这些组合到一起的事件。它按照严格的顺序应用这个函数。它只发射与发射数据项最少的那个Observable一样多的数据。

    /**
     * 一个界面需要展示用户的一些信息, 而这些信息分别要从两个服务器接口中获取, 而只有当两个都获取到了之后才能进行展示
     */
    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操作符

    1. 解决上下游流速不均衡常规思路:

      扫描二维码关注公众号,回复: 169459 查看本文章

      1)从数量上进行治理, 减少发送进水缸里的事件,但是过滤事件会导致事件丢失

      2)从速度上进行治理, 减缓事件发送进水缸的速度,但是减速又可能导致性能损失

    2. 同步和异步的区别仅仅在于是否有缓存池

    3. 大数据流用Flowable,小数据流用Observable
    4. 响应式拉取
    5. request当成下游处理事件的能力, 下游能处理几个就告诉上游我要几个
    6. Flowable里默认有一个大小为128的水缸, 当上下游工作在不同的线程中时, 上游就会先把事件发送到这个水缸中
    7. BackpressureStrategy.BUFFER无大小限制
    8. BackpressureStrategy.ERROR会在出现上下游流速不均衡的时候直接抛出MissingBackpressureException异常
    9. BackpressureStrategy.DROP直接把存不下的事件丢弃
    10. BackpressureStrategy.LATEST只保留最新的事件
    11. 不是自己创建的Flowable:
      1) onBackpressureBuffer()
      2) onBackpressureDrop()
      3) onBackpressureLatest()
    12. 当上下游在同一个线程中的时候,在下游调用request(n)就会直接改变上游中的requested的值,多次调用便会叠加这个值,而上游每发送一个事件之后便会去减少这个值,当这个值减少至0的时候,继续发送事件便会抛异常了
    13. 当上下游工作在不同的线程里时,每一个线程里都有一个requested,而我们调用request()时,实际上改变的是下游主线程中的requested,而上游中的requested的值是由RxJava内部调用request(n)去设置的,这个调用会在合适的时候自动触发
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() {}
                })
    }
}

猜你喜欢

转载自blog.csdn.net/johnwcheung/article/details/79593849