RxJava 2.x 实战系列(一):RxJava 简介

1. 什么是响应式编程?

答:

  • 响应式编程,Reactive Programming, 简称 RP
  • 在计算机中,响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
  • 传统的编程方式是顺序执行的,需要等待直至完成上一个任务之后才会执行下一个任务。无论是提升机器的性能还是代码的性能,本质上都需要依赖上一个任务的完成。如果需要响应迅速,就得把同步执行的方式换成异步执行,方法执行变成消息发送。这就是异步编程的方式,它是响应式编程的重要特征之一。

2. 响应式编程的特点?

答:

  • 异步编程:

    • 提供了合适的异步编程模型,能够挖掘多核 CPU 的能力、提高效率、降低延迟和阻塞等。
  • 数据流:

    • 基于数据流模型,响应式编程提供一套统一的 Stream 风格的数据处理接口。与 Java 8 中的 Stream 相比,响应式编程除了支持静态数据流,还支持动态数据流,并且允许复用和同时接入多个订阅者。
  • 变化传播:

    • 简单来说就是以一个数据流为输入,经过一连串操作转化为另一个数据流,然后分发给各个订阅者的过程。这就有点像函数式编程中的组合函数,将多个函数串联起来,把一组输入数据转化为格式迥异的输出数据。

3. 响应式编程的应用?

答:

  • 响应式编程在用户界面编程领域及基于实时系统的动画方面都有广泛的应用。
  • 在处理嵌套回调的异步事件、复杂的列表过滤和变换的时候也都有良好的表现。

4. 什么是函数式编程?

答:

  • 函数式编程,Funtional Programming,简称 FP
  • 在函数式编程中,由于数据是不可变的(immutable),因此没有并发编程的问题,是线程安全的。
  • 它将计算机运算看作数学中函数的计算,主要特点是将计算过程分解为多个可复用的函数,并且避免了状态及变量的概念。
  • 函数式编程虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。

5. 函数式编程的特点?

答:

  • 函数是“第一等公民”:

    • 所谓“第一等公民”(First Class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
  • 闭包和高阶函数:

    • 闭包是起函数的作用并可以像对象一样操作的对象。与此类似,FP 语言支持高阶函数。高阶函数可以用另一个函数(间接地,用一个表达式)作为输入参数,在某些情况下,它甚至返回一个函数作为输出参数。这两种结构结合在一起,即可以用优雅的方式进行模块化编程,这是使用 FP 的最大好处。
  • 递归:

    • 把递归作为控制流程的机制。例如,在 Haskell 的世界中,没有变量赋值和流程跳转,如果要实现一些简单的功能,比如求一个数组中的最大值,则需要借助递归来实现。
  • 惰性求值(Lazy Evaluation):

    • 它表示为“延迟求值”和“最小化求值”。惰性求值使得代码具备了巨大的优化潜能。支持惰性求值的编译器会像数学家看待代数表达式那样看待函数式编程的程序,即抵消相同项从而避免执行无谓的代码,安排代码执行顺序从而实现更高的执行效率甚至是减少错误。惰性求值另一个重要的好处是它可以构造一个无限的数据类型,无须担心由无穷计算所导致的内存溢出错误。
  • 没有“副作用”(Side Effect):

    • 指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。函数式编程强调没有“副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。

6. 什么是函数响应式编程?

答:

  • 函数响应式编程结合了函数式和响应式的优点,把函数范式里的一套思路和响应式编程结合起来就是函数响应式编程。
  • 传统的面向对象编程是通过抽象出的对象关系来解决问题,函数式编程是通过函数(function)的组合来解决问题,响应式编程是通过函数式编程的方式来解决回调地狱(Callback Hell)的问题。
  • 用传统的面向对象来处理异步事件不是很直观,处理并发也十分麻烦,所有才产生了函数响应式编程。

7. RxJava 产生的由来?

答:

  • RxJavaReactive ExtensionsJava 实现,用于通过使用 Observable/Flowable 序列来构建异步和基于事件的程序的库。
  • RxJava 扩展观察者模式以支持数据/事件序列,并添加允许你以声明方式组合序列的操作符,同时提取对低优先级的线程、同步、线程安全性和并发数据结构等问题的隐藏。

8. 什么是 Rx?

答:

  • ReactiveXReactive Extensions 的缩写,一般简写为 Rx。最初是 LINQ 的一个扩展,由微软架构师 Erik Meijer 领导的团队所开发,于 201211 月开源。
  • Rx 是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便地处理异步数据流,Rx 库支持 .NET、JavaScript 和 C++
  • Rx 的大部分语音库由 ReactiveX 这个组织负责维护,比较流行的有 RxJavaRxJSRx.NET,社区网站是 http://reactivex.io/

9. ReactiveX 的历史?

答:

  • 微软给的定义是,Rx 是一个函数库,让开发者可以利用可观察序列和 LINQ 风格查询操作符来编写异步和基于事件的程序。通过使用 Rx,开发者可以通过利用 Observables 表示异步数据流,用 LINQ 操作符查询异步数据流,用 Schedulers 参数化异步数据流的并发处理。可以这样定义 Rx: Rx=Observables + LINQ + Schedulers
  • RxJava 2.x 中,Observables 包含了 ObservablesFlowableSingleMaybeCompletable 等。
  • ReactiveX.io 给的定义是,Rx 是一个使用可观察数据流进行异步编程的编程接口,ReactiveX 结合了观察者模式、迭代器模式和函数式编程的精华。

10. 怎样理解 Rx 模式?

答:

  • 使用观察者模式:

    • 创建:Rx 可以方便地创建事件流和数据流。
    • 组合:Rx 使用查询式的操作符组合和变换数据流。
    • 监听:Rx 可以订阅任何可观察的数据流并执行操作。
  • 简化代码:

    • 函数式风格:对可观察的数据流使用无副作用的输入/输出函数,避免了程序里错综复杂的状态。
    • 简化代码:Rx 的操作符通常可以将复杂的难题简化为很少的几行代码。
    • 异步错误处理:传统的 try/catch 没办法处理异步计算,Rx 提供了合适的错误处理机制。
    • 轻松使用并发:RxObservables (包括 ObservableFlowableSingleCompletableMaybe)和 Schedulers 可以让开发者摆脱底层的线程同步和各种并发问题。

11. 【笔试题】某 APP 从服务端获取酒店列表,并将价格大于等于 500 的房间全部列出来展示在界面上?

答:

  • 传统写法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    new Thread() {
    @Override
    public void run() {
    super.run();
    // 从服务端获取酒店列表
    List<Hotel> hotels = getHotelsFromServer();
    for(Hotel hotel : hotels) {
    List<Room> rooms = hotel.rooms;
    for(Room room : rooms) {
    if(room.price >= 500) {
    runOnUiThread(
    new Runnable() {
    @Override
    public void run() {
    // 将房间的信息添加到 UI 上
    displayUI(room);
    }
    });
    }
    }
    }
    }
    }.start();
  • 使用 RxJava:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    Observable.fromIterable(getHotelsFromServer())
    .flatMap(new Function<Hotel, Observable<Room>> () {
    @Override
    public Observable<Room> apply(Hotel hotel) {
    return Observable.fromIterable(hotel.rooms);
    }
    })
    .filter(new Predicate<Room>() {
    @Override
    public boolean test(@NonNull Room room) throws Exception {
    return room.price >= 500;
    }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Consumer<Room>() {
    @Override
    public void accept(Room room) {
    // 将房间的信息添加到屏幕上
    displayUI(room);
    }
    });
  • RxJava + Lambda:

    1
    2
    3
    4
    5
    6
    Observable.fromIterable(getHotelsFromServer())
    .flatMap(hotel -> Observable.fromIterable(hotel.rooms))
    .filter(room -> room.price >= 500)
    .subscribOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread()) // 该行和上面一行还能用 compose 操作符和 Transformer 一起封装
    .subscribe(this::displayUI);

12. 【笔试题】写一个 RxJava 版的 Hello World?

答:

  • RxJava 版:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Observable.create(new ObservableOnSubscribe<String> () {
    @Override
    public void subscribe(@NonNull ObservableEmitter<String> e) throws Exception {
    e.onNext("Hello World");
    }
    }).subscribe(new Consumer<String>() {
    @Override
    public void accept(@NonNull String s) throws Exception {
    System.out.println(s);
    }
    });
  • 简化的 RxJava 版:

    1
    2
    3
    4
    5
    6
    Observable.just("Hello World").subscribe(new Consumer<String> () {
    @Override
    public void accept(@NonNull String s) throws Exception {
    System.out.println(s);
    }
    });
  • RxJava + Lambda:

    1
    Observable.just("Hello World").subscribe(System.out::println); // System.out.println 是方法引用,简化版的 Lambda 表达式,是 Java 8 的特性
-------------本文结束感谢您的阅读-------------