├── RxJava系列教程 ├── 22.Google Agera 从入门到放弃.md ├── images │ ├── 聚合_01.png │ ├── 聚合_02.png │ ├── 聚合_03.png │ ├── 聚合_04.png │ ├── 聚合_05.png │ ├── monad_01.png │ ├── monad_02.png │ ├── monad_03.png │ ├── 时间平移_01.png │ ├── 时间平移_02.png │ ├── 时间平移_03.png │ ├── 时间平移_04.png │ ├── 时间平移_05.png │ ├── 时间平移_06.png │ ├── 时间平移_07.png │ ├── 时间平移_08.png │ ├── 时间平移_09.png │ ├── 时间平移_10.png │ ├── 时间平移_11.png │ ├── 时间平移_12.png │ ├── 时间平移_13.png │ ├── 时间平移_14.png │ ├── 时间平移_15.png │ ├── 时间平移_16.png │ ├── 时间平移_17.png │ ├── 时间平移_18.png │ ├── 时间平移_19.png │ ├── 时间平移_20.png │ ├── 时间平移_21.png │ ├── 时间平移_22.png │ ├── 检查数据_01.png │ ├── 检查数据_02.png │ ├── 检查数据_03.png │ ├── 检查数据_04.png │ ├── 检查数据_05.png │ ├── 组合数据流_01.png │ ├── 组合数据流_02.png │ ├── 组合数据流_03.png │ ├── 组合数据流_04.png │ ├── 组合数据流_05.png │ ├── 组合数据流_06.png │ ├── 组合数据流_07.png │ ├── 组合数据流_08.png │ ├── 组合数据流_09.png │ ├── 组合数据流_10.png │ ├── 自定义操作函数.png │ ├── 转换数据流_01.png │ ├── 转换数据流_02.png │ ├── 转换数据流_03.png │ ├── 过滤数据_01.png │ ├── 过滤数据_02.png │ ├── 过滤数据_03.png │ ├── 过滤数据_04.png │ ├── 过滤数据_05.png │ ├── 过滤数据_06.png │ ├── 过滤数据_07.png │ ├── 过滤数据_08.png │ ├── 意外情况处理_01.png │ ├── 意外情况处理_02.png │ ├── 意外情况处理_03.png │ ├── 意外情况处理_04.png │ ├── 意外情况处理_05.png │ ├── 高级错误处理_01.png │ ├── 高级错误处理_02.png │ ├── 高级错误处理_03.png │ ├── Backpressure_01.png │ ├── Backpressure_02.png │ ├── Backpressure_03.png │ ├── Backpressure_04.png │ ├── hot & cold Observable_01.png │ ├── hot & cold Observable_02.png │ └── hot & cold Observable_03.png ├── 23.RxJava 参考资源.md ├── 0.Intro To RxJava 系列教程.md ├── 1.RxJava 教程第一部分:入门之 Why Rx.md ├── README.md ├── 27.RxJava Android 开发全家桶.md ├── 21.Intro To RxJava 系列教程 总结.md ├── 13.Android 专用响应式编程框架 — Agera.md ├── 26.RxJava 常见的错误用法.md ├── 3.RxJava 教程第一部分:入门之 生命周期管理.md ├── 24.RxJava 的 compose 操作函数实战.md ├── 30.RxJava 前传 3.md ├── 28.RxJava 前传 1.md ├── 18.RxJava 教程第四部分:并发 之测试.md ├── 11.RxJava 教程第三部分:驯服数据流之 高级错误处理.md ├── 2.RxJava 教程第一部分:入门之 关键的类.md ├── 25.不该使用 RxJava 的一些情况.md ├── 19.RxJava 教程第四部分:并发 之意外情况处理.md ├── 9.RxJava 教程第三部分:驯服数据流之副作用.md ├── 6.RxJava 教程第二部分:事件流基础之 检查数据.md ├── 10.RxJava 教程第三部分:驯服数据流之 避免 monad.md ├── 4.RxJava 教程第二部分:事件流基础之 创建事件流.md ├── 15.RxJava 教程第三部分:驯服数据流之 hot & cold Observable.md └── 17.RxJava 教程第四部分:并发 之线程调度.md ├── assets ├── rxjava_vedio.png ├── rxjava_vedio_2.png └── rxjava_vedio_3.png ├── RxJava1.0 ├── images │ ├── RxJava.png │ ├── RxJava_01.png │ ├── RxJava_02.png │ ├── RxJava_03.png │ ├── RxJava_04.jpg │ ├── RxJava_05.png │ ├── RxJava_06.gif │ ├── RxJava_07.png │ ├── RxJava_08.png │ ├── RxJava_09.png │ ├── RxJava_10.png │ ├── RxJava_11.gif │ ├── RxJava_12.png │ ├── RxJava_13.png │ ├── RxJava_14.png │ ├── RxJava_15.png │ └── RxJava_16.jpg └── Rxjava操作符大全.md ├── RxJava2.0 ├── images │ ├── RxJava2_01.png │ ├── RxJava2_02.png │ ├── RxJava2_03.png │ ├── RxJava2_04.png │ ├── RxJava2_05.png │ ├── RxJava2_06.png │ ├── RxJava2_07.png │ ├── RxJava2_08.png │ ├── RxJava2_09.png │ ├── RxJava2_10.png │ ├── RxJava2_11.png │ ├── RxJava2_12.png │ ├── RxJava2_13.gif │ ├── RxJava2_14.png │ ├── RxJava2_15.gif │ ├── RxJava2_16.gif │ ├── RxJava2_17.gif │ ├── RxJava2_18.png │ ├── RxJava2_19.png │ ├── RxJava2_20.gif │ ├── RxJava2_21.gif │ ├── RxJava2_22.gif │ ├── RxJava2_23.gif │ ├── RxJava2_24.gif │ ├── RxJava2_25.gif │ ├── RxJava2_25.png │ ├── RxJava2_26.gif │ ├── RxJava2_27.gif │ ├── RxJava2_28.gif │ ├── RxJava2_29.gif │ ├── RxJava2_30.gif │ ├── RxJava2_31.png │ ├── RxJava2_32.png │ ├── RxJava2_33.jpg │ ├── RxJava2_34.png │ └── RxJava2_35.gif ├── 给初学者的RxJava2.0教程-6.md ├── 给初学者的RxJava2.0教程-3.md ├── 给初学者的RxJava2.0教程-5.md └── 给初学者的RxJava2.0教程-1.md ├── book.json ├── .gitignore ├── node.md ├── SUMMARY.md └── README.md /RxJava系列教程/22.Google Agera 从入门到放弃.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/rxjava_vedio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/assets/rxjava_vedio.png -------------------------------------------------------------------------------- /assets/rxjava_vedio_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/assets/rxjava_vedio_2.png -------------------------------------------------------------------------------- /assets/rxjava_vedio_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/assets/rxjava_vedio_3.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava.png -------------------------------------------------------------------------------- /RxJava系列教程/images/聚合_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/聚合_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/聚合_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/聚合_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/聚合_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/聚合_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/聚合_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/聚合_04.png -------------------------------------------------------------------------------- /RxJava系列教程/images/聚合_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/聚合_05.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_01.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_02.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_03.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_04.jpg -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_05.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_06.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_06.gif -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_07.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_08.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_09.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_10.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_11.gif -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_12.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_13.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_14.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_15.png -------------------------------------------------------------------------------- /RxJava1.0/images/RxJava_16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava1.0/images/RxJava_16.jpg -------------------------------------------------------------------------------- /RxJava系列教程/images/monad_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/monad_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/monad_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/monad_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/monad_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/monad_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_04.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_05.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_06.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_07.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_08.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_09.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_10.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_11.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_12.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_13.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_14.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_15.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_16.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_17.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_18.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_19.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_20.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_21.png -------------------------------------------------------------------------------- /RxJava系列教程/images/时间平移_22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/时间平移_22.png -------------------------------------------------------------------------------- /RxJava系列教程/images/检查数据_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/检查数据_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/检查数据_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/检查数据_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/检查数据_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/检查数据_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/检查数据_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/检查数据_04.png -------------------------------------------------------------------------------- /RxJava系列教程/images/检查数据_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/检查数据_05.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_04.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_05.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_06.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_07.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_08.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_09.png -------------------------------------------------------------------------------- /RxJava系列教程/images/组合数据流_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/组合数据流_10.png -------------------------------------------------------------------------------- /RxJava系列教程/images/自定义操作函数.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/自定义操作函数.png -------------------------------------------------------------------------------- /RxJava系列教程/images/转换数据流_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/转换数据流_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/转换数据流_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/转换数据流_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/转换数据流_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/转换数据流_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/过滤数据_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/过滤数据_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/过滤数据_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/过滤数据_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/过滤数据_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/过滤数据_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/过滤数据_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/过滤数据_04.png -------------------------------------------------------------------------------- /RxJava系列教程/images/过滤数据_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/过滤数据_05.png -------------------------------------------------------------------------------- /RxJava系列教程/images/过滤数据_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/过滤数据_06.png -------------------------------------------------------------------------------- /RxJava系列教程/images/过滤数据_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/过滤数据_07.png -------------------------------------------------------------------------------- /RxJava系列教程/images/过滤数据_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/过滤数据_08.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_01.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_02.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_03.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_04.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_05.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_06.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_07.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_08.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_09.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_10.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_11.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_12.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_13.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_13.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_14.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_15.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_15.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_16.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_17.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_17.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_18.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_19.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_20.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_20.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_21.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_21.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_22.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_22.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_23.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_23.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_24.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_24.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_25.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_25.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_25.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_26.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_26.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_27.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_27.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_28.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_28.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_29.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_29.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_30.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_30.gif -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_31.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_32.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_33.jpg -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_34.png -------------------------------------------------------------------------------- /RxJava2.0/images/RxJava2_35.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava2.0/images/RxJava2_35.gif -------------------------------------------------------------------------------- /RxJava系列教程/images/意外情况处理_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/意外情况处理_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/意外情况处理_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/意外情况处理_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/意外情况处理_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/意外情况处理_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/意外情况处理_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/意外情况处理_04.png -------------------------------------------------------------------------------- /RxJava系列教程/images/意外情况处理_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/意外情况处理_05.png -------------------------------------------------------------------------------- /RxJava系列教程/images/高级错误处理_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/高级错误处理_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/高级错误处理_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/高级错误处理_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/高级错误处理_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/高级错误处理_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/Backpressure_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/Backpressure_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/Backpressure_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/Backpressure_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/Backpressure_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/Backpressure_03.png -------------------------------------------------------------------------------- /RxJava系列教程/images/Backpressure_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/Backpressure_04.png -------------------------------------------------------------------------------- /RxJava系列教程/images/hot & cold Observable_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/hot & cold Observable_01.png -------------------------------------------------------------------------------- /RxJava系列教程/images/hot & cold Observable_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/hot & cold Observable_02.png -------------------------------------------------------------------------------- /RxJava系列教程/images/hot & cold Observable_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackChan1999/RxJava_Docs_For_Android_Developer/HEAD/RxJava系列教程/images/hot & cold Observable_03.png -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流,现在已经支持几乎全部的流行编程语言了", 3 | "title" : "给 Android 开发者的 RxJava 详解", 4 | "language": "zh-hans", 5 | "author" : "AllenIverson", 6 | "gitbook": "3.2.3", 7 | "pdf": { 8 | "fontFamily": "等线" 9 | } 10 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf -------------------------------------------------------------------------------- /RxJava系列教程/23.RxJava 参考资源.md: -------------------------------------------------------------------------------- 1 | ## RxJava 参考资源 2 | 3 | 通过弹子图(Marbles)可以帮助理解 RxJava 中的各种操作函数。通过实例代码可以加深了解每个操作函数。 下面是几个非常好的学习 Rxjava 的工具: 4 | 5 | 6 | 7 | 这个网站通过交互图形介绍了很多 RxJava 中的操作函数。选择要查看的操作函数,然后显示该函数的 Marble 图,该图是可以交互的,可以用鼠标拖动每个数据的位置,查看输出的结果的变化。对于理解 RxJava 的操作函数非常有帮助,看起来也非常直观。 8 | 9 | [rxmarbles](https://play.google.com/store/apps/details?id=com.moonfleet.rxmarbles) 10 | 11 | rxmarbles.com 网站的 Android 应用。在手机上也可以方便的随时随地查看每个操作函数的图形介绍。 12 | 13 | [RxJava-Android-Samples](https://github.com/kaushikgopal/RxJava-Android-Samples) 14 | 15 | 这个位于 Github 的 RxJava Android示例项目中使用真实项目中的很多实用场景来演示了 RxJava 的各种常用方式。 里面包含和 Retrofit 、Volley 集成;以及如何用 RxJava 来当做 EventBus 使用;还有线程调度等很多常用的案例。 -------------------------------------------------------------------------------- /RxJava系列教程/0.Intro To RxJava 系列教程.md: -------------------------------------------------------------------------------- 1 | ## Intro To RxJava 系列教程 2 | 3 | 本系列教程根据 Rx.NET 的经典入门教程 [IntroToRx](http://www.introtorx.com/) 改编而来,用 RxJava 中的实现来介绍 reactive programmer 的理念和使用方式,方便 Java 开发者了解和学习 RxJava。 本教程原文位于 [Github](https://github.com/Froussios/Intro-To-RxJava)。 本系列文章从头到尾详细解释了 RxJava 的使用场景和使用方式以及高级使用技巧。在阅读本教程之前你不需要对 reactive 或者 functional programming 有任何了解,只需要了解 Java 就可以看懂本教程中的内容,帮助你进入 RxJava 的世界,学会使用 RxJava。 4 | 5 | #### 本系列教程的结构 6 | 7 | 本系列的结构是从入门到精通,所以需要从头到尾逐章阅读。并且每个部分都是独立的,所以有经验的同学也可以参考每章的内容。 8 | 9 | 文章中的代码也可以从 [Github](https://github.com/goodev/Intro-To-RxJava/blob/master/tests/java/itrx) 直接下载并允许。建议在第一次阅读的时候直接看文章中的代码即可,如有需要可以单独下载代码进一步学习研究。 10 | 11 | RxJava 本身是针对一个数据流(事件流)做各种操作,每当有新的事件发生,则会通知到 Observable 上订阅的 Subscriber 对象。 12 | 13 | #### Java 8 Lambda 14 | 15 | 为了排版和代码的简洁性,代码中使用了 Java 8 的 Lambda 表达式,如果对 Lambda 不了解的同学可以先参考 [掌握 Java 8 Lambda](http://blog.chengyunfeng.com/?p=902) 一文。 16 | -------------------------------------------------------------------------------- /node.md: -------------------------------------------------------------------------------- 1 | ## RxJava 1.0 2 | 3 | 观察者模式 + Lambda + Stream Api(操作符)+ 调度器 4 | 5 | ![](RxJava1.0/images/RxJava.png) 6 | 7 | - Observable 8 | 9 | - create() 10 | - just() 11 | - from() 12 | - subscribe() 13 | - subscribeOn() 14 | - observeOn() 15 | - doOnSubscribe() 16 | - Observer 17 | 18 | - onCompleted() 19 | - onNext() 20 | - onError() 21 | - Subscriber 22 | - onStart() 23 | - request() 24 | - Subscription 25 | 26 | - unsubscribe() 27 | - isUnsubscribed() 28 | - Action 29 | - Fun 30 | - Schedulers 31 | 32 | - io() 33 | - immediate() 34 | - newThread() 35 | - computation() 36 | - AndroidSchedulers 37 | - mainThread() 38 | 39 | ## Operator 40 | 41 | ## RxJava 2.0 42 | 43 | - Observable 44 | - fromIterable() 45 | - delay() 46 | 47 | 48 | - ObservableSource 49 | - Disposable 50 | - dispose() 51 | 52 | 53 | - CompositeDisposable 54 | - add() 55 | - clear() 56 | - BiFunction 57 | - Consumer 58 | - Flowable 59 | - Backpressure 60 | - BackpressureStrategy 61 | - ERROR 62 | - BUFFER 63 | - DROP 64 | - LATEST 65 | 66 | ### 操作符 67 | 68 | - map() 一对一变换 69 | - flatMap() 扁平化 70 | - concatMap() 有序 71 | - Zip() -------------------------------------------------------------------------------- /RxJava系列教程/1.RxJava 教程第一部分:入门之 Why Rx.md: -------------------------------------------------------------------------------- 1 | ## 1. 入门之 Why Rx 2 | 3 | > 用户希望查看实时的数据,他们现在就要看最新的微博;他们的订单立刻就被确认;价格需要现在定下;在线游戏需要及时响应。作为码农,你需要发送并忘记的( fire-and-forget )消息。你不希望发送一个消息然后阻塞等待结果返回。当结果返回的时候,你希望结果推送给你。当处理的结果是一个集合数据的时候,你更希望逐个的接收到每个结果;你不希望等待整个结果集合都完成后一起返回。世界已经进步到推送的情景了;用户期待我们能跟上他们的步伐。码农有工具来推送数据了,推送数据很简单。码农需要工具来响应推送的数据。 4 | 5 | 欢迎来到 RxJava 系列教程,RxJava 是由大名鼎鼎的 Netflix 公司在开发软件过程中应用 Rx.NET 思想解决实际问题,并最总提炼出来的一套应用于 Java 的 Rx 框架。Rx 是一个优雅解决问题的框架,functional programmer 对该框架比较熟悉。Rx 有如下一些优点: 6 | 7 | - 统一性(Unitive) 8 | 9 | Rx 中的查询是使用和 functional programming 通用的风格实现的,例如前面介绍的 [Java 8 中的 Streams 和 Lambda表达式](http://blog.chengyunfeng.com/?p=902)。在 Rx 里面,可以在事件上使用 functional 风格的转换操作。 10 | 11 | - 扩展性(Extensible) 12 | 13 | RxJava 可以自定义操作函数。虽然 Java 的语法现在导致自定义操作函数有点麻烦。RxJava 提供了所有的可扩展性来支持其他运行 Jvm 上语音的支持,比如 kotlin、scale 等。 14 | 15 | - 描述性(Declarative) 16 | 17 | Functional transformations are read in a declarative way. 18 | 19 | - 组合性(Composable) 20 | 21 | Rx 的操作函数可以组合起来形成更加复杂的操作 22 | 23 | - 转换性(Transformative) 24 | 25 | Rx 操作函数可以把一个类型的数据转换为另外一种需要的类型,根据需要还可以过滤、映射、扩展数据流。 26 | 27 | ### 何时应该使用 Rx? 28 | 29 | Rx 适合组合和消耗一系列的事件(数据)。下面是一些使用 Rx 的场景: 30 | 31 | #### 应当使用 Rx 32 | 33 | - UI 事件响应,例如 鼠标移动、按钮点击事件 34 | - Domain 事件,例如 属性改变、集合更新、订单提交、注册成功 等 35 | - 基础架构事件,例如 文件监视、系统事件 等 36 | - 集成事件,例如从一个消息队列来的一个广播;来自于 WebSockets API 的推送事件 等 37 | - CEP 集成事件,例如 [StreamInsight](https://blogs.msdn.microsoft.com/streaminsight/2012/11/15/what-is-streaminsight-a-primer-for-non-programmers/) 或者 StreamBase. 38 | 39 | #### 可以使用 Rx 40 | 41 | - Future 的结果或者类似的模式 42 | 这些模式已经工作的很好的,使用 Rx 在这些情况下对开发过程并不会有太多的提升。 43 | 44 | #### 不要使用 Rx 45 | 46 | - 把 iterables 变成 observables, 只是为了在 Rx 库中使用它们。 -------------------------------------------------------------------------------- /RxJava系列教程/README.md: -------------------------------------------------------------------------------- 1 | ## RxJava系列教程 2 | 3 | * [Intro To RxJava 系列教程](0.Intro To RxJava 系列教程.md) 4 | * [入门之 Why Rx](1.RxJava 教程第一部分:入门之 Why Rx.md) 5 | * [入门之 关键的类](2.RxJava 教程第一部分:入门之 关键的类.md) 6 | * [入门之 生命周期管理](3.RxJava 教程第一部分:入门之 生命周期管理.md) 7 | * [RxJava 前传 1](28.RxJava 前传 1.md) 8 | * [RxJava 前传 2](29.RxJava 前传 2.md) 9 | * [RxJava 前传 3](30.RxJava 前传 3.md) 10 | * [事件流基础之 创建事件流](4.RxJava 教程第二部分:事件流基础之 创建事件流.md) 11 | * [事件流基础之 过滤数据](5.RxJava 教程第二部分:事件流基础之 过滤数据.md) 12 | * [事件流基础之 检查数据](6.RxJava 教程第二部分:事件流基础之 检查数据.md) 13 | * [事件流基础之 聚合](7.RxJava 教程第二部分:事件流基础之 聚合.md) 14 | * [事件流基础之 转换数据流](8.RxJava 教程第二部分:事件流基础之 转换数据流.md) 15 | * [驯服数据流之副作用](9.RxJava 教程第三部分:驯服数据流之副作用.md) 16 | * [驯服数据流之 避免 monad](10.RxJava 教程第三部分:驯服数据流之 避免 monad.md) 17 | * [驯服数据流之 高级错误处理](11.RxJava 教程第三部分:驯服数据流之 高级错误处理.md) 18 | * [驯服数据流之 组合数据流](12.RxJava 教程第三部分:驯服数据流之 组合数据流.md) 19 | * [Android 专用响应式编程框架 — Agera](13.Android 专用响应式编程框架 — Agera.md) 20 | * [驯服数据流之 时间平移](14.RxJava 教程第三部分:驯服数据流之 时间平移.md) 21 | * [驯服数据流之 hot & cold Observable](15.RxJava 教程第三部分:驯服数据流之 hot & cold Observable.md) 22 | * [驯服数据流之自定义操作函数](16.RxJava 教程第三部分:驯服数据流之自定义操作函数.md) 23 | * [并发 之线程调度](17.RxJava 教程第四部分:并发 之线程调度.md) 24 | * [并发 之测试](18.RxJava 教程第四部分:并发 之测试.md) 25 | * [并发 之意外情况处理](19.RxJava 教程第四部分:并发 之意外情况处理.md) 26 | * [并发 之数据流发射太快如何办](20.RxJava 教程第四部分:并发 之数据流发射太快如何办.md) 27 | * [Intro To RxJava 系列教程 总结](21.Intro To RxJava 系列教程 总结.md) 28 | * [Google Agera 从入门到放弃](22.Google Agera 从入门到放弃.md) 29 | * [RxJava 参考资源](23.RxJava 参考资源.md) 30 | * [RxJava 的 compose() 操作函数实战](24.RxJava 的 compose 操作函数实战.md) 31 | * [不该使用 RxJava 的一些情况](25.不该使用 RxJava 的一些情况.md) 32 | * [RxJava 常见的错误用法](26.RxJava 常见的错误用法.md) 33 | * [RxJava Android 开发全家桶](27.RxJava Android 开发全家桶.md) 34 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [关于](README.md) 4 | * [给 Android 开发者的 RxJava 详解](RxJava1.0/给Android开发者的RxJava详解.md) 5 | * [RxJava 与 Retrofit 结合的最佳实践](RxJava1.0/RxJava与Retrofit结合的最佳实践.md) 6 | * [给初学者的RxJava2.0教程-1](RxJava2.0/给初学者的RxJava2.0教程-1.md) 7 | * [给初学者的RxJava2.0教程-2](RxJava2.0/给初学者的RxJava2.0教程-2.md) 8 | * [给初学者的RxJava2.0教程-3](RxJava2.0/给初学者的RxJava2.0教程-3.md) 9 | * [给初学者的RxJava2.0教程-4](RxJava2.0/给初学者的RxJava2.0教程-4.md) 10 | * [给初学者的RxJava2.0教程-5](RxJava2.0/给初学者的RxJava2.0教程-5.md) 11 | * [给初学者的RxJava2.0教程-6](RxJava2.0/给初学者的RxJava2.0教程-6.md) 12 | * [给初学者的RxJava2.0教程-7](RxJava2.0/给初学者的RxJava2.0教程-7.md) 13 | * [给初学者的RxJava2.0教程-8](RxJava2.0/给初学者的RxJava2.0教程-8.md) 14 | * [给初学者的RxJava2.0教程-9](RxJava2.0/给初学者的RxJava2.0教程-9.md) 15 | * [RxJava系列教程](RxJava系列教程/README.md) 16 | * [Intro To RxJava 系列教程](RxJava系列教程/0.Intro To RxJava 系列教程.md) 17 | * [入门之 Why Rx](RxJava系列教程/1.RxJava 教程第一部分:入门之 Why Rx.md) 18 | * [入门之 关键的类](RxJava系列教程/2.RxJava 教程第一部分:入门之 关键的类.md) 19 | * [入门之 生命周期管理](RxJava系列教程/3.RxJava 教程第一部分:入门之 生命周期管理.md) 20 | * [RxJava 前传 1](RxJava系列教程\28.RxJava 前传 1.md) 21 | * [RxJava 前传 2](RxJava系列教程\29.RxJava 前传 2.md) 22 | * [RxJava 前传 3](RxJava系列教程\30.RxJava 前传 3.md) 23 | * [事件流基础之 创建事件流](RxJava系列教程/4.RxJava 教程第二部分:事件流基础之 创建事件流.md) 24 | * [事件流基础之 过滤数据](RxJava系列教程/5.RxJava 教程第二部分:事件流基础之 过滤数据.md) 25 | * [事件流基础之 检查数据](RxJava系列教程/6.RxJava 教程第二部分:事件流基础之 检查数据.md) 26 | * [事件流基础之 聚合](RxJava系列教程/7.RxJava 教程第二部分:事件流基础之 聚合.md) 27 | * [事件流基础之 转换数据流](RxJava系列教程/8.RxJava 教程第二部分:事件流基础之 转换数据流.md) 28 | * [驯服数据流之副作用](RxJava系列教程/9.RxJava 教程第三部分:驯服数据流之副作用.md) 29 | * [驯服数据流之 避免 monad](RxJava系列教程/10.RxJava 教程第三部分:驯服数据流之 避免 monad.md) 30 | * [驯服数据流之 高级错误处理](RxJava系列教程/11.RxJava 教程第三部分:驯服数据流之 高级错误处理.md) 31 | * [驯服数据流之 组合数据流](RxJava系列教程/12.RxJava 教程第三部分:驯服数据流之 组合数据流.md) 32 | * [Android 专用响应式编程框架 — Agera](RxJava系列教程/13.Android 专用响应式编程框架 — Agera.md) 33 | * [驯服数据流之 时间平移](RxJava系列教程/14.RxJava 教程第三部分:驯服数据流之 时间平移.md) 34 | * [驯服数据流之 hot & cold Observable](RxJava系列教程/15.RxJava 教程第三部分:驯服数据流之 hot & cold Observable.md) 35 | * [驯服数据流之自定义操作函数](RxJava系列教程/16.RxJava 教程第三部分:驯服数据流之自定义操作函数.md) 36 | * [并发 之线程调度](RxJava系列教程/17.RxJava 教程第四部分:并发 之线程调度.md) 37 | * [并发 之测试](RxJava系列教程/18.RxJava 教程第四部分:并发 之测试.md) 38 | * [并发 之意外情况处理](RxJava系列教程/19.RxJava 教程第四部分:并发 之意外情况处理.md) 39 | * [并发 之数据流发射太快如何办](RxJava系列教程/20.RxJava 教程第四部分:并发 之数据流发射太快如何办.md) 40 | * [Intro To RxJava 系列教程 总结](RxJava系列教程/21.Intro To RxJava 系列教程 总结.md) 41 | * [Google Agera 从入门到放弃](RxJava系列教程/22.Google Agera 从入门到放弃.md) 42 | * [RxJava 参考资源](RxJava系列教程/23.RxJava 参考资源.md) 43 | * [RxJava 的 compose() 操作函数实战](RxJava系列教程/24.RxJava 的 compose 操作函数实战.md) 44 | * [不该使用 RxJava 的一些情况](RxJava系列教程/25.不该使用 RxJava 的一些情况.md) 45 | * [RxJava 常见的错误用法](RxJava系列教程/26.RxJava 常见的错误用法.md) 46 | * [RxJava Android 开发全家桶](RxJava系列教程/27.RxJava Android 开发全家桶.md) 47 | * [Rxjava操作符大全](RxJava1.0/Rxjava操作符大全.md) -------------------------------------------------------------------------------- /RxJava系列教程/27.RxJava Android 开发全家桶.md: -------------------------------------------------------------------------------- 1 | ## 27. RxJava Android 开发全家桶 2 | 3 | [RxJava](http://blog.chengyunfeng.com/?p=983) 在 Android 应用开发中越来越流行,但是由于其门槛稍高,初次使用不免遇到很多问题,例如在 [RxJava 常见的错误用法](http://blog.chengyunfeng.com/?p=1010) 和 [不该使用 RxJava 的一些情况](http://blog.chengyunfeng.com/?p=1009) 中所描述的情况。为了避免这些常见的问题,很多民间高手开发了很多在 Android 应用中可以使用的 Rx 扩展类库,组合使用这些类库,可以更方便的使用 RxJava 并且可以避免一些常见的错误用法。 本文来介绍一些使用 RxJava 必备的扩展库。 4 | 5 | ### RxAndroid 6 | 7 | [RxAndroid](https://github.com/ReactiveX/RxAndroid) 这个就毫无疑问了, Android 开发中使用 RxJava 必备元素,虽然里面只是提供了简单的两个功能。 AndroidSchedulers.mainThread() 和 AndroidSchedulers.handlerThread(handler) ,但这确是 Android 开发中最核心的功能之一。 8 | 9 | ### RxBinding 10 | 11 | [RxBinding](https://github.com/JakeWharton/RxBinding) 是把 Android 中各种 UI 控件的事件转换为 RxJava 中的数据流。这样就可以把 UI 控件的事件当做 RxJava 中的数据流来使用了。 比如 View 的 onClick 事件,使用 RxView.clicks(view) 即可获取到一个 Observable 对象,每当用户点击这个 View 的时候,该 Observable 对象就发射一个事件(onNext 被调用), Observable 的 Observer 订阅者就可以通过 onNext 回调知道用户点击了 View。 12 | 13 | ### RxLifecycle 14 | 15 | [RxLifecycle](https://github.com/trello/RxLifecycle) 配合 Activity/Fragment 生命周期来管理订阅的。 由于 RxJava Observable 订阅后(调用 subscribe 函数),一般会在后台线程执行一些操作(比如访问网络请求数据),当后台操作返回后,调用 Observer 的 onNext 等函数,然后在 更新 UI 状态。 但是后台线程请求是需要时间的,如果用户点击刷新按钮请求新的微博信息,在刷新还没有完成的时候,用户退出了当前界面返回前面的界面,这个时候刷新的 Observable 如果不取消订阅,则会导致之前的 Activity 无法被 JVM 回收导致内存泄露。 这就是 Android 里面的生命周期管理需要注意的地方,RxLifecycle 就是用来干这事的。比如下面的示例: 16 | 17 | ```java 18 | myObservable 19 | .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) 20 | .subscribe(); 21 | ``` 22 | 23 | 在 Activity 销毁的时候, RxLifecycle 会自动取消订阅这个 Observer。 这样就不用自己手动管理了。 24 | 25 | ### Retrofit 26 | 27 | 现在几乎大部分的 Android 应用都需要请求网络获取数据,而 [Retrofit](http://blog.chengyunfeng.com/?p=491) 就是用来简化网络请求的一个库,并且支持 RxJava。比如: 28 | 29 | ```java 30 | @GET("/users/{user}") 31 | Observable user(@Path("user") String user); 32 | ``` 33 | 34 | 上面的代码定义了一个 GET 请求,请求的路径是 /users/{user}并且带有一个用户名的参数。 返回的结果为 一个 Observable 。 这样配合前面的 RxBinding,就可以很容易的实现一个 用户点击一个刷新按钮去请求服务器数据的操作。 35 | 36 | ```java 37 | RxView.clicks(view).flatMap(v -> githubService.user(user)).subscribe(); 38 | ``` 39 | ### SqlBrite 40 | 41 | 如果您的应用使用了 Sqlite 来保存数据的话,则 [SqlBrite](http://blog.chengyunfeng.com/?p=990) 是个很好的配合 RxJava 使用的库。 42 | 43 | 除了上面这些主要的类库外,还有一些封装其他 Android 服务的库: 44 | 45 | - [Rx Preferences](https://github.com/f2prateek/rx-preferences) 通过 RxJava 的方式来访问 SharedPreferences。 46 | - [RxPermissions](https://github.com/tbruyelle/RxPermissions) 用于支持 Android M 动态权限申请的库。 47 | 48 | 还有一些配合 Google Play Service 服务的库: 49 | 50 | - [RxFit](https://github.com/patloew/RxFit) 封装了 Fitness API 的调用。 51 | - [RxNotification](https://github.com/pucamafra/RxNotification) 封装了 firebase 通知 api。 52 | - [Android-ReactiveLocation](https://github.com/mcharmas/Android-ReactiveLocation) 封装了 Google Play Service API 中和位置相关的 api。 53 | 54 | 如果您要是开发 Android Wear 应用的话,会使用到 Wearable API 来实现手表和手机直接的数据通信,则可以使用 [RxWear](https://github.com/patloew/RxWear) 库。 55 | 56 | ### [RxBus](https://github.com/AndroidKnife/RxBus) 57 | 58 | Event Bus By RxJava -------------------------------------------------------------------------------- /RxJava系列教程/21.Intro To RxJava 系列教程 总结.md: -------------------------------------------------------------------------------- 1 | ## 21. Intro To RxJava 系列教程 总结 2 | 3 | Intro To RxJava 系列教程一共四个部分,第一部分介绍了为什么要使用 RxJava以及 RxJava的关键概念: 4 | 5 | - [RxJava 教程第一部分:入门之 Why Rx](1.RxJava 教程第一部分:入门之 Why Rx.md) 6 | - [RxJava 教程第一部分:入门之 关键的类](2.RxJava 教程第一部分:入门之 关键的类.md) 7 | - [RxJava 教程第一部分:入门之 生命周期管理](3.RxJava 教程第一部分:入门之 生命周期管理.md) 8 | 9 | 如果看完第一部分对于为什么要使用 RxJava 和 何时使用 RxJava 还不太明白的,可以参考 RxJava 前传: 10 | 11 | - [RxJava 前传 1](28.RxJava 前传 1.md) 12 | - [RxJava 前传 2](29.RxJava 前传 2.md) 13 | - [RxJava 前传 3](30.RxJava 前传 3.md) 14 | 15 | 通过第一部分,可以了解到 RxJava 其实主要应用了观察者模式的理念。在 RxJava 中主要干4件事:一、Observable 用来生产数据并把数据丢给观察者;二、Observer 用来处理 Observable 生产的数据;三、在数据从生产者到消费者的过程中,数据可以被再加工;四、可以指定在那个线程中加工数据和消费数据。 16 | 17 | 注意上面的数据不仅仅是指普通的模型数据(比如 一个数据库中对应的表),可以是任意的事件类型,只要是和时间相关的类型都可以。比如,点击UI 上的一个按钮触发的事件;访问 URL 地址返回的内容;访问数据库返回的数据 等。 18 | 19 | 文章第二部分主要介绍了对数据流(事件流)的一些常用的处理函数。 20 | 21 | - [RxJava 教程第二部分:事件流基础之 创建数据流](http://blog.chengyunfeng.com/?p=959) 22 | - [RxJava 教程第二部分:事件流基础之 过滤数据](http://blog.chengyunfeng.com/?p=960) 23 | - [RxJava 教程第二部分:事件流基础之 检查数据](http://blog.chengyunfeng.com/?p=961) 24 | - [RxJava 教程第二部分:事件流基础之 聚合](http://blog.chengyunfeng.com/?p=962) 25 | - [RxJava 教程第二部分:事件流基础之 转换数据流](http://blog.chengyunfeng.com/?p=964) 26 | 27 | 这部分设计到如何创建一个数据流,也就是如何把传统的数据或者事件转换为 RxJava 的 Observable,比如把网络请求返回结果的功能封装为 Observable、把点击按钮的点击事件封装成 Observable 。然后就是对数据流中数据的处理,比如 一个按钮点击事件,如果用户对一个按钮快速的连续点击(国内的测试同学喜欢这么干),则在某个时间范围内,你可能只希望处理第一次点击事件,后续的点击事件都给丢弃了;这个点击按钮每一次点击都可以看做事件流中的一个事件,然后你可以使用一些过滤操作函数来把不符合要求的事件过滤掉,只处理你想要的数据。除了过滤操作以外,还可以检查数据是否满足某一个测试条件、也可以把数据流中的所有数据聚合起来、如果源来的数据流中的数据在你代码中无法直接使用,则你还可以把数据流中的数据转换为另外一种类型。 28 | 29 | 文章第三部分则介绍了对数据流更高级的处理函数。 30 | 31 | - [RxJava 教程第三部分:驯服数据流之 副作用](http://blog.chengyunfeng.com/?p=968) 32 | - [RxJava 教程第三部分:驯服数据流之 避免 monad](http://blog.chengyunfeng.com/?p=969) 33 | - [RxJava 教程第三部分:驯服数据流之 高级错误处理](http://blog.chengyunfeng.com/?p=970) 34 | - [RxJava 教程第三部分:驯服数据流之 组合数据流](http://blog.chengyunfeng.com/?p=972) 35 | - [RxJava 教程第三部分:驯服数据流之 时间平移](http://blog.chengyunfeng.com/?p=974) 36 | - [RxJava 教程第三部分:驯服数据流之 hot & cold Observable](http://blog.chengyunfeng.com/?p=975) 37 | - [RxJava 教程第三部分:驯服数据流之 自定义操作函数](http://blog.chengyunfeng.com/?p=976) 38 | 39 | 如何在传统遗留代码中使用 RxJava以及数据流中的错误处理。还介绍了如何把多个数据流合并起来、如果推迟数据流中数据的发射。如果 RxJava 中提供的操作函数集合还不能满足你的要求,你还可以自定义操作函数。 40 | 41 | 文章最后一部分介绍了多线程相关的概念和如何在并发环境下使用。 42 | 43 | - [RxJava 教程第四部分:并发 之线程调度](http://blog.chengyunfeng.com/?p=978) 44 | - [RxJava 教程第四部分:并发 之测试](http://blog.chengyunfeng.com/?p=979) 45 | - [RxJava 教程第四部分:并发 之意外情况处理](http://blog.chengyunfeng.com/?p=980) 46 | - [RxJava 教程第四部分:并发 之数据流发射太快如何办](http://blog.chengyunfeng.com/?p=981) 47 | 48 | 通常情况下,生产数据的源头可能需要比较长的时间去执行,比如请求一个网址,获取返回的内容。可以通过 RxJava 提供的方法把耗时的请求放到另外一个线程中执行。另外把数据从一种类型转换为另外一种类型的过程可能也是很耗时的,同样也可以指定每个操作函数在那个线程执行。还介绍了 如果生产数据的源,生产数据的速度很快,而消费者无法及时的处理这些数据该肿么办。 49 | 50 | 看完本系列教程,可以发现 RxJava 本质上只是提供了一个处理数据流的框架。 51 | 52 | 可以这样打个比方,有个生产肥皂的机器 (Observable),该机器每隔10秒生产一块肥皂(原始的数据),后面还有一个机器用来在肥皂上面印上公司 logo (操作函数,把一种数据转换为另外一种数据),在后面还有一个机器用来把肥皂放到盒子中包装起来(同样也是一个操作函数),最后有个工人(消费者)把每盒肥皂装箱打包。 53 | 54 | 而 RxJava 的方便之处就是把这个流程中的处理给简化了,并且可以像使用 Builder 模式一样串联调用,使代码看起来更加优雅。再加上还可以指定每个操作函数执行的线程,使多线程处理更加方便。 55 | 56 | 就拿前面提到的用户快速狂按一个按钮的情况来说,在原生 Android 系统里面是不认为这种情况为 bug, 比如使用原生的 Android 系统,在vpn设置界面快速点击两次添加 vpn的按钮,则可以看到添加 vpn 的对话框会打开两个,关闭一个还有另外一个存在。正常用户不会这样操作系统,就算用户这样操作了,多打开一个同样的界面其实影响也不大,用户关闭即可。 而国内的测试同学经常会测试这个情况,大家通常情况下都是在 View onClick 的时候记录下点击的时间,然后在 onClick 中比较前一次点击的时间和当前的时间,如果这个时间小于一个值(比如 1秒)就认为这次点击是无效的。添加了一个变量用来记录时间并且每次点击都要比较,只是为了处理快速点击的情况。如果使用 RxJava 则就非常简单了: 57 | 58 | ```java 59 | RxView.clicks(button).throttleFirst(500, TimeUnit.MILLISECONDS).subscribe(…); 60 | ``` 61 | 62 | 只处理 500ms 内的第一次点击。 一旦你掌握了 RxJava,发现使用 Rx 的方式来思考问题和解决问题写出的代码是如此的优雅。 63 | -------------------------------------------------------------------------------- /RxJava系列教程/13.Android 专用响应式编程框架 — Agera.md: -------------------------------------------------------------------------------- 1 | ## 13. Android 专用响应式编程框架 — Agera 2 | 3 | 在响应式编程(Reactive programming)这么热的今天,Google 也耐不住寂寞了,周末 Google 开源了他们在 Google Play Movies 项目中内部使用的 Android 专用的响应式编程框架 — [Agera](https://github.com/google/agera)。 Agera 和 RxJava 没有任何关系,只是[响应式编程](http://reactivex.io/)在 Android 平台上的轻量级实现。 4 | 5 | 下面是一个示例: 6 | 7 | ```java 8 | public class AgeraActivity extends Activity 9 | implements Receiver, Updatable { 10 | private static final ExecutorService NETWORK_EXECUTOR = 11 | newSingleThreadExecutor(); 12 | private static final ExecutorService DECODE_EXECUTOR = 13 | newSingleThreadExecutor(); 14 | private static final String BACKGROUND_BASE_URL = 15 | "http://www.gravatar.com/avatar/4df6f4fe5976df17deeea19443d4429d?s="; 16 | 17 | private Repository> background; 18 | private ImageView backgroundView; 19 | 20 | @Override 21 | protected void onCreate(final Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | // Set the content view 24 | setContentView(R.layout.activity_main); 25 | 26 | // Find the background view 27 | backgroundView = (ImageView) findViewById(R.id.background); 28 | 29 | // Create a repository containing the result of a bitmap request. Initially 30 | // absent, but configured to fetch the bitmap over the network based on 31 | // display size. 32 | background = repositoryWithInitialValue(Result.absent()) 33 | .observe() // Optionally refresh the bitmap on events. In this case never 34 | .onUpdatesPerLoop() // Refresh per Looper thread loop. In this case never 35 | .getFrom(new Supplier() { 36 | @NonNull 37 | @Override 38 | public HttpRequest get() { 39 | DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); 40 | int size = Math.max(displayMetrics.heightPixels, 41 | displayMetrics.widthPixels); 42 | return httpGetRequest(BACKGROUND_BASE_URL + size) 43 | .compile(); 44 | } 45 | }) // Supply an HttpRequest based on the display size 46 | .goTo(NETWORK_EXECUTOR) // Change execution to the network executor 47 | .attemptTransform(httpFunction()) 48 | .orSkip() // Make the actual http request, skip on failure 49 | .goTo(DECODE_EXECUTOR) // Change execution to the decode executor 50 | .thenTransform(new Function>() { 51 | @NonNull 52 | @Override 53 | public Result apply(@NonNull HttpResponse response) { 54 | byte[] body = response.getBody(); 55 | return absentIfNull(decodeByteArray(body, 0, body.length)); 56 | } 57 | }) // Decode the response to the result of a bitmap, absent on failure 58 | .onDeactivation(SEND_INTERRUPT) // Interrupt thread on deactivation 59 | .compile(); // Create the repository 60 | } 61 | 62 | @Override 63 | protected void onResume() { 64 | super.onResume(); 65 | // Start listening to the repository, triggering the flow 66 | background.addUpdatable(this); 67 | } 68 | 69 | @Override 70 | protected void onPause() { 71 | super.onPause(); 72 | // Stop listening to the repository, deactivating it 73 | background.removeUpdatable(this); 74 | } 75 | 76 | @Override 77 | public void update() { 78 | // Called as the repository is updated 79 | // If containing a valid bitmap, send to accept below 80 | background.get().ifSucceededSendTo(this); 81 | } 82 | 83 | @Override 84 | public void accept(@NonNull Bitmap background) { 85 | // Set the background bitmap to the background view 86 | backgroundView.setImageBitmap(background); 87 | } 88 | } 89 | ``` 90 | 91 | 更多详情请参考官方网站: https://github.com/google/agera 92 | -------------------------------------------------------------------------------- /RxJava系列教程/26.RxJava 常见的错误用法.md: -------------------------------------------------------------------------------- 1 | ## 26. RxJava 常见的错误用法 2 | 3 | RxJava 用起来很爽,特别是和 retrofit 一起用了请求网络数据。对于大部分初学者呢,都会出现这样的用法: 4 | 5 | ```java 6 | service = GithubService.createGithubService(githubToken); 7 | view.setOnClickListener( v -> service.user(name) 8 | .subscribeOn(Schedulers.io()) 9 | .observeOn(AndroidSchedulers.mainThread()) 10 | .subscribe(System.out::println)); 11 | ``` 12 | 13 | 当点击一个按钮的时候,去请求服务器数据然后使用返回的结果刷新 UI。(比如当前显示用户信息的界面上有个刷新按钮,点击一下就去请求数据并刷新界面)。 14 | 笔者就曾经写过这样的代码。但是经过简单的测试就发现这是有问题的! 15 | 16 | 1. 由于网络请求是在后台线程发生的,并且需要时间,如果网络请求没有完成,用户点击了返回按键退出了当前界面则会引起 Activity 泄露 17 | 2. 每次点击刷新按钮都会触发一个新的网络请求,同时网络请求返回的顺序是不确定的,可能导致收到的数据问题 18 | 3. 如果把收到的数据保存到一个集合中,多次点击刷新按钮会导致同样的结果出现在数据集合中 19 | 20 | 比如输入一个用户名并点击刷新按钮查看其详细资料,在结果没有返回的时候,再次输入一个新的用户名并点击刷新按钮,则有可能第二次请求先返回,然后第一个请求结果才返回,这样用户最终看到的是第一个用户名对应的详细信息而不是第二个用户的。 21 | 22 | 其中第一个问题比较好解决,使用一个 CompositeSubscription ,把每个 Subscription 都添加到这里,在 onDestroy 里面取消注册即可(subscriptions.unsubscribe())。 23 | 24 | ```java 25 | subscriptions = new CompositeSubscription(); 26 | service = GithubService.createGithubService(githubToken); 27 | view.setOnClickListener( v -> { 28 | Subscription sub = service.user(name) 29 | .subscribeOn(Schedulers.io()) 30 | .observeOn(AndroidSchedulers.mainThread()) 31 | .subscribe(System.out::println); 32 | subscriptions.add(sub); 33 | }); 34 | ``` 35 | 36 | 但是另外两个问题呢? 37 | 这里就要使用 Subject 来转发 View 的onClick 事件了。例如下面使用 PublishSubject: 38 | 39 | ```java 40 | subject = PublishSubject.create(); 41 | subscriptions = new CompositeSubscription(); 42 | service = GithubService.createGithubService(githubToken); 43 | view.setOnClickListener(v-> subject.onNext(v)); 44 | Subscription sub = subject.flatMap(v-> service.user(name)) 45 | .subscribeOn(Schedulers.io()) 46 | .observeOn(AndroidSchedulers.mainThread()) 47 | .subscribe(System.out::println); 48 | subscriptions.add(sub); 49 | ``` 50 | 51 | 这样可以避免每次点击刷新按钮都创建一个新的 Subscription。 52 | 而第三种情况,可以使用 switchMap 来解决 53 | 54 | ```java 55 | subject = PublishSubject.create(); 56 | subscriptions = new CompositeSubscription(); 57 | service = GithubService.createGithubService(githubToken); 58 | view.setOnClickListener(v-> subject.onNext(v)); 59 | Subscription sub = subject.switchMap(v-> service.user(name)) 60 | .subscribeOn(Schedulers.io()) 61 | .observeOn(AndroidSchedulers.mainThread()) 62 | .subscribe(System.out::println); 63 | subscriptions.add(sub); 64 | ``` 65 | 66 | 另外为了避免用户快速的点击同一个按钮,则可以使用 throttleFirst 来过滤掉后面一段时间内的点击事件。 67 | 同时如果使用了 RxBinding 和 RxLifecycle 则代码会更加简洁清晰。 68 | 69 | ```java 70 | RxView.clicks(view) 71 | .subscribeOn(AndroidSchedulers.mainThread()) 72 | .doOnNext(aVoid1 -> _adapter.clear()) 73 | .throttleFirst(300, TimeUnit.MILLISECONDS) 74 | .observeOn(Schedulers.io()) 75 | .switchMap(aVoid -> _githubService.contributors(_username.getText().toString(), _repo.getText().toString())) 76 | .compose(bindUntilEvent(FragmentEvent.DESTROY_VIEW)) 77 | .observeOn(AndroidSchedulers.mainThread()) 78 | .subscribe(contributors -> { 79 | for (Contributor c : contributors) { 80 | _adapter.add(format("%s has made %d contributions to %s", 81 | c.login, 82 | c.contributions, 83 | _repo.getText().toString())); 84 | } 85 | }); 86 | ``` 87 | 88 | 上面的代码根据 https://github.com/kaushikgopal/RxJava-Android-Samples/blob/master/app/src/main/java/com/morihacky/android/rxjava/fragments/RetrofitFragment.java 中的代码修改而来,可以把上面的代码插入到 onCreateView 函数的 return 语句之前。同时取消掉 onListContributorsClicked 函数。 89 | 同时 RxJava-Android-Samples 这个项目有 3432 人加星 有 639 人 fork。 说明还是有不少人关注的,但是里面的示例用法还是有不少问题的。所以大家在参考示例项目学习的时候,一定要学会思考,不能直接复制粘贴代码就拿来用了,RxJava-Android-Samples 项目只是告诉 Rxjava 初学者 RxJava 可以这么用,那是具体用的对不对就不一定了! 90 | -------------------------------------------------------------------------------- /RxJava系列教程/3.RxJava 教程第一部分:入门之 生命周期管理.md: -------------------------------------------------------------------------------- 1 | # 3. 入门之生命周期管理 2 | 3 | Rx 背后的理念是:无法知道事件流何时发射数据、也不知何时结束发射,但是你需要控制何时开始和结束接受事件。订阅者可能使用了一些资源,这些资源需要在停止接收事件的时候释放。 通过 subscription 可以实现生命周期管理。 4 | 5 | ## Subscribing 6 | 7 | Observable.subscribe 有好几个重载函数,每个函数都是某种情况的简化形式。 8 | 9 | ```java 10 | Subscription subscribe() 11 | Subscription subscribe(Action1 onNext) 12 | Subscription subscribe(Action1 onNext, Action1 onError) 13 | Subscription subscribe(Action1 onNext, Action1 onError, Action0 onComplete) 14 | Subscription subscribe(Observer observer) 15 | Subscription subscribe(Subscriber subscriber) 16 | ``` 17 | 18 | 第一个 subscribe() 函数只是订阅事件,但是不去处理这些事件。带有一个或者多个 Action 参数的,使用这些参数来构造一个 Subscriber 对象。这些参数分别对应 onNext、onError 和 onComplete 这三种类型的事件,如果没有提供则代表不处理这个类型的事件。 19 | 20 | 下面的示例演示处理 error 的情况: 21 | 22 | ```java 23 | Subject s = ReplaySubject.create(); 24 | s.subscribe( 25 | v -> System.out.println(v), 26 | e -> System.err.println(e)); 27 | s.onNext(0); 28 | s.onError(new Exception("Oops")); 29 | ``` 30 | 31 | 输出结果: 32 | 33 | ``` 34 | 0 35 | java.lang.Exception: Oops 36 | ``` 37 | 38 | 如果你不处理 error,则在发生错误的时候,会抛出 OnErrorNotImplementedException 异常。该异常发生在生产者这边,上面的示例生产者和消费者位于同一线程,所以你可以直接 try- catch 住,但是在实际应用中,生产者和消费者通常不再同一个线程,所以最好还是提供一个 错误处理函数,否则你不知道错误发生了并导致事件流结束了。 39 | 40 | ## Unsubscribing 41 | 42 | 在事件流结束发射之前,你可以主动停止接收事件。每个 subscribe 函数都会返回一个 Subscription 示例,该示例有两个函数: 43 | 44 | ```java 45 | boolean isUnsubscribed() 46 | void unsubscribe() 47 | ``` 48 | 49 | 只需要调用 unsubscribe 函数就可以停止接收数据了。 50 | 51 | ```java 52 | Subject values = ReplaySubject.create(); 53 | Subscription subscription = values.subscribe( 54 | v -> System.out.println(v), 55 | e -> System.err.println(e), 56 | () -> System.out.println("Done") 57 | ); 58 | values.onNext(0); 59 | values.onNext(1); 60 | subscription.unsubscribe(); 61 | values.onNext(2); 62 | ``` 63 | 64 | 输出结果: 65 | 66 | ``` 67 | 0 68 | 1 69 | ``` 70 | 71 | 一个 observer 调用 unsubscribe 取消监听并不妨碍同一个 observable 上的其他 Observer 对象。 72 | 73 | ```java 74 | Subject values = ReplaySubject.create(); 75 | Subscription subscription1 = values.subscribe( 76 | v -> System.out.println("First: " + v) 77 | ); 78 | Subscription subscription2 = values.subscribe( 79 | v -> System.out.println("Second: " + v) 80 | ); 81 | values.onNext(0); 82 | values.onNext(1); 83 | subscription1.unsubscribe(); 84 | System.out.println("Unsubscribed first"); 85 | values.onNext(2); 86 | ``` 87 | 88 | 输出结果: 89 | 90 | ``` 91 | First: 0 92 | Second: 0 93 | First: 1 94 | Second: 1 95 | Unsubscribed first 96 | Second: 2 97 | ``` 98 | 99 | ## onError 和 onCompleted 100 | 101 | onError 和 onCompleted 意味着结束事件流。observable 需要遵守该规范,在 onError 或者 onCompleted 发生后就不应该再发射事件了。 102 | 103 | ```java 104 | Subject values = ReplaySubject.create(); 105 | Subscription subscription1 = values.subscribe( 106 | v -> System.out.println("First: " + v), 107 | e -> System.out.println("First: " + e), 108 | () -> System.out.println("Completed") 109 | ); 110 | values.onNext(0); 111 | values.onNext(1); 112 | values.onCompleted(); 113 | values.onNext(2); 114 | ``` 115 | 116 | 结果: 117 | 118 | ``` 119 | First: 0 120 | First: 1 121 | Completed 122 | ``` 123 | 124 | ## 释放资源 125 | 126 | Subscription 和其使用的资源绑定在一起。所以你应该记得释放 Subscription 来释放资源。使用 Subscriptions 的工厂函数可以把 Subscription 和需要的资源绑定在一起,然后可以使用 unsubscribe 来释放绑定的资源。 127 | 128 | ```java 129 | Subscription s = Subscriptions.create(() -> System.out.println("Clean")); 130 | s.unsubscribe(); 131 | ``` 132 | 133 | 输出结果: 134 | 135 | ``` 136 | Clean 137 | ``` 138 | 139 | Subscriptions.create 函数需要一个 Action 接口类型参数,在 unsubscribe 调用的时候会执行该接口来释放资源。 也有其他一些函数可以简化开发: 140 | 141 | - Subscriptions.empty() 返回一个当 unsubscribe 的时候 啥也不做的Subscription 。当要求你返回一个 Subscription ,但是你确没有资源需要释放,则可以返回这个空的 Subscription。 142 | - Subscriptions.from(Subscription… subscriptions),返回的 Subscription 释放的时候,会调用所有参数 Subscription 的 unsubscribe 函数。 143 | - Subscriptions.unsubscribed() 返回一个已经释放过的 Subscription。 144 | 145 | Subscription 也有一些标准的实现: 146 | 147 | - BooleanSubscription 148 | - CompositeSubscription 149 | - MultipleAssignmentSubscription 150 | - RefCountSubscription 151 | - SafeSubscriber 152 | - Scheduler.Worker 153 | - SerializedSubscriber 154 | - SerialSubscription 155 | - Subscriber 156 | - TestSubscriber 157 | 158 | 在后面将会看到他们的使用方式。这里注意 Subscriber 同时也实现了 Subscription。所以我们也可以直接用 Subscriber 来取消监听。 159 | -------------------------------------------------------------------------------- /RxJava系列教程/24.RxJava 的 compose 操作函数实战.md: -------------------------------------------------------------------------------- 1 | ## 24. RxJava 的 compose 操作函数实战 2 | 3 | 如果你对 RxJava 还不了解,请参考Intro To RxJava 系列教程,如果你看了其中的自定义操作函数后,认为 compose 操作函数只在自定义操作函数中才需要使用,则 Dan lew 通过这篇文章告诉你,并非如此。 很多情况我们都可以使用 compose 函数。 4 | 5 | RxJava 一大特性就是串联调用各种操作函数,这样代码看起来比较整洁,也能清晰的表达代码所要实现的功能,例如: 6 | 7 | ```java 8 | Observable.from(someSource) 9 | .map(data -> manipulate(data)) 10 | .subscribeOn(Schedulers.io()) 11 | .observeOn(AndroidSchedulers.mainThread()) 12 | .subscribe(data -> doSomething(data)); 13 | ``` 14 | 15 | 特别是在实现请求网络的时候,每次都需要使用 subscribeOn() 和 observeOn() 函数来指定在 IO 线程请求网络,在 UI 线程处理返回的数据。如果这两个函数的调用能够封装到一个函数中来复用,这样写代码是不是更加简单一些。 16 | 17 | 在没有发现 compose 之前是这么干的 18 | 19 | 在刚刚开始接触 RxJava 的时候,我是通过下面的方式来实现复用的: 20 | 21 | ```java 22 | Observable applySchedulers(Observable observable) { 23 | return observable.subscribeOn(Schedulers.io()) 24 | .observeOn(AndroidSchedulers.mainThread()); 25 | } 26 | ``` 27 | 28 | 然后用上面的函数把 Observable 包裹起来(Observable 为上面函数的参数): 29 | 30 | ```java 31 | applySchedulers( 32 | Observable.from(someSource) 33 | .map(data -> manipulate(data)) 34 | ) 35 | .subscribe(data -> doSomething(data)); 36 | ``` 37 | 38 | 虽然上面的代码实现了需要的功能,但是看起来太难看了,也太费解了 — applySchedulers() 到底干了啥? 如果在同一个 Observable 多次使用了类似的代码,则看起来是不是更加头疼。 39 | 40 | ## Transformer 闪亮登场 41 | 42 | 聪明的 RxJava 开发者团队已经意识到这个问题,并且提供了一个解决方式: Transformer 和 Observable.compose(). 43 | Transformer 其实就是 Func1<Observable, Observable> 这个接口,只不过起了一个更好听的名字。参数是一个 Observable,返回另外一个类型的 Observable。 Observable 的很多数据处理的操作函数也都是这样的。 44 | 45 | 使用 Transformer 来实现一开始需要的功能: 46 | 47 | ```java 48 | Transformer applySchedulers() { 49 | return new Transformer() { 50 | @Override 51 | public Observable call(Observable observable) { 52 | return observable.subscribeOn(Schedulers.io()) 53 | .observeOn(AndroidSchedulers.mainThread()); 54 | } 55 | }; 56 | } 57 | ``` 58 | 59 | 使用 lambdas 表达式更加简单一些: 60 | 61 | ```java 62 | Transformer applySchedulers() { 63 | return observable -> observable.subscribeOn(Schedulers.io()) 64 | .observeOn(AndroidSchedulers.mainThread()); 65 | } 66 | ``` 67 | 68 | 然后和 compose 一起使用: 69 | 70 | ```java 71 | Observable.from(someSource) 72 | .map(data -> manipulate(data)) 73 | .compose(applySchedulers()) 74 | .subscribe(data -> doSomething(data)); 75 | ``` 76 | 77 | 看起来是不是简洁多了,并且代码还是可以串联的调用。 78 | 79 | 上面的代码在低于 JDK 8 的 SDK 中无法正确编译。由于 之前的 JDK 无法通过返回值来推导泛型类型的参数,你需要明确的告诉编译器返回的参数类型: 80 | 81 | ```java 82 | Observable.from(someSource) 83 | .map(data -> manipulate(data)) 84 | .compose(this.applySchedulers()) 85 | .subscribe(data -> doSomething(data)); 86 | ``` 87 | 88 | ## 复用 Transformer 89 | 90 | 上面的代码在每次只需该操作的时候都会创建一个新的 Transformer 实例。 其实通过创建一个 Transformer 实例来使用可以避免该问题。 91 | 92 | 如果你需要把 Observable 从一个确定的类型转换为另外一个确定的类型,则可以这样创建一个示例: 93 | 94 | ```java 95 | Transformer myTransformer = new Transformer() { 96 | // ...Do your work here... 97 | }; 98 | ``` 99 | 100 | 但是我们上面线程调度的 Transformer 类型是不确定的,但是无法定义一个泛型的 Transformer 实例: 101 | 102 | ```java 103 | // 无法通过编译,不知道 T 是从哪里来的 104 | Transformer myTransformer; 105 | ``` 106 | 107 | 虽然可以使用 Transformer<Object, Object>,但是返回的 Observable 的类型就丢失了,导致返回的 Observable 不太好用。 108 | 109 | 在 Java sdk 中的 Collections 中有个解决该问题的方式,一些创建类型安全的不可变空集合的函数。内部使用非泛型的示例,返回的时候通过泛型来转换。 110 | 111 | 使用通用的技巧,我们的 Transformer 实例如下: 112 | 113 | ```java 114 | final Transformer schedulersTransformer = 115 | observable -> observable.subscribeOn(Schedulers.io()) 116 | .observeOn(AndroidSchedulers.mainThread()); 117 | 118 | @SuppressWarnings("unchecked") 119 | Transformer applySchedulers() { 120 | return (Transformer) schedulersTransformer; 121 | } 122 | ``` 123 | 124 | 这样我们可以宠用这同一个实例了,不用每次都创建一个新的 Transformer 实例。 125 | 126 | 注意:上面的类型转换是不安全的,这就要求你的 Transformer 实现确实是无关类型的。比如这里的线程调度的两个函数,他们是不会修改 Observable 的类型的,所以可以这样使用。 127 | 128 | ## flatMap() 是干啥的呢? 129 | 130 | flatMap() 的返回值也是 Observable, 是不是就意味着 flatMap 也可以重用部分操作函数? 131 | 132 | 他们的区别在于,compose() 是更高层的抽象: 他的操作是作用在整个事件流上的,而不是里面发射的单个数据。具体来讲就是: 133 | 134 | 只能通过 compose() 来从数据流中获取源 Observable 。所以对于影响整个事件流的操作函数(例如 subscribeOn() 和 observeOn())需要使用 compose()。 135 | 136 | 如果你把 subscribeOn()/observeOn() 在 flatMap 内使用,则只会对在 flatMap 里面创建的 Observable 有用而不是整个数据流。 137 | 138 | 一旦创建了事件流,compose() 就立刻开始执行了。而flatMap() 只有当每次 onNext() 调用的时候才开始执行。也就是说,flatMap() 转换的是每个单独的数据而 compose() 是作用在整个数据流上的。 139 | 140 | 由于每次调用 Observable 的 onNext() 函数 flatMap() 都会创建一个新的 Observable。所以 flatMap() 的效率并不是很好。 141 | 142 | ## 结论 143 | 144 | 如果你只是想把几个常用的操作函数封装为一个函数来复用代码,则请使用 compose()。 flatMap() 有很多种用法的,但是并不适合这种情况。 145 | -------------------------------------------------------------------------------- /RxJava系列教程/30.RxJava 前传 3.md: -------------------------------------------------------------------------------- 1 | ## RxJava 前传 3 2 | 3 | 前一篇文章我们把所有的操作都修改为异步的了,最终的代码 看起来是不是有点眼熟啊? 再仔细看看。还没发现? 如果把匿名类修改为 Java 8 的 lambdas 表达式(逻辑是一样的,只是让代码看起来更清晰点)就很容易发现了。 4 | 5 | ```java 6 | public class CatsHelper { 7 | 8 | ApiWrapper apiWrapper; 9 | 10 | public AsyncJob saveTheCutestCat(String query) { 11 | AsyncJob> catsListAsyncJob = apiWrapper.queryCats(query); 12 | AsyncJob cutestCatAsyncJob = catsListAsyncJob.map(cats -> findCutest(cats)); 13 | AsyncJob storedUriAsyncJob = cutestCatAsyncJob.flatMap(cat -> apiWrapper.store(cat)); 14 | return storedUriAsyncJob; 15 | } 16 | 17 | private Cat findCutest(List cats) { 18 | return Collections.max(cats); 19 | } 20 | } 21 | ``` 22 | 这样看起来是不是就很清晰了。 这个代码和刚刚开头的阻塞式代码是不是非常相似: 23 | ```java 24 | public class CatsHelper { 25 | 26 | Api api; 27 | 28 | public Uri saveTheCutestCat(String query){ 29 | List cats = api.queryCats(query); 30 | Cat cutest = findCutest(cats); 31 | Uri savedUri = api.store(cutest); 32 | return savedUri; 33 | } 34 | 35 | private Cat findCutest(List cats) { 36 | return Collections.max(cats); 37 | } 38 | } 39 | ``` 40 | 现在他们不仅逻辑是一样的,语义上也是一样的。 太棒了! 41 | 42 | 同时我们还可以使用组合操作,现在把两个异步操作组合一起并返还另外一个异步操作。 43 | 44 | 异常处理也会传递到最终的回调接口中。 45 | 46 | 下面来看看 RxJava 吧。 47 | 48 | 你没必要把上面代码应用到您的项目中去, 这些简单的、线程不安全的代码只是 RxJava 的一部分。 49 | 只有一些名字上的不同: 50 | 51 | 1. AsyncJob 等同于 Observable, 不仅仅可以返回一个结果,还可以返回一系列的结果,当然也可能没有结果返回。 52 | 2. Callback 等同于 Observer, 除了onNext(T t), onError(Throwable t)以外,还有一个onCompleted()函数,该函数在结束继续返回结果的时候通知Observable 。 53 | 3. abstract void start(Callback callback) 和 Subscription subscribe(final Observer observer) 类似,返回一个Subscription ,如果你不再需要后面的结果了,可以取消该任务。 54 | 55 | 除了 map 和 flatMap 以外, Observable 还有很多其他常见的转换操作。 56 | 57 | 下面是 RxJava 版本的代码: 58 | 59 | ```java 60 | public class ApiWrapper { 61 | Api api; 62 | 63 | public Observable> queryCats(final String query) { 64 | return Observable.create(new Observable.OnSubscribe>() { 65 | @Override 66 | public void call(final Subscriber> subscriber) { 67 | api.queryCats(query, new Api.CatsQueryCallback() { 68 | @Override 69 | public void onCatListReceived(List cats) { 70 | subscriber.onNext(cats); 71 | } 72 | 73 | @Override 74 | public void onQueryFailed(Exception e) { 75 | subscriber.onError(e); 76 | } 77 | }); 78 | } 79 | }); 80 | } 81 | 82 | public Observable store(final Cat cat) { 83 | return Observable.create(new Observable.OnSubscribe() { 84 | @Override 85 | public void call(final Subscriber subscriber) { 86 | api.store(cat, new Api.StoreCallback() { 87 | @Override 88 | public void onCatStored(Uri uri) { 89 | subscriber.onNext(uri); 90 | } 91 | 92 | @Override 93 | public void onStoreFailed(Exception e) { 94 | subscriber.onError(e); 95 | } 96 | }); 97 | } 98 | }); 99 | } 100 | } 101 | 102 | public class CatsHelper { 103 | 104 | ApiWrapper apiWrapper; 105 | 106 | public Observable saveTheCutestCat(String query) { 107 | Observable> catsListObservable = apiWrapper.queryCats(query); 108 | Observable cutestCatObservable = catsListObservable.map(new Func1, Cat>() { 109 | @Override 110 | public Cat call(List cats) { 111 | return CatsHelper.this.findCutest(cats); 112 | } 113 | }); 114 | Observable storedUriObservable = cutestCatObservable.flatMap(new Func1>() { 115 | @Override 116 | public Observable call(Cat cat) { 117 | return apiWrapper.store(cat); 118 | } 119 | }); 120 | return storedUriObservable; 121 | } 122 | 123 | private Cat findCutest(List cats) { 124 | return Collections.max(cats); 125 | } 126 | } 127 | ``` 128 | 把 Observable 替换为 AsyncJob 后 他们的代码是一样的。 129 | 130 | ### 结论 131 | 132 | 通过简单的转换操作,我们可以把异步操作抽象出来。这种抽象的结果可以像操作简单的阻塞函数一样来操作异步操作并组合异步操作。这样我们就可以摆脱层层嵌套的回调接口了,并且不用手工的去处理每次异步操作的异常。 133 | 134 | ### 参考资料 135 | - http://yarikx.github.io/NotRxJava/ 136 | - http://reactivex.io/ 137 | - https://github.com/ReactiveX/Rx 138 | - https://github.com/ReactiveX/RxJava/wiki 139 | - http://queue.acm.org/detail.cfm?id=2169076 140 | - https://www.coursera.org/course/reactive 141 | - http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/ -------------------------------------------------------------------------------- /RxJava系列教程/28.RxJava 前传 1.md: -------------------------------------------------------------------------------- 1 | ## RxJava 前传 1 2 | 3 | RxJava 近几年开始在 Android 开发者社区流行,但是由于解决的是复杂的异步操作问题,再加上 Java 语法的限制导致 RxJava 的代码看起来比较费劲,RxJava 的学习曲线也进一步被拉高了。本文介绍 RxJava 的基本原理以及其要解决的问题,来帮助初学者理解 RxJava 框架。 4 | 5 | ### Cat App 6 | 7 | 将使用一个真实世界的例子来逐步帮助大家理解 RxJava 的设计思想。 所以我们将创建一个下载 Cat 图片的应用。 8 | 9 | 需要解决的问题是: 10 | 11 | 有一个 web 服务提供了一些 API 来搜索整个网络上的符合查询关键字的所有猫的图片。 每个图片包含一个可爱程度的参数 – 一个整数值表示其可爱程度。 我们的任务就是 :下载返回的猫图片,选择最可爱的那个,并且把图片保存到本地存储中。 12 | 13 | 我们只关注 下载、处理和保存图片的三个核心功能。 14 | 15 | 下面开动吧! 16 | 17 | ### Model 和 API 18 | 19 | 下面是一个简单的描述 Cat 的模型类: 20 | 21 | ```java 22 | public class Cat implements Comparable{ 23 | Bitmap image; 24 | int cuteness; 25 | 26 | @Override 27 | public int compareTo(Cat another) { 28 | return Integer.compare(cuteness, another.cuteness); 29 | } 30 | } 31 | ``` 32 | 33 | 我们的 API 将会使用来自 cat-sdk.jar 中的阻塞风格的接口。 34 | 35 | ```java 36 | public interface Api { 37 | List queryCats(String query); 38 | Uri store(Cat cat); 39 | } 40 | ``` 41 | 42 | 看起来很简单吧! 现在开始编写我们的业务逻辑。 43 | 44 | ```java 45 | public class CatsHelper { 46 | 47 | Api api; 48 | 49 | public Uri saveTheCutestCat(String query){ 50 | List cats = api.queryCats(query); 51 | Cat cutest = findCutest(cats); 52 | Uri savedUri = api.store(cutest); 53 | return savedUri; 54 | } 55 | 56 | private Cat findCutest(List cats) { 57 | return Collections.max(cats); 58 | } 59 | } 60 | ``` 61 | 62 | (@ο@) 哇~,这也太简单太清晰了吧!来看看上面的代码是多么的酷。主要的函数 saveTheCutestCat 只包含了 3个函数调用。使用参数来调用这些函数,并接收返回的参数。 并且等待每个函数执行并返回结果。 63 | 64 | 如此简单、如此有效。下面来看看这种简单函数的其他优点。 65 | 66 | ### 组合(Composition) 67 | 68 | 可以看到我们的 saveTheCutestCat 由其他三个函数调用所组成的。我们通过函数来把一个大功能分割为每个容易理解的小功能。通过函数调用来组合使用这些小功能。使用和理解起来都相当简单。 69 | 70 | ### 异常传递 71 | 72 | 另外一个使用函数的好处就是方便处理异常。每个函数都可以通过抛出异常来结束运行。该异常可以在抛出异常的函数里面处理,也可以在调用该函数的外面处理,所以我们无需每次都处理每个异常,我们可以在一个地方处理所有可能抛出的异常。 73 | 74 | ```java 75 | try{ 76 | List cats = api.queryCats(query); 77 | Cat cutest = findCutest(cats); 78 | Uri savedUri = api.store(cutest); 79 | return savedUri; 80 | } catch (Exception e) { 81 | e.printStackTrace() 82 | return someDefaultValue; 83 | } 84 | ``` 85 | 86 | 这样,我们就可以处理这三个函数中所抛出的任何异常了。如果没有 try catch 语句,我们也可以把异常继续传递下去。 87 | 88 | ### 向异步进军 89 | 90 | 但是,现实世界中我们往往没法等待。有些时候你没法只使用阻塞调用。在 Android 中你需要处理各种异步操作。 91 | 92 | 就拿 Android 的 OnClickListener 接口来说吧, 如果你需要处理一个 View 的点击事件,你必须提供一个 该 Listener 的实现来处理用户的点击事件。下面来看看如何处理异步调用。 93 | 94 | ### 异步网络调用 95 | 96 | 假设我们的 cats-sdk.jar 使用了异步调用的 API 来访问网络资源,例如使用 Ion 来异步访问网络。 97 | 98 | 这样我们的新 API 接口就变为这样了: 99 | 100 | ```java 101 | public interface Api { 102 | interface CatsQueryCallback { 103 | void onCatListReceived(List cats); 104 | void onError(Exception e); 105 | } 106 | 107 | void queryCats(String query, CatsQueryCallback catsQueryCallback); 108 | 109 | Uri store(Cat cat); 110 | } 111 | ``` 112 | 113 | 这样我们查询猫的操作就变为异步的了, 通过 CatsQueryCallback 回调接口来结束查询的数据和处理异常情况。 114 | 115 | 我们的业务逻辑也需要跟着改变一下: 116 | 117 | ```java 118 | public class CatsHelper { 119 | 120 | public interface CutestCatCallback { 121 | void onCutestCatSaved(Uri uri); 122 | void onQueryFailed(Exception e); 123 | } 124 | 125 | Api api; 126 | 127 | public void saveTheCutestCat(String query, CutestCatCallback cutestCatCallback){ 128 | api.queryCats(query, new Api.CatsQueryCallback() { 129 | @Override 130 | public void onCatListReceived(List cats) { 131 | Cat cutest = findCutest(cats); 132 | Uri savedUri = api.store(cutest); 133 | cutestCatCallback.onCutestCatSaved(savedUri); 134 | } 135 | 136 | @Override 137 | public void onError(Exception e) { 138 | cutestCatCallback.onQueryFailed(e); 139 | } 140 | }); 141 | } 142 | 143 | private Cat findCutest(List cats) { 144 | return Collections.max(cats); 145 | } 146 | } 147 | ``` 148 | 149 | 这样我们就没法使用阻塞式函数调用了, 我们无法继续使用阻塞调用来使用这些 API 了(当然了,万事无绝对,如果你使用 synchronized, CountdownLatch 等方式来阻塞线程,你还是可以继续使用,本文我们需要处理异步方式来理解这些概念。) 所以,我们没法让 saveTheCutestCat 函数返回一个值了, 我们需要一个回调接口来异步的处理结果。 150 | 151 | 这里我们再进一步,使用两个异步操作来实现我们的功能, 例如 我们还使用异步 IO 来写文件。 152 | 153 | ```java 154 | public interface Api { 155 | interface CatsQueryCallback { 156 | void onCatListReceived(List cats); 157 | void onQueryFailed(Exception e); 158 | } 159 | 160 | interface StoreCallback{ 161 | void onCatStored(Uri uri); 162 | void onStoreFailed(Exception e); 163 | } 164 | 165 | void queryCats(String query, CatsQueryCallback catsQueryCallback); 166 | 167 | void store(Cat cat, StoreCallback storeCallback); 168 | } 169 | ``` 170 | 171 | 业务逻辑变为: 172 | 173 | ```java 174 | public class CatsHelper { 175 | 176 | public interface CutestCatCallback { 177 | void onCutestCatSaved(Uri uri); 178 | void onError(Exception e); 179 | } 180 | 181 | Api api; 182 | 183 | public void saveTheCutestCat(String query, CutestCatCallback cutestCatCallback){ 184 | api.queryCats(query, new Api.CatsQueryCallback() { 185 | @Override 186 | public void onCatListReceived(List cats) { 187 | Cat cutest = findCutest(cats); 188 | api.store(cutest, new Api.StoreCallback() { 189 | @Override 190 | public void onCatStored(Uri uri) { 191 | cutestCatCallback.onCutestCatSaved(uri); 192 | } 193 | 194 | @Override 195 | public void onStoreFailed(Exception e) { 196 | cutestCatCallback.onError(e); 197 | } 198 | }); 199 | } 200 | 201 | @Override 202 | public void onQueryFailed(Exception e) { 203 | cutestCatCallback.onError(e); 204 | } 205 | }); 206 | } 207 | 208 | private Cat findCutest(List cats) { 209 | return Collections.max(cats); 210 | } 211 | } 212 | ``` 213 | 214 | 现在再来看看我们的业务逻辑代码,是不是和之前的阻塞式调用那么简单、那么清晰? 当然不一样了,上面的异步操作代码看起来太恐怖了! 这里有太多的干扰代码了,太多的匿名类了,但是不可否认,他们的业务逻辑其实是一样的。查询猫的列表数据、找出最可爱的并保持其图片。 215 | 216 | 组合功能也不见了! 现在你没法像阻塞操作一样来组合调用每个功能了。异步操作中,每次你都必须通过回调接口来手工的处理结果。 217 | 218 | 那么关于异常传递和处理呢? 没了!异步代码中的异常不会自动传递了,我们需要手工的重新传递出去。(onStoreFailed 和 onQueryFailed 就是干这事用的) 219 | 220 | 上面的代码非常难懂也更难发现潜在的 BUG。 221 | 222 | 然后呢?我们如何处理这种情况呢?我们是不是就被困在这种无法组合的回调接口困局中呢? 下次我们来看看如何优化该问题。 -------------------------------------------------------------------------------- /RxJava1.0/Rxjava操作符大全.md: -------------------------------------------------------------------------------- 1 | ## Rxjava操作符大全 2 | 3 | 在Rxjava当中最重要的就是操作符,RxJava当中有着庞大的操作符 4 | 5 | ### 创建操作符:负责创建Observable对象 6 | 7 | | 操作符 | 功能描述 | 8 | | ------------ | ---------------------------------------- | 9 | | create() | 使用一个函数从头创建一个Observable | 10 | | just() | 将一个或多个对象转换成发射这个或这些对象的一个Observable | 11 | | from() | 将一个Iterable, 一个Future, 或者一个数组转换成一个Observable | 12 | | repeat() | 创建一个重复发射指定数据或数据序列的Observable | 13 | | repeatWhen() | 创建一个重复发射指定数据或数据序列的Observable,它依赖于另一个Observable发射的数据 | 14 | | defer() | 只有当订阅者订阅才创建Observable;为每个订阅创建一个新的Observable | 15 | | range() | 创建一个发射指定范围的整数序列的Observable | 16 | | interval() | 创建一个按照给定的时间间隔发射整数序列的Observable | 17 | | timer() | 创建一个在给定的延时之后发射单个数据的Observable | 18 | | empty() | 创建一个什么都不做直接通知完成的Observable | 19 | | error() | 创建一个什么都不做直接通知错误的Observable | 20 | | never() | 创建一个不发射任何数据的Observable | 21 | 22 | ### 变换操作符:对Observable发射的数据执行变换操作的各种操作符 23 | | 操作符 | 功能描述 | 24 | | ---------------------------------------- | ---------------------------------------- | 25 | | map() | 对序列的每一项都应用一个函数来变换Observable发射的数据序列 | 26 | | flatMap(), concatMap(), flatMapIterable() | 扁平映射,将Observable发射的数据集合变换为Observables集合,然后将这些Observable发射的数据平坦化的放进一个单独的Observable,可以认为是一个将嵌套的数据结构展开的过程。 | 27 | | switchMap() | 将Observable发射的数据集合变换为Observables集合,然后只发射这些Observables最近发射的数据 | 28 | | scan() | 对Observable发射的每一项数据应用一个函数,然后按顺序依次发射每一个值 | 29 | | groupBy() | 将Observable分拆为Observable集合,将原始Observable发射的数据按Key分组,每一个Observable发射一组不同的数据 | 30 | | buffer() | 它定期从Observable收集数据到一个集合,然后把这些数据集合打包发射,而不是一次发射一个 | 31 | | window() | 定期将来自Observable的数据分拆成一些Observable窗口,然后发射这些窗口,而不是每次发射一项 | 32 | | cast() | 在发射之前强制将Observable发射的所有数据转换为指定类型 | 33 | 34 | ### 过滤操作符:用于过滤和选择Observable发射的数据序列 35 | 36 | | 操作符 | 功能描述 | 37 | | ----------------------------------- | ------------------------------------ | 38 | | filter() | 过滤数据 | 39 | | takeLast() | 只发射最后的N项数据 | 40 | | last() | 只发射最后的一项数据 | 41 | | lastOrDefault() | 只发射最后的一项数据,如果Observable为空就发射默认值 | 42 | | takeLastBuffer() | 将最后的N项数据当做单个数据发射 | 43 | | skip() | 跳过开始的N项数据 | 44 | | skipLast() | 跳过最后的N项数据 | 45 | | take() | 只发射开始的N项数据 | 46 | | first() and takeFirst() | 只发射第一项数据,或者满足某种条件的第一项数据 | 47 | | firstOrDefault() | 只发射第一项数据,如果Observable为空就发射默认值 | 48 | | elementAt() | 发射第N项数据 | 49 | | elementAtOrDefault() | 发射第N项数据,如果Observable数据少于N项就发射默认值 | 50 | | sample() or throttleLast() | 定期发射Observable最近的数据 | 51 | | throttleFirst() | 定期发射Observable发射的第一项数据 | 52 | | throttleWithTimeout() or debounce() | 只有当Observable在指定的时间后还没有发射数据时,才发射一个数据 | 53 | | timeout() | 如果在一个指定的时间段后还没发射数据,就发射一个异常 | 54 | | distinct() | 过滤掉重复数据 | 55 | | distinctUntilChanged() | 过滤掉连续重复的数据 | 56 | | ofType() | 只发射指定类型的数据 | 57 | | ignoreElements() | 丢弃所有的正常数据,只发射错误或完成通知 | 58 | 59 | ### 结合操作符:用于组合多个Observables的组合使用 60 | 61 | | 操作符 | 功能描述 | 62 | | ------------------------- | ---------------------------------------- | 63 | | startWith() | 在数据序列的开头增加一项数据 | 64 | | merge() | 将多个Observable合并为一个 | 65 | | mergeDelayError() | 合并多个Observables,让没有错误的Observable都完成后再发射错误通知 | 66 | | zip() | 使用一个函数组合多个Observable发射的数据集合,然后再发射这个结果 | 67 | | and(), then(), and when() | (rxjava-joins) 通过模式和计划组合多个Observables发射的数据集合 | 68 | | combineLatest() | 当两个Observables中的任何一个发射了一个数据时,通过一个指定的函数组合每个Observable发射的最新数据(一共两个数据),然后发射这个函数的结果 | 69 | | join() and groupJoin() | 无论何时,如果一个Observable发射了一个数据项,只要在另一个Observable发射的数据项定义的时间窗口内,就将两个Observable发射的数据合并发射 | 70 | | switchOnNext() | 将一个发射Observables的Observable转换成另一个Observable,后者发射这些Observables最近发射的数据 | 71 | 72 | ### 辅助操作符:用于bservable的辅助操作符 73 | 74 | | 操作符 | 功能描述 | 75 | | ---------------------------------- | ---------------------------------------- | 76 | | materialize() | 将Observable转换成一个通知列表convert an Observable into a list of Notifications | 77 | | dematerialize() | 将上面的结果逆转回一个Observable | 78 | | timestamp() | 给Observable发射的每个数据项添加一个时间戳 | 79 | | serialize() | 强制Observable按次序发射数据并且要求功能是完好的 | 80 | | cache() | 记住Observable发射的数据序列并发射相同的数据序列给后续的订阅者 | 81 | | observeOn() | 指定观察者观察Observable的调度器 | 82 | | subscribeOn() | 指定Observable执行任务的调度器 | 83 | | doOnEach() | 注册一个动作,对Observable发射的每个数据项使用 | 84 | | doOnCompleted() | 注册一个动作,对正常完成的Observable使用 | 85 | | doOnError() | 注册一个动作,对发生错误的Observable使用 | 86 | | doOnTerminate() | 注册一个动作,对完成的Observable使用,无论是否发生错误 | 87 | | doOnSubscribe() | 注册一个动作,在观察者订阅时使用 | 88 | | doOnUnsubscribe() | 注册一个动作,在观察者取消订阅时使用 | 89 | | finallyDo() | 注册一个动作,在Observable完成时使用 | 90 | | delay() | 延时发射Observable的结果 | 91 | | delaySubscription() | 延时处理订阅请求 | 92 | | timeInterval() | 定期发射数据 | 93 | | using() | 创建一个只在Observable生命周期存在的资源 | 94 | | single() | 强制返回单个数据,否则抛出异常 | 95 | | singleOrDefault() | 如果Observable完成时返回了单个数据,就返回它,否则返回默认数据 | 96 | | toFuture(), toIterable(), toList() | 将Observable转换为其它对象或数据结构 | -------------------------------------------------------------------------------- /RxJava系列教程/18.RxJava 教程第四部分:并发 之测试.md: -------------------------------------------------------------------------------- 1 | ## 18. 并发之测试 2 | 3 | 在开发软件的时候,我们需要确保代码正确执行。为了快速的获取每次修改后的反馈,通常开发人员使用自定义测试。 4 | 5 | 在同步的 Rx 中测试和普通 Java 中的单元测试没有太大的区别。如果要测试异步代码,可能会有点需要注意的地方,比如要测试下面的代码: 6 | 7 | ```java 8 | Observable.interval(1, TimeUnit.SECONDS) 9 | .take(5) 10 | ``` 11 | 12 | 上面的 Observable 发射一个数据流,需要 5秒 来发射完所有的数据。如果我们使用自动化测试这个代码,则是不是意味着测试代码也要执行 5秒,如果我们有成千上万个这样的测试,测试将消耗很多时间去完成。 13 | 14 | ### TestScheduler 15 | 16 | 上面示例的代码,5秒钟的时间其实大部分都在等待。如果我们可以加快系统时钟,则可以很快的完成数据流的发射。虽然实际操作中,无法加速系统时钟,但是可以加速一个虚拟的时钟。在 Rx 设计中,考虑到只在 scheduler 中使用时间相关的操作。这样可以用一个虚拟的 TestScheduler 来替代真实的 Scheduler。 17 | 18 | TestScheduler 和前面一节介绍的线程调度功能是一样的。调度的任务要么立刻执行,要么在将来某个时刻执行。区别在于 TestScheduler 中的时间是不动的,只有被调用了时间才会继续。 19 | 20 | ### advanceTimeTo 21 | 22 | advanceTimeTo 函数就是把 TestScheduler 中的时钟前进指定的时间刻度。 23 | 24 | ```java 25 | TestScheduler s = Schedulers.test(); 26 | 27 | s.createWorker().schedule( 28 | () -> System.out.println("Immediate")); 29 | s.createWorker().schedule( 30 | () -> System.out.println("20s"), 31 | 20, TimeUnit.SECONDS); 32 | s.createWorker().schedule( 33 | () -> System.out.println("40s"), 34 | 40, TimeUnit.SECONDS); 35 | 36 | System.out.println("Advancing to 1ms"); 37 | s.advanceTimeTo(1, TimeUnit.MILLISECONDS); 38 | System.out.println("Virtual time: " + s.now()); 39 | 40 | System.out.println("Advancing to 10s"); 41 | s.advanceTimeTo(10, TimeUnit.SECONDS); 42 | System.out.println("Virtual time: " + s.now()); 43 | 44 | System.out.println("Advancing to 40s"); 45 | s.advanceTimeTo(40, TimeUnit.SECONDS); 46 | System.out.println("Virtual time: " + s.now()); 47 | ``` 48 | 49 | 结果: 50 | 51 | ``` 52 | Advancing to 1ms 53 | Immediate 54 | Virtual time: 1 55 | Advancing to 10s 56 | Virtual time: 10000 57 | Advancing to 40s 58 | 20s 59 | 40s 60 | Virtual time: 40000 61 | ``` 62 | 63 | 上面示例中创建的 3 个任务,第一个任务立刻执行,第二个和第三个在将来执行。可以看到如果不调用 advanceTimeTo 来使时间前进,则所有任务都不会执行,因为在 TestScheduler 中时间是停止的。当时间前进的时候, TestScheduler 会同步执行所有满足时间条件的任务。 64 | 65 | advanceTimeTo 可以设置时间为任意时刻。也可以回到过去(设置的时间比当前的时间还早)。所以这个函数如果不注意使用,可能会导致不可预见的 bug。一般建议使用下面这个函数。 66 | 67 | ### advanceTimeBy 68 | 69 | advanceTimeBy 顾名思义,在当前时间基础上前进多少。 70 | 71 | ```java 72 | TestScheduler s = Schedulers.test(); 73 | 74 | s.createWorker().schedule( 75 | () -> System.out.println("Immediate")); 76 | s.createWorker().schedule( 77 | () -> System.out.println("20s"), 78 | 20, TimeUnit.SECONDS); 79 | s.createWorker().schedule( 80 | () -> System.out.println("40s"), 81 | 40, TimeUnit.SECONDS); 82 | 83 | System.out.println("Advancing by 1ms"); 84 | s.advanceTimeBy(1, TimeUnit.MILLISECONDS); 85 | System.out.println("Virtual time: " + s.now()); 86 | 87 | System.out.println("Advancing by 10s"); 88 | s.advanceTimeBy(10, TimeUnit.SECONDS); 89 | System.out.println("Virtual time: " + s.now()); 90 | 91 | System.out.println("Advancing by 40s"); 92 | s.advanceTimeBy(40, TimeUnit.SECONDS); 93 | System.out.println("Virtual time: " + s.now()); 94 | ``` 95 | 96 | 结果: 97 | 98 | ``` 99 | Advancing by 1ms 100 | Immediate 101 | Virtual time: 1 102 | Advancing by 10s 103 | Virtual time: 10001 104 | Advancing by 40s 105 | 20s 106 | 40s 107 | Virtual time: 50001 108 | ``` 109 | 110 | ### triggerActions 111 | 112 | triggerActions 不会修改时间。只是用来执行当前可以调度的任务。 113 | 114 | ```java 115 | TestScheduler s = Schedulers.test(); 116 | 117 | s.createWorker().schedule( 118 | () -> System.out.println("Immediate")); 119 | s.createWorker().schedule( 120 | () -> System.out.println("20s"), 121 | 20, TimeUnit.SECONDS); 122 | 123 | s.triggerActions(); 124 | System.out.println("Virtual time: " + s.now()); 125 | ``` 126 | 127 | 结果: 128 | 129 | ``` 130 | Immediate 131 | Virtual time: 012 132 | ``` 133 | 134 | ### 调度冲突 135 | 136 | 有些任务可能在同一时刻执行。如果发生这种情况,则被称之为 调度冲突。 这些任务调度的顺序就是他们执行的顺序(也就是按照顺序执行) 137 | 138 | ```java 139 | TestScheduler s = Schedulers.test(); 140 | 141 | s.createWorker().schedule( 142 | () -> System.out.println("First"), 143 | 20, TimeUnit.SECONDS); 144 | s.createWorker().schedule( 145 | () -> System.out.println("Second"), 146 | 20, TimeUnit.SECONDS); 147 | s.createWorker().schedule( 148 | () -> System.out.println("Third"), 149 | 20, TimeUnit.SECONDS); 150 | 151 | s.advanceTimeTo(20, TimeUnit.SECONDS); 152 | ``` 153 | 154 | 结果: 155 | 156 | ``` 157 | First 158 | Second 159 | Third 160 | ``` 161 | 162 | ### 测试 163 | 164 | Rx 的 Observable的 大部分操作函数都有一个可以指定 Scheduler 的重载形式。在这些函数上同样可以使用 TestScheduler。 165 | 166 | ```java 167 | @Test 168 | public void test() { 169 | TestScheduler scheduler = new TestScheduler(); 170 | List expected = Arrays.asList(0L, 1L, 2L, 3L, 4L); 171 | List result = new ArrayList<>(); 172 | Observable 173 | .interval(1, TimeUnit.SECONDS, scheduler) 174 | .take(5) 175 | .subscribe(i -> result.add(i)); 176 | assertTrue(result.isEmpty()); 177 | scheduler.advanceTimeBy(5, TimeUnit.SECONDS); 178 | assertTrue(result.equals(expected)); 179 | } 180 | ``` 181 | 182 | 这样测试代码就可以很开的完成,比较适合测试简短的 Rx 代码。在实际代码中,可以把获取 Scheduler 的函数封装起来,在 debug 版本中使用 TestScheduler ,而在发布版本中使用真实的 Scheduler。 183 | 184 | ### TestSubscriber 185 | 186 | 上面的测试中,我们手工的收集发射的数据并根据期望的数据去对比,来判断测试是否成功。由于这样的测试很常见,Rx 提供了一个 TestSubscriber 来帮助简化测试过程。 前面的测试代码使用 TestSubscriber 可以变为这样: 187 | 188 | ```java 189 | @Test 190 | public void test() { 191 | TestScheduler scheduler = new TestScheduler(); 192 | TestSubscriber subscriber = new TestSubscriber<>(); 193 | List expected = Arrays.asList(0L, 1L, 2L, 3L, 4L); 194 | Observable 195 | .interval(1, TimeUnit.SECONDS, scheduler) 196 | .take(5) 197 | .subscribe(subscriber); 198 | assertTrue(subscriber.getOnNextEvents().isEmpty()); 199 | scheduler.advanceTimeBy(5, TimeUnit.SECONDS); 200 | subscriber.assertReceivedOnNext(expected); 201 | } 202 | ``` 203 | 204 | TestSubscriber 不仅仅只收集数据,还有如下一些函数: 205 | 206 | ``` 207 | java.lang.Thread getLastSeenThread() 208 | java.util.List> getOnCompletedEvents() 209 | java.util.List getOnErrorEvents() 210 | java.util.List getOnNextEvents() 211 | ``` 212 | 213 | 有两点需要额外注意: 214 | 215 | 1. getLastSeenThread 函数。 TestSubscriber 会检查在那个线程执行回调函数,并记录最后一个线程。如果你想测试回调函数是否发生在 GUI 线程,则可以使用这个函数。 216 | 2. 有趣的是 getOnCompletedEvents 可以返回多个结束事件。这是违反 Rx 约定的情况,可以通过测试来检查。 217 | 218 | TestSubscriber 还提供了一些常见的判断函数: 219 | 220 | ```java 221 | void assertNoErrors() 222 | void assertReceivedOnNext(java.util.List items) 223 | void assertTerminalEvent() 224 | void assertUnsubscribed() 225 | ``` 226 | 227 | 另外还可以阻塞直到特定的事件发生: 228 | 229 | ```java 230 | void awaitTerminalEvent() 231 | void awaitTerminalEvent(long timeout, java.util.concurrent.TimeUnit unit) 232 | void awaitTerminalEventAndUnsubscribeOnTimeout(long timeout, java.util.concurrent.TimeUnit unit) 233 | ``` 234 | 235 | 指定时间可能会导致超时的异常(没有在规定的时间内完成)。 236 | -------------------------------------------------------------------------------- /RxJava系列教程/11.RxJava 教程第三部分:驯服数据流之 高级错误处理.md: -------------------------------------------------------------------------------- 1 | # 11. 驯服数据流之高级错误处理 2 | 3 | 在实际项目代码中可能出现各种各样的异常情况,并不是每一个异常都需要告诉上层代码的。在传统的 Java 中,你可以捕获一个异常,然后决定是自己处理该异常还是再次抛出去。同样,在 RxJava 中,你也可以根据异常来执行不同的逻辑而无需结束 Observable,也不再强迫 Observer 处理所有情况。 4 | 5 | ## Resume 6 | 7 | ### onErrorReturn 8 | 9 | onErrorReturn 操作函数的功能是:当发生错误的时候,发射一个默认值然后结束数据流。所以 Subscriber 看不到异常信息,看到的是正常的数据流结束状态。 10 | 11 | ![RxJava](images/高级错误处理_01.png) 12 | 13 | ```java 14 | Observable values = Observable.create(o -> { 15 | o.onNext("Rx"); 16 | o.onNext("is"); 17 | o.onError(new Exception("adjective unknown")); 18 | }); 19 | 20 | values 21 | .onErrorReturn(e -> "Error: " + e.getMessage()) 22 | .subscribe(v -> System.out.println(v)); 23 | ``` 24 | 25 | 结果: 26 | 27 | ``` 28 | Rx 29 | is 30 | Error: adjective unknown 31 | ``` 32 | 33 | ### onErrorResumeNext 34 | 35 | onErrorResumeNext 的功能是:当错误发生的时候,使用另外一个数据流继续发射数据。在返回的 Observable 中是看不到错误信息的。 36 | 37 | ```java 38 | public final Observable onErrorResumeNext( 39 | Observable resumeSequence) 40 | public final Observable onErrorResumeNext( 41 | Func1> resumeFunction) 42 | ``` 43 | 44 | ![RxJava](images/高级错误处理_02.png) 45 | 46 | 第二个重载的函数可以根据错误的信息来返回不同的 Observable。 47 | 48 | ```java 49 | Observable values = Observable.create(o -> { 50 | o.onNext(1); 51 | o.onNext(2); 52 | o.onError(new Exception("Oops")); 53 | }); 54 | 55 | values 56 | .onErrorResumeNext(Observable.just(Integer.MAX_VALUE)) 57 | .subscribe(new PrintSubscriber("with onError: ")); 58 | ``` 59 | 60 | 结果: 61 | 62 | ``` 63 | with onError: : 1 64 | with onError: : 2 65 | with onError: : 2147483647 66 | with onError: : Completed1234 67 | ``` 68 | 69 | 利用这个操作函数可以实现把一个异常信息包装起来再次抛出。在传统的 Java 中,如果异常发生的时候发现当前无法处理该异常,则会再次抛出该异常。通常情况下都会包装(Wrap)一下异常信息再抛出。在 Rx 中也可以这样用: 70 | 71 | ```java 72 | .onErrorResumeNext(e -> Observable.error(new UnsupportedOperationException(e))) 73 | ``` 74 | 75 | ### onExceptionResumeNext 76 | 77 | onExceptionResumeNext 和 onErrorResumeNext 的区别是只捕获 Exception; 78 | 79 | ```java 80 | Observable values = Observable.create(o -> { 81 | o.onNext("Rx"); 82 | o.onNext("is"); 83 | //o.onError(new Throwable() {}); // 这个为 error 不会捕获 84 | o.onError(new Exception()); // 这个为 Exception 会被捕获 85 | }); 86 | 87 | values 88 | .onExceptionResumeNext(Observable.just("hard")) 89 | .subscribe(v -> System.out.println(v)); 90 | ``` 91 | 92 | Retry 93 | 94 | 如果发生了不定性的异常,则通常会重试一下看看是否正常了。 retry 的功能就算重新订阅到事件流,并重头重新开始发射数据。 95 | 96 | ```java 97 | public final Observable retry() 98 | public final Observable retry(long count)12 99 | ``` 100 | 101 | ![RxJava](images/高级错误处理_03.png) 102 | 103 | 没有参数的 retry() 函数会一直重试,直到没有异常发生为止。而带有参数的 retry(n) 函数会重试 N 次, 如果 N 次后还是失败,则不再重试了,数据流发射一个异常信息并结束。 104 | 105 | ```java 106 | Random random = new Random(); 107 | Observable values = Observable.create(o -> { 108 | o.onNext(random.nextInt() % 20); 109 | o.onNext(random.nextInt() % 20); 110 | o.onError(new Exception()); 111 | }); 112 | 113 | values 114 | .retry(1) 115 | .subscribe(v -> System.out.println(v)); 116 | ``` 117 | 118 | 结果: 119 | 120 | ``` 121 | java.lang.Exception1 122 | ``` 123 | 124 | 上面的示例,发射了两个数字遇到异常信息,然后重试一次,又发射 两个数据遇到异常信息,然后抛出该异常并结束。 125 | 126 | **请注意:上面的示例中两次发射的数字不一样。说明 retry 并不像 replay 一样会缓存之前的数据。一般情况下,这样的情况都是不合理的。所以一般情况下,只有具有副作用的时候或者 Observable 是 hot 的时候 才应该使用 retry。** 127 | 128 | ### retryWhen 129 | 130 | retryWhen 更具有控制力。 131 | 132 | ```java 133 | public final Observable retryWhen( 134 | Func1,? extends Observable> notificationHandler) 135 | ``` 136 | 137 | retryWhen 的参数是一个函数, 该函数的输入参数为一个异常 Observable,返回值为另外一个 Observable。 输入参数中包含了 retryWhen 发生时候遇到的异常信息;返回的 Observable 为一个信号,用来判别何时需要重试的: 138 | 139 | - 如果返回的 Observable 发射了一个数据,retryWhen 将会执行重试操作 140 | - 如果返回的 Observable 发射了一个错误信息,retryWhen 将会发射一个错误并不会重试 141 | - 如果返回的 Observable 正常结束了,retryWhen 也正常结束。 142 | 143 | 参数返回的 Observable 发射的数据类型是无关紧要的。该 Observable 的数据只是用来当做是否重试的信号。数据本身是无用的。 144 | 145 | 下面一个示例,构造一个等待 100 毫秒再重试的机制: 146 | 147 | ```java 148 | Observable source = Observable.create(o -> { 149 | o.onNext(1); 150 | o.onNext(2); 151 | o.onError(new Exception("Failed")); 152 | }); 153 | 154 | source.retryWhen((o) -> o 155 | .take(2) 156 | .delay(100, TimeUnit.MILLISECONDS)) 157 | .timeInterval() 158 | .subscribe( 159 | System.out::println, 160 | System.out::println); 161 | ``` 162 | 163 | 结果: 164 | 165 | ``` 166 | TimeInterval [intervalInMilliseconds=21, value=1] 167 | TimeInterval [intervalInMilliseconds=0, value=2] 168 | TimeInterval [intervalInMilliseconds=104, value=1] 169 | TimeInterval [intervalInMilliseconds=0, value=2] 170 | TimeInterval [intervalInMilliseconds=103, value=1] 171 | TimeInterval [intervalInMilliseconds=0, value=2] 172 | ``` 173 | 174 | 源 Observable 发射两个数字 然后遇到异常;当异常发生的时候,retryWhen 返回的 判断条件 Observable 会获取到这个异常,这里等待 100毫秒然后把这个异常当做数据发射出去告诉 retryWhen 开始重试。take(2) 参数确保判断条件 Observable 只发射两个数据(源 Observable 出错两次)然后结束。所以当源 Observable 出现两次错误以后就不再重试了。 175 | 176 | ### using 177 | 178 | using 操作函数是用来管理资源的,如果一个 Observable 需要使用一个资源来发射数据(比如 需要使用一个文件资源,从文件中读取内容),当该 Observable 结束的时候(不管是正常结束还是异常结束)就释放该资源。这样你就不用自己管理资源了, 用 Rx 的方式来管理资源。 179 | 180 | ```java 181 | public static final Observable using( 182 | Func0 resourceFactory, 183 | Func1> observableFactory, 184 | Action1 disposeAction) 185 | ``` 186 | 187 | using 有三个参数。当 Observable 被订阅的时候,resourceFactory 用来获取到需要的资源;observableFactory 用这个资源来发射数据;当 Observable 完成的时候,disposeAction 来释放资源。 188 | 189 | 下面的示例中,假设 String 是一个需要管理的资源。 190 | 191 | ```java 192 | Observable values = Observable.using( 193 | () -> { 194 | String resource = "MyResource"; 195 | System.out.println("Leased: " + resource); 196 | return resource; 197 | }, 198 | (resource) -> { 199 | return Observable.create(o -> { 200 | for (Character c : resource.toCharArray()) 201 | o.onNext(c); 202 | o.onCompleted(); 203 | }); 204 | }, 205 | (resource) -> System.out.println("Disposed: " + resource)); 206 | 207 | values 208 | .subscribe( 209 | v -> System.out.println(v), 210 | e -> System.out.println(e)); 211 | ``` 212 | 213 | 结果: 214 | 215 | ``` 216 | Leased: MyResource 217 | M 218 | y 219 | R 220 | e 221 | s 222 | o 223 | u 224 | r 225 | c 226 | e 227 | Disposed: MyResource 228 | ``` 229 | 230 | 当订阅到 values 的时候, 调用 resourceFactory 函数返回一个字符串 “MyResource”;observableFactory 使用返回的 “MyResource” 字符串来生成一个 Observable, 该 Observable 发射”MyResource” 字符串中的每个字符;当发生完成的时候, disposeAction 来释放这个字符串资源。 231 | 232 | 有一点需要注意: 和使用 create 创建 Observable 一样,我们需要自己来结束 Observable 的发射(onCompleted 的调用)。如果你没有结束 Observable,则资源是永远不会释放的。 233 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 给 Android 开发者的 RxJava 详解 2 | 3 | ## GitHub托管 4 | 5 | https://github.com/JackChan1999/RxJava_Docs_For_Android_Developer 6 | 7 | ## GitBook在线阅读 8 | 9 | 在线阅读,PDF、ePub、Mobi电子书下载 10 | 11 | https://www.gitbook.com/book/alleniverson/rxjava-docs-for-android-developer/details 12 | 13 | ## ReactiveX/RxJava文档中文版 14 | 15 | https://www.gitbook.com/book/alleniverson/rxdocs/welcome 16 | 17 | ## RxJava 视频教程 18 | 19 | ![](assets/rxjava_vedio_2.png) 20 | 21 | ![](assets/rxjava_vedio.png) 22 | 23 | ![](assets/rxjava_vedio_3.png) 24 | 25 | ## 目录 26 | 27 | * [给 Android 开发者的 RxJava 详解](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava1.0/给Android开发者的RxJava详解.html) 28 | * [RxJava 与 Retrofit 结合的最佳实践](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava1.0/RxJava与Retrofit结合的最佳实践.html) 29 | * [给初学者的RxJava2.0教程-1](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-1.html) 30 | * [给初学者的RxJava2.0教程-2](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-2.html) 31 | * [给初学者的RxJava2.0教程-3](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-3.html) 32 | * [给初学者的RxJava2.0教程-4](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-4.html) 33 | * [给初学者的RxJava2.0教程-5](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-5.html) 34 | * [给初学者的RxJava2.0教程-6](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-6.html) 35 | * [给初学者的RxJava2.0教程-7](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-7.html) 36 | * [给初学者的RxJava2.0教程-8](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-8.html) 37 | * [给初学者的RxJava2.0教程-9](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava2.0/给初学者的RxJava2.0教程-9.html) 38 | * [RxJava系列教程](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\README.html) 39 | * [Intro To RxJava 系列教程](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\0.Intro%20To%20RxJava%20系列教程.html) 40 | * [入门之 Why Rx](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\1.RxJava%20教程第一部分:入门之%20Why%20Rx.html) 41 | * [入门之 关键的类](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\2.RxJava%20教程第一部分:入门之%20关键的类.html) 42 | * [入门之 生命周期管理](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\3.RxJava%20教程第一部分:入门之%20生命周期管理.html) 43 | * [RxJava 前传 1](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\RxJava系列教程\28.RxJava%20前传%201.html) 44 | * [RxJava 前传 2](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\RxJava系列教程\29.RxJava%20前传%202.html) 45 | * [RxJava 前传 3](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\RxJava系列教程\30.RxJava%20前传%203.html) 46 | * [事件流基础之 创建事件流](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\4.RxJava%20教程第二部分:事件流基础之%20创建事件流.html) 47 | * [事件流基础之 过滤数据](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\5.RxJava%20教程第二部分:事件流基础之%20过滤数据.html) 48 | * [事件流基础之 检查数据](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\6.RxJava%20教程第二部分:事件流基础之%20检查数据.html) 49 | * [事件流基础之 聚合](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\7.RxJava%20教程第二部分:事件流基础之%20聚合.html) 50 | * [事件流基础之 转换数据流](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\8.RxJava%20教程第二部分:事件流基础之%20转换数据流.html) 51 | * [驯服数据流之副作用](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\9.RxJava%20教程第三部分:驯服数据流之副作用.html) 52 | * [驯服数据流之 避免 monad](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\10.RxJava%20教程第三部分:驯服数据流之%20避免%20monad.html) 53 | * [驯服数据流之 高级错误处理](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\11.RxJava%20教程第三部分:驯服数据流之%20高级错误处理.html) 54 | * [驯服数据流之 组合数据流](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\12.RxJava%20教程第三部分:驯服数据流之%20组合数据流.html) 55 | * [Android 专用响应式编程框架 — Agera](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\13.Android%20专用响应式编程框架%20—%20Agera.html) 56 | * [驯服数据流之 时间平移](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\14.RxJava%20教程第三部分:驯服数据流之%20时间平移.html) 57 | * [驯服数据流之 hot & cold Observable](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\15.RxJava%20教程第三部分:驯服数据流之%20hot%20&%20cold%20Observable.html) 58 | * [驯服数据流之自定义操作函数](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\16.RxJava%20教程第三部分:驯服数据流之自定义操作函数.html) 59 | * [并发 之线程调度](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\17.RxJava%20教程第四部分:并发%20之线程调度.html) 60 | * [并发 之测试](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\18.RxJava%20教程第四部分:并发%20之测试.html) 61 | * [并发 之意外情况处理](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\19.RxJava%20教程第四部分:并发%20之意外情况处理.html) 62 | * [并发 之数据流发射太快如何办](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\20.RxJava%20教程第四部分:并发%20之数据流发射太快如何办.html) 63 | * [Intro To RxJava 系列教程 总结](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\21.Intro%20To%20RxJava%20系列教程%20总结.html) 64 | * [Google Agera 从入门到放弃](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\22.Google%20Agera%20从入门到放弃.html) 65 | * [RxJava 参考资源](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\23.RxJava%20参考资源.html) 66 | * [RxJava 的 compose() 操作函数实战](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\24.RxJava%20的%20compose%20操作函数实战.html) 67 | * [不该使用 RxJava 的一些情况](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\25.不该使用%20RxJava%20的一些情况.html) 68 | * [RxJava 常见的错误用法](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\26.RxJava%20常见的错误用法.html) 69 | * [RxJava Android 开发全家桶](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava系列教程\27.RxJava%20Android%20开发全家桶.html) 70 | * [Rxjava操作符大全](https://alleniverson.gitbooks.io/rxjava-docs-for-android-developer/content/RxJava1.0/RxJava1.0/Rxjava操作符大全.html) 71 | 72 | ## 关注我 73 | 74 | - Email:<619888095@qq.com> 75 | - CSDN博客:[Allen Iverson](http://blog.csdn.net/axi295309066) 76 | - 新浪微博:[AndroidDeveloper](http://weibo.com/u/1848214604?topnav=1&wvr=6&topsug=1&is_all=1) 77 | - GitHub:[JackChan1999](https://github.com/JackChan1999) 78 | - GitBook:[alleniverson](https://www.gitbook.com/@alleniverson) 79 | - 个人博客:[JackChan](https://jackchan1999.github.io/) 80 | -------------------------------------------------------------------------------- /RxJava系列教程/2.RxJava 教程第一部分:入门之 关键的类.md: -------------------------------------------------------------------------------- 1 | # 2. 入门之关键的类 2 | 3 | Rx 有两个最基础的类型,和其他一些扩展这两种类型的类。两个核心的类为: Observable 和 Observer。Subject 是同时继承了 Observable 和 Observer。 4 | 5 | Rx 是在 Observer 模式之上建立起来的。这种模式很常见,在 Java 中有很多地方都使用了该模式,比如 JavaFx 中的 EventHandler。 这些简单的使用方式和 Rx 对比有如下区别: 6 | 7 | - 使用 event handler 来处理事件很难组合使用 8 | - 无法延时处理查询事件 9 | - 可能会导致内存泄露 10 | - 没有标准的标示完成的方式 11 | - 需要手工的来处理并行和多线程 12 | 13 | ## Observable 14 | 15 | Observable 是第一个核心类。该类包含了 Rx 中的很多实现,以及所有核心的操作函数(operator、或者说 操作符)。在本系列教程中会逐步介绍每个操作函数。现在我们只需要理解 subscribe 函数即可,下面是该函数的一种定义: 16 | 17 | ```java 18 | public final Subscription subscribe(Subscriber subscriber) 19 | ``` 20 | 21 | 该函数是用来接收 observable 发射的事件的。当事件被发射后,他们就丢给了 subscriber, subscriber 是用来处理事件的实现。这里的 Subscriber 参数实现了 Observer 接口。 22 | 23 | 一个 Observable 发射三种类型的事件: 24 | 25 | - Values (数据) 26 | - 完成状态,告诉 Subscriber 事件(数据) 发射完毕,没有其他数据了 27 | - Error, 错误状态,如果在发射数据的过程中出现错误了。会发送该事件。 28 | 29 | ## Observer 30 | 31 | Subscriber 是 Observer 的一个实现。 Subscriber 实现了其他一些额外的功能,可以作为我们实现 Observer 的基类。现在先看看 Observer 的接口定义: 32 | 33 | ```java 34 | interface Observer { 35 | void onCompleted(); 36 | void onError(java.lang.Throwable e); 37 | void onNext(T t); 38 | } 39 | ``` 40 | 41 | 每次 Observable 发射事件的时候就会执行这三个对应的函数。Observer 的 onNext 函数会被调用0次或者多次,然后会调用 onCompleted 或者 onError。在 onCompleted 或者 onError 发生以后就不会再有其他事件发射出来了。 42 | 43 | 在使用 Rx 开发的过程中,你会看到很多 Observable,但是 Observer 出场的时候很少。但是理解 Observer 的概念是非常重要的,虽然有很多简写方式来帮助更加简洁的使用 Observer。 44 | 45 | ## 实现 Observable 和 Observer 46 | 47 | 你可以手工的实现 Observer 或者扩展 Observable。 在真实场景中并不需要这样做,Rx 已经提供了很多可以直接使用的工厂方法了。使用 Rx 提供的工具来创建 Observable 和 Observer 比手工实现要更加安全和简洁。 48 | 49 | 要订阅到一个 Observable,并不需要提供一个 Observer 示例。subscribe 函数有各种重载方法可以使用,你可以只订阅 onNext 事件,有可以只订阅 onError 事件,这样就不用提供 Observer 对象就可以接受事件了。每次只需要提供你关心的函数即可,例如 如果你不关心 error 和完成事件,则只提供 onNext 来接收每次发送的数据即可。 50 | 51 | 配合 Java 8 的 Lambda 表达式则使用起来代码看起来会更加简洁,所以本系列示例代码会使用 lambda 表达式,如果你不了解的话,可以先看看掌握 Java 8 Lambda 表达式。 52 | 53 | ## Subject 54 | 55 | Subject 是 Observable 的一个扩展,同时还实现了 Observer 接口。第一眼看上去可能有点奇怪,但是在有些场合下使用 Subject 将会非常便捷。他们可以像 Observer 一样接收事件,同时还可以像 Observable 一样把接收到的事件再发射出去。这种特性非常适合 Rx 中的接入点,当你的事件来自于 Rx 框架之外的代码的时候,你可以把这些数据先放到 Subject 中,然后再把 Subject 转换为一个 Observable,就可以在 Rx 中使用它们了。你可以把 Subject 当做 Rx 中的 事件管道。 56 | 57 | Subject 有两个参数类型:输入参数和输出参数。这样设计是为了抽象而不是因为使用 Subject 是为了转换数据类型。转换数据应该使用转换操作函数来完成,后面我们将介绍各种操作函数。 58 | 59 | Subject 有各种不同的具体实现。下面将介绍一些非常重要的实现以及他们之间的区别。 60 | 61 | ## PublishSubject 62 | 63 | PublishSubject 是最直接的一个 Subject。当一个数据发射到 PublishSubject 中时,PublishSubject 将立刻把这个数据发射到订阅到该 subject 上的所有 subscriber 中。 64 | 65 | ```java 66 | public static void main(String[] args) { 67 | PublishSubject subject = PublishSubject.create(); 68 | subject.onNext(1); 69 | subject.subscribe(System.out::println); 70 | subject.onNext(2); 71 | subject.onNext(3); 72 | subject.onNext(4); 73 | } 74 | ``` 75 | 76 | 输出结果: 77 | 78 | ``` 79 | 2 80 | 3 81 | 4 82 | ``` 83 | 84 | > 上面的 System.out::println 是 Lambda 表达式中的函数引用,如果表达式代码块只有一个函数调用,则可以直接使用函数引用来简化代码 85 | 86 | 可以看到,数据 1 并没有打印出来,原因是当我们订阅到 subject 的时候,1 已经发射出去了。当订阅到 subject 后就开始接收 发射到 subject 中的数据了。 87 | 88 | 这是我们初次看到如何使用 subscribe 函数,值得详细研究下是如何用的。 这里我们使用了一个重载的参数只有一个 Function 类型。这个参数 Function 接收一个参数 Integer 并且没有返回值。 没有返回值的 Function 在 Rx 中被称之为 action。 可以使用下面几种方式来提供这个 Function: 89 | 90 | - 提供一个 Action1 的实现对象 91 | - 使用 Lambda 表达式 实现 92 | - 使用符合该接口定义类型的 Lambda 表达式函数引用。这里 System.out::println 函数可以接受一个 Object 对象,符合 Action 的定义(接受一个参数并没有返回值),所以我们可以把该函数作为函数应用使用。 subscribe 将会使用他收到的值作为 println 函数的参数来调用 println 函数。 93 | 94 | ## ReplaySubject 95 | 96 | ReplaySubject 可以缓存所有发射给他的数据。当一个新的订阅者订阅的时候,缓存的所有数据都会发射给这个订阅者。 由于使用了缓存,所以每个订阅者都会收到所以的数据: 97 | 98 | ```java 99 | ReplaySubject s = ReplaySubject.create(); 100 | s.subscribe(v -> System.out.println("Early:" + v)); 101 | s.onNext(0); 102 | s.onNext(1); 103 | s.subscribe(v -> System.out.println("Late: " + v)); 104 | s.onNext(2);123456 105 | ``` 106 | 107 | 输出结果: 108 | 109 | ``` 110 | Early:0 111 | Early:1 112 | Late: 0 113 | Late: 1 114 | Early:2 115 | Late: 2 116 | ``` 117 | 118 | 不管是何时订阅的,每个订阅者都收到了所有的数据。注意后一个订阅者在处理 2 之前就先收到了之前发射的数据 0和1. 119 | 120 | 缓存所有的数据并不是一个十分理想的情况,如果 Observable 事件流运行很长时间,则缓存所有的数据会消耗很多内存。可以限制缓存数据的数量和时间。 ReplaySubject.createWithSize 限制缓存多少个数据;而 ReplaySubject.createWithTime 限制一个数据可以在缓存中保留多长时间。 121 | 122 | ```java 123 | ReplaySubject s = ReplaySubject.createWithSize(2); 124 | s.onNext(0); 125 | s.onNext(1); 126 | s.onNext(2); 127 | s.subscribe(v -> System.out.println("Late: " + v)); 128 | s.onNext(3);123456 129 | ``` 130 | 131 | 输出结果: 132 | 133 | ``` 134 | Late: 1 135 | Late: 2 136 | Late: 3 137 | ``` 138 | 139 | 由于指定只缓存两个数据,所以当订阅的时候第一个数据 0 就收不到了。 限制缓存的时间也是一样的情况: 140 | 141 | ```java 142 | ReplaySubject s = ReplaySubject.createWithTime(150, TimeUnit.MILLISECONDS, 143 | Schedulers.immediate()); 144 | s.onNext(0); 145 | Thread.sleep(100); 146 | s.onNext(1); 147 | Thread.sleep(100); 148 | s.onNext(2); 149 | s.subscribe(v -> System.out.println("Late: " + v)); 150 | s.onNext(3); 151 | ``` 152 | 153 | 输出结果: 154 | 155 | ``` 156 | Late: 1 157 | Late: 2 158 | Late: 3 159 | ``` 160 | 161 | 使用时间缓存创建 ReplaySubject 需要指定一个 Scheduler, Scheduler 是 Rx 中保持时间的方式。现在可以假装他不存在,不用关心他。 162 | 163 | ReplaySubject.createWithTimeAndSize 则可以同时限制时间和个数。 164 | 165 | ## BehaviorSubject 166 | 167 | BehaviorSubject 只保留最后一个值。 等同于限制 ReplaySubject 的个数为 1 的情况。在创建的时候可以指定一个初始值,这样可以确保党订阅者订阅的时候可以立刻收到一个值。 168 | 169 | ```java 170 | BehaviorSubject s = BehaviorSubject.create(); 171 | s.onNext(0); 172 | s.onNext(1); 173 | s.onNext(2); 174 | s.subscribe(v -> System.out.println("Late: " + v)); 175 | s.onNext(3); 176 | ``` 177 | 178 | 输出结果: 179 | 180 | ``` 181 | Late: 2 182 | Late: 3 183 | ``` 184 | 185 | 下面的示例只是打印出 Completed, 由于最后一个事件就是 Completed。 186 | 187 | ```java 188 | BehaviorSubject s = BehaviorSubject.create(); 189 | s.onNext(0); 190 | s.onNext(1); 191 | s.onNext(2); 192 | s.onCompleted(); 193 | s.subscribe( 194 | v -> System.out.println("Late: " + v), 195 | e -> System.out.println("Error"), 196 | () -> System.out.println("Completed") 197 | ); 198 | ``` 199 | 200 | > 这里使用了 subscribe 函数的另外一种重载形式,接受三个参数。 201 | 202 | 下面使用了默认初始化值,如果订阅者的发射数据之前就订阅了,则会收到这个初始化的值: 203 | 204 | ``` 205 | BehaviorSubject s = BehaviorSubject.create(0); 206 | s.subscribe(v -> System.out.println(v)); 207 | s.onNext(1); 208 | ``` 209 | 210 | 输出结果: 211 | 212 | ``` 213 | 0 214 | 1 215 | ``` 216 | 217 | 由于 BehaviorSubject 的定义就是总是有可用的数据,所以一般都会使用初始化值来创建 BehaviorSubject 。 218 | 219 | ## AsyncSubject 220 | 221 | AsyncSubject 也缓存最后一个数据。区别是 AsyncSubject 只有当数据发送完成时(onCompleted 调用的时候)才发射这个缓存的最后一个数据。可以使用 AsyncSubject 发射一个数据并立刻结束。 222 | 223 | ```java 224 | AsyncSubject s = AsyncSubject.create(); 225 | s.subscribe(v -> System.out.println(v)); 226 | s.onNext(0); 227 | s.onNext(1); 228 | s.onNext(2); 229 | s.onCompleted(); 230 | ``` 231 | 232 | 输出结果: 233 | 234 | ``` 235 | 2 236 | ``` 237 | 238 | 如果上面的示例不调用 s.onCompleted(); 则什么结果都不会打印出来。 239 | 240 | ## 隐含的规则 241 | 242 | Rx 中有一些隐含的规则在代码中并不太容易看到。一个重要的规则就是当一个事件流结束(onError 或者 onCompleted 都会导致事件流结束)后就不会发射任何数据了。这些 Subject 的实现都遵守这个规则,subscribe 函数也拒绝违反该规则的情况。 243 | 244 | ```java 245 | Subject s = ReplaySubject.create(); 246 | s.subscribe(v -> System.out.println(v)); 247 | s.onNext(0); 248 | s.onCompleted(); 249 | s.onNext(1); 250 | s.onNext(2); 251 | ``` 252 | 253 | 结果: 254 | 255 | ``` 256 | 0 257 | ``` 258 | 259 | 但是在 Rx 实现中并没有完全确保这个规则,所以你在使用 Rx 的过程中要注意遵守该规则,否则会出现意料不到的情况。 260 | -------------------------------------------------------------------------------- /RxJava系列教程/25.不该使用 RxJava 的一些情况.md: -------------------------------------------------------------------------------- 1 | ## 25. 不该使用 RxJava 的一些情况 2 | 3 | Reactive programming 是一种改变游戏规则的技术。如果您正确的使用它,则会改变您的编程方式。一年之前笔者(原文作者,下同)开始接触 RxJava 并尝试使用 RxJava 来处理 UI 事件(并且成为了 [RxJavaFX](https://github.com/ReactiveX/RxJavaFX/) 的管理者)。在使用 RxJava 一段时间后,笔者发现 RxJava 能干很多事。 并且改变了很多编程的方式和方法,从 并发到 IO 以及 业务逻辑和算法。 4 | 5 | 笔者开始到处使用 RxJava ,只要是可以用的地方就开始使用 RxJava,这样也可以更快的掌握 RxJava 的用法。 6 | 7 | 一年以后,笔者发现 RxJava 并不总是最佳的解决方案。虽然,现在笔者所写的每个应用都是 reactive 的,但是现在在有些地方可能会选择不使用 reactive 的方式来编程。需要注意的是,把任何东西转换为 Observable 是很容易的。 所以本篇文章主要介绍何时您提供的 API 应该返回非 Observable 的数据类型,如果客户端调用你的 API 的时候想使用 Observable ,则他们自己可以很容易的转换为 Observable。 8 | 9 | ## 第一种情况:简单的常量集合数据 10 | 11 | 这是最简单的一种不适合使用 Observable 的情况。例如有个如下的 enum 类型定义: 12 | 13 | ```java 14 | public enum EmployeeType { 15 | FULL_TIME, 16 | CONTRACTOR, 17 | INTERN 18 | } 19 | ``` 20 | 21 | 如果你想遍历这个枚举类型,通过下面的方式把它转换成 Observable 是不是适用所有情况呢? 22 | 23 | ```java 24 | Observable employeeTypes = Observable.from(Employee.values()); 25 | ``` 26 | 如果你已经在使用 Observable 的操作符来操作数据了,这个时候使用 Observable 版本的 EmployeeType 比较合适。但是通常情况下不是这样的。 27 | 28 | 简单的常量数据并不太适合转换为 Observable 。从 API 的角度来说, 使用传统的方式来返回这种数据类型是比较好的情况。如果调用 API 的人想使用 Observable 则可以很容易的完成转换。 29 | 30 | ## 第二种情况:开销比较大的、缓存的对象 31 | 32 | 假设有个用来执行正则表达式搜索的 ActionQualifier 类,由于编译正则表达式是非常耗时的,所以不停的创建新的 ActionQualifier 对象是很不明智的: 33 | 34 | ```java 35 | public final class ActionQualifier { 36 | 37 | private final Pattern codeRegexPattern; 38 | private final int actionNumber; 39 | 40 | ActionQualifier(String codeRegex, int actionNumber) { 41 | this.codeRegexPattern = Pattern.compile(codeRegex); 42 | this.actionNumber = actionNumber; 43 | } 44 | 45 | public boolean qualify(String code) { 46 | return codeRegexPattern.matcher(code).find(); 47 | } 48 | public int getActionCode() { 49 | return actionNumber; 50 | } 51 | } 52 | ``` 53 | 54 | 如果你使用 [RxJava-JDBC](https://github.com/davidmoten/rxjava-jdbc) 并且在操作过程中使用了 ActionQualifier 对象,当有多个订阅者订阅到这个查询数据的 Observable 上的时候,由于每个订阅都会重新查询数据库,所以创建新的 ActionQualifier 是非常耗时的: 55 | 56 | ```java 57 | Observable actionQualifiers = db 58 | .select("SELECT CODE_REGEX, ACTION_NUMBER FROM ACTION_MAPPING") 59 | .get(rs -> new ActionQualifier(rs.getString("CODE_REGEX"), rs.getInt("ACTION_NUMBER"))); 60 | ``` 61 | 62 | 为了避免每个订阅者订阅的时候都查询一次,你可以选择使用 cache() 来缓存数据。这样的话,actionQualifiers 就没法更新了并且可能长期持有,导致虚拟机无法回收该对象。 63 | 64 | ```java 65 | Observable actionQualifiers = db 66 | .select("SELECT CODE_REGEX, ACTION_NUMBER FROM ACTION_MAPPING") 67 | .get(rs -> new ActionQualifier(rs.getString("CODE_REGEX"), rs.getInt("ACTION_NUMBER"))) 68 | .cache(); 69 | ``` 70 | 71 | Dave Moten 提供了一个[巧妙的解决方式](http://stackoverflow.com/questions/33047209/rxjava-is-there-an-observable-cacheddefer-operator-or-some-equivalent),缓存设定过期时间,然后重新订阅到源 Observable。但是最终你可能会问,是否可以把 actionQualifiers 保存在一个 List 中,然后手工的去刷新。 72 | 73 | ```java 74 | List actionQualifiers = db 75 | .select("SELECT CODE_REGEX, ACTION_NUMBER FROM ACTION_MAPPING") 76 | .get(rs -> new ActionQualifier(rs.getString("CODE_REGEX"), rs.getInt("ACTION_NUMBER"))) 77 | .toList().toBlocking().first(); 78 | ``` 79 | 80 | 当你需要使用 Observable 的时候,则可以很容易的把 List 转换为 Observable : 81 | 82 | ```java 83 | Observable.from(actionQualifiers).filter(aq -> aq.qualify("TXB.*")); 84 | ``` 85 | 86 | 不管使用哪种方式,缓存大量的消耗资源的对象都是很不好处理的。并且使用 Observable 的 cache 还会导致内存占用,根据您的具体情况,你可以灵活选择使用哪种方式。 87 | 88 | ## 第三种情况:简单的查看和单步的操作(Simple “Lookups” and Single-Step Monads) 89 | 90 | RxJava 的优势之一是可以很容易的组合很多操作函数。Take these, then filter that, map to this, and reduce to that. 91 | 92 | ```java 93 | Observable products = ... 94 | 95 | Observable totalSoldUnits = products 96 | .filter(pd -> pd.getCategoryId() == 3) 97 | .map(pd -> pd.getSoldUnits()) 98 | .reduce(0,(x,y) -> x + y) 99 | ``` 100 | 101 | 如果只是简单的一步操作呢? 102 | 103 | ```java 104 | Observable category = Category.forId(263); 105 | ``` 106 | 107 | 这种情况是不是滥用 Observable 呢? 直接返回数据是不是更好呢? 108 | 109 | ```java 110 | Category category = Category.forId(263); 111 | ``` 112 | 113 | 如果返回的结果有多个 Category 、或者你不想处理返回数据为 null 的情况,则会使用 Observable 。但是在下面的示例中会看到,这种过度的使用 Observable 会导致更多的模板代码出现。 114 | 115 | 如果你非要这样用,则使用的时候,可以很容易的转换为 Observable: 116 | 117 | ```java 118 | Observable category = Observable.just(Category.forId(263)) 119 | .filter(c -> c != null); 120 | ``` 121 | 122 | ## 第四种情况:经常使用到的属性(Frequently Qualified Properties) 123 | 124 | 先解释下上面提到的情况,比如有下面一个 Product 类 125 | 126 | ```java 127 | public final class Product { 128 | private final int id; 129 | private final String description; 130 | private final int categoryId; 131 | 132 | public Product(int id, String description, int categoryId) { 133 | this.id = id; 134 | this.description = description; 135 | this.categoryId = categoryId; 136 | } 137 | public Observable getCategory() { 138 | return Category.forId(categoryId); 139 | } 140 | } 141 | ``` 142 | 143 | 上面的 getCategory() 返回的是 Observable。如果你经常使用这个函数,则用起来可能相当麻烦。假设每个 Category 中有个 getGroup() 函数返回一个整数,代表所在的分组。可以根据这个分组来过滤每个组的分类: 144 | 145 | ```java 146 | Observable products = ... 147 | 148 | Observable productsInGroup5 = 149 | products.flatMap(p -> p.getCategory().filter(c -> c.getGroup() == 5).map(p -> c)); 150 | ``` 151 | 152 | 如此简单的一个需求,代码看起来居然这么复杂到使用了 FlatMap 。如果 getCategory() 返回的是 category 对象则用起来就相当简单了: 153 | 154 | ```java 155 | public Category getCategory() { 156 | return Category.forId(categoryId); 157 | } 158 | 159 | Observable productsInGroup5 = 160 | products.filter(p -> p.getCategory().getGroup() == 5); 161 | ``` 162 | 163 | 所以针对这种经常使用到的函数或者属性,最好不要返回 Observable 的形式。 164 | 165 | ## 第五种情况:有状态的对象(Capturing State) 166 | 167 | RxJava 中的数据通常是无状态的。这样可以方便并行处理多个操作。但是在实际的业务逻辑中,通常需要保留一些状态。比如打印价格时候,需要保留历史价格信息: 168 | 169 | ```java 170 | public final class PricePoint { 171 | private final int id; 172 | private final int productId; 173 | private final BigDecimal price; 174 | private final ImmutableList historicalPricePoints; 175 | 176 | public PricePoint(int id, int productId, BigDecimal price) { 177 | this.id = id; 178 | this.productId = productId; 179 | this.price = price; 180 | historicalPricePoints = HistoricalPricePoints.forProductId(productId); 181 | } 182 | public ImmutableList getHistoricalPricePoints() { 183 | return historicalPricePoints; 184 | } 185 | } 186 | ``` 187 | 188 | 然后通过 Observable 的方式来获取历史信息: 189 | 190 | ```java 191 | public final class PricePoint { 192 | private final int id; 193 | private final int productId; 194 | private final BigDecimal price; 195 | 196 | public PricePoint(int id, int productId, BigDecimal price) { 197 | this.id = id; 198 | this.productId = productId; 199 | this.price = price; 200 | } 201 | public Observable getHistoricalPricePoints() { 202 | return HistoricalPricePoints.forProductId(productId); 203 | } 204 | } 205 | ``` 206 | 207 | 但是如果这个操作是比较消耗资源的,则又回到了第二种情况。 208 | 209 | 针对这种有状态的数据,还是使用传统的查询方式比较好。 210 | 211 | ## 总结 212 | 213 | 使用了 RxJava,尝到了 Rxjava 的好处,您会到处使用它,但是 RxJava 是用来解决比较复杂或者非常复杂情况的,对于简单的情况还是简单处理吧。凡事要把握好度,过犹不及! 上面的一些情况,对于有经验的 RxJava 开发者可能很容易避免这种情况,对于初学者可能还处于 使用 RxJava 的蜜月期,看问题没这么透彻很容易陷入到过度使用 RxJava 的情况。 214 | 再次提醒,以上只是笔者自己的主观意见,如果有不同见解,欢迎一起讨论交流。 215 | 216 | > 原文地址: http://tomstechnicalblog.blogspot.hk/2016/07/when-not-to-use-rxjava.html 217 | -------------------------------------------------------------------------------- /RxJava系列教程/19.RxJava 教程第四部分:并发 之意外情况处理.md: -------------------------------------------------------------------------------- 1 | ## 19. 并发之意外情况处理 2 | 3 | Rx 尽量避免状态泄露到数据流之外的场景。但是有些东西本身就带有状态。比如服务器可以上线和离线、手机可以访问Wifi、按钮被按下了等。在 Rx 中国,我们在一段时间内看到这些事件,并称之为窗口(window)。其他事件在这个窗口内发生可能需要特殊处理。例如,手机在使用移动收费上网的时候,会把网络请求优先级降低,来避免天价流量费的情况。 4 | 5 | > 注意:上面的一段话估计是翻译的,有点语句不通。更多参考[官网](http://www.introtorx.com/Content/v1.0.10621.0/17_SequencesOfCoincidence.html#SequencesOfCoincidence) 6 | 7 | ### 窗口 Window 8 | 9 | buffer 函数可以缓存多个数据并整体发射。 window 操作函数和 buffer 有一对一的关系。区别在于 window 不会整体返回缓存的数据。而是把缓存的数据当做一个新的 Observable 数据流来返回。这样当源 Observable 有数据发射了,这个数据就立刻发射到 window 返回的 Observable 里面了。 10 | 11 | 下图可以看到二者的区别: 12 | 13 | ![RxJava](images/意外情况处理_01.png) 14 | 15 | window : 16 | 17 | ![RxJava](images/意外情况处理_02.png) 18 | 19 | 如果你还没有了解 buffer, 建议你到前面的章节下去看看 buffer。 buffer 和 window 的函数形式是一样的,功能也非常类似,并且易于理解。 buffer 都可以用 window 来实现其功能: 20 | 21 | ```java 22 | source.buffer(...) 23 | // 和下面的是一样的功能 24 | source.window(...).flatMap(w -> w.toList()) 25 | ``` 26 | 27 | ### Window by count 28 | 29 | 窗口内可以限定数目。当窗口发射的数据达到了限定的数目,当前窗口的 Observable 就结束并开启一个新的窗口。 30 | 31 | ![RxJava](images/意外情况处理_03.png) 32 | 33 | 和buffer 一样, 使用 window(int count, int skip) 也可以跳过数据或者重叠使用数据。 34 | 35 | ```java 36 | Observable 37 | .merge( 38 | Observable.range(0, 5) 39 | .window(3,1)) 40 | .subscribe(System.out::println); 41 | ``` 42 | 43 | 结果: 44 | 45 | ``` 46 | 0 47 | 1 48 | 1 49 | 2 50 | 2 51 | 2 52 | 3 53 | 3 54 | 3 55 | 4 56 | 4 57 | 4 58 | ``` 59 | 60 | 可以看到当有数据重叠的时候, 多个 Observable 会返回同样的数据,可以把结果输出形式修改一下,方便查看: 61 | 62 | ```java 63 | Observable.range(0, 5) 64 | .window(3, 1) 65 | .flatMap(o -> o.toList()) 66 | .subscribe(System.out::println); 67 | ``` 68 | 69 | 结果: 70 | 71 | ``` 72 | [0, 1, 2] 73 | [1, 2, 3] 74 | [2, 3, 4] 75 | [3, 4] 76 | [4] 77 | ``` 78 | 79 | 这样就可以看到 window 和 buffer 是非常类似的了。 80 | 81 | ### Window by time 82 | 83 | 同样也可以指定窗口的时间长度: 84 | 85 | ![RxJava](images/意外情况处理_04.png) 86 | 87 | ```java 88 | public final Observable> window(long timespan, long timeshift, java.util.concurrent.TimeUnit unit) 89 | ``` 90 | 91 | ```java 92 | Observable.interval(100, TimeUnit.MILLISECONDS) 93 | .take(5) 94 | .window(250, 100, TimeUnit.MILLISECONDS) 95 | .flatMap(o -> o.toList()) 96 | .subscribe(System.out::println); 97 | ``` 98 | 99 | 结果: 100 | 101 | ``` 102 | [0, 1] 103 | [0, 1, 2] 104 | [1, 2, 3] 105 | [2, 3, 4] 106 | [3, 4] 107 | [4] 108 | ``` 109 | 110 | 上面的示例中,每隔100ms开始一个新的窗口,每个窗口持续 250ms。 第一个窗口从 0ms 开始并捕获到数据 [0, 1](0 是在第100ms的时候发射的)。 111 | 112 | ### Window with signal 113 | 114 | 同样也可以用另外一个信号 Observable 当做窗口结束的信号。 115 | 116 | ![RxJava](images/意外情况处理_05.png) 117 | 118 | 信号 Observable 直接也可以相互传递事件。 119 | 120 | ![RxJava](images/意外情况处理_05.png) 121 | 122 | 下面是使用信号 Observable 实现的重叠窗口: 123 | 124 | ```java 125 | Observable.interval(100, TimeUnit.MILLISECONDS) 126 | .take(5) 127 | .window( 128 | Observable.interval(100, TimeUnit.MILLISECONDS), 129 | o -> Observable.timer(250, TimeUnit.MILLISECONDS)) 130 | .flatMap(o -> o.toList()) 131 | .subscribe(System.out::println); 132 | ``` 133 | 134 | 结果: 135 | 136 | ``` 137 | [1, 2] 138 | [2, 3] 139 | [3, 4] 140 | [4] 141 | [] 142 | ``` 143 | 144 | 注意上面的数字 0 没有捕获到,原因在于源 Observable 和 信号 Observable 都是在同一时刻发生的,但是在实际操作中并没有这种情况。所以当信号 Observable发射的时候, 数字 0 已经发射出去了。 145 | 146 | ### Join 147 | 148 | join 可以把两个数据流中的数据组合一起。 zip 函数根据数据发射的顺序来组合数据。 join 可以根据时间来组合。 149 | 150 | ```java 151 | public final Observable join( 152 | Observable right, 153 | Func1> leftDurationSelector, 154 | Func1> rightDurationSelector, 155 | Func2 resultSelector) 156 | ``` 157 | 158 | join 组合的两个 Observable 被称之为 左和右。 上面的函数并不是静态的,调用该函数的 Observable就是 左 。参数中的 leftDurationSelector 和 rightDurationSelector 分别使用 左右发射的数据为参数,然后返回一个定义时间间隔的 Observable,和 window 的最后一个重载函数类似。这个时间间隔是用来选择里面发射的数据并组合一起。里面的数据会当做参数调用 resultSelector ,然后返回一个组合后的数据。然后组合后的数据由 join 返回的 Observable 发射出去。 159 | 160 | join 比较难以理解以及强大之处就是如果选择组合的数据。当有数据在 源 Observable 中发射,就开始一个该数据的时间窗口。对应的时间间隔用来计时该数据的窗口何时结束。在时间窗口还没结束的时候,另外一个 Observable 发射的数据就和当前的数据组合一起。左右数据流的处理方式是一样的,所以为了简化介绍,我们假定只有一个 源 Observable 有时间窗口。 161 | 162 | 下面的示例中, 左Observable 数据流从来不结束而右Observable的时间窗口为 0. 163 | 164 | ```java 165 | Observable left = 166 | Observable.interval(100, TimeUnit.MILLISECONDS) 167 | .map(i -> "L" + i); 168 | Observable right = 169 | Observable.interval(200, TimeUnit.MILLISECONDS) 170 | .map(i -> "R" + i); 171 | 172 | left 173 | .join( 174 | right, 175 | i -> Observable.never(), 176 | i -> Observable.timer(0, TimeUnit.MILLISECONDS), 177 | (l,r) -> l + " - " + r 178 | ) 179 | .take(10) 180 | .subscribe(System.out::println); 181 | ``` 182 | 183 | 结果: 184 | 185 | ``` 186 | L0 - R0 187 | L1 - R0 188 | L0 - R1 189 | L1 - R1 190 | L2 - R1 191 | L3 - R1 192 | L0 - R2 193 | L1 - R2 194 | L2 - R2 195 | L3 - R2 196 | ``` 197 | 198 | 由于左边的 Observable 时间窗口是永久,这意味着左边每个发射的数据都会和右边的 数据组合。 当右边数据发射的比左边的慢一倍。所以当左边的数据发射了两个对应右边的同一个数据。然后右边发射下一个数据就开启了右边的一个新的时间窗口,然后左右的数据会从开始的数据和右边的新窗口中的数据组合。 199 | 200 | 下面示例把左右源 Observable 发射的间隔都设置为 100ms,然后把左时间窗口设置为 150ms: 201 | 202 | ```java 203 | Observable left = 204 | Observable.interval(100, TimeUnit.MILLISECONDS) 205 | .map(i -> "L" + i); 206 | Observable right = 207 | Observable.interval(100, TimeUnit.MILLISECONDS) 208 | .map(i -> "R" + i); 209 | 210 | left 211 | .join( 212 | right, 213 | i -> Observable.timer(150, TimeUnit.MILLISECONDS), 214 | i -> Observable.timer(0, TimeUnit.MILLISECONDS), 215 | (l,r) -> l + " - " + r 216 | ) 217 | .take(10) 218 | .subscribe(System.out::println); 219 | ``` 220 | 221 | 结果: 222 | 223 | ``` 224 | L0 - R0 225 | L0 - R1 226 | L1 - R1 227 | L1 - R2 228 | L2 - R2 229 | L2 - R3 230 | L3 - R3 231 | L3 - R4 232 | L4 - R4 233 | L4 - R5 234 | ``` 235 | 236 | 左右同时发射数据,所以左右同时开始第一个时间窗口,然后组合的数据为 “L0 – R0”。然后 左边的时间窗口继续,而右边发射新的数据 R1 则右边的数据R1和左边的 L0 组合 “L0 – R1”,然后过了 50ms 后, 左边的时间窗口结束了,开启下一个时间窗口,结果为 “L1 – R1”。 一直重复下去。 237 | 238 | 两个数据流都有时间窗口。每个数据流中的每个值按照如下方式组合: 239 | 240 | - 如果旧的数据时间窗口还没有结束,则和另外一个数据流中的每个旧的数据组合 241 | - 如果当前数据的时间窗口还没有结束,则和另外一个数据流中的每个新的数据组合。 242 | 243 | ### groupJoin 244 | 245 | 只要检测到一个组合数据,join 就用两个数据调用 resultSelector 并发射返回的数据。 而 groupJoin 又有不同的功能: 246 | 247 | ```java 248 | public final Observable groupJoin( 249 | Observable right, 250 | Func1> leftDuration, 251 | Func1> rightDuration, 252 | Func2,? extends R> resultSelector) 253 | ``` 254 | 255 | 除了 resultSelector 以外,其他参数和 join 函数的参数是一样的。这个 resultSelector 从左边的数据流中获取一个数据并从右边数据流中获取一个 Observable。这个 Observable 会发射和左边数据配对的所有数据。groupJoin 中的配对和 join 一样是对称的,但是结果是不一样的。可以把 resultSelect 实现为一个 GroupedObservable, 左边的数据当做 key,而把右边的数据发射出去。 256 | 257 | 还使用第一个 join的示例,左边的数据流的时间窗口重来不关闭: 258 | 259 | ```java 260 | Observable left = 261 | Observable.interval(100, TimeUnit.MILLISECONDS) 262 | .map(i -> "L" + i) 263 | .take(6); 264 | Observable right = 265 | Observable.interval(200, TimeUnit.MILLISECONDS) 266 | .map(i -> "R" + i) 267 | .take(3); 268 | 269 | left 270 | .groupJoin( 271 | right, 272 | i -> Observable.never(), 273 | i -> Observable.timer(0, TimeUnit.MILLISECONDS), 274 | (l, rs) -> rs.toList().subscribe(list -> System.out.println(l + ": " + list)) 275 | ) 276 | .subscribe(); 277 | ``` 278 | 279 | 结果: 280 | 281 | ``` 282 | L0: [R0, R1, R2] 283 | L1: [R0, R1, R2] 284 | L2: [R1, R2] 285 | L3: [R1, R2] 286 | L4: [R2] 287 | L5: [R2] 288 | ``` 289 | 290 | 上面的 示例和 jion 中的示例数据配对是一样的,只是 resultSelector 不一样导致输出的结果不一样。 291 | 292 | 使用 groupJoin 和 flatMap 可以实现 jion的 功能: 293 | 294 | ```java 295 | .join( 296 | right, 297 | leftDuration 298 | rightDuration, 299 | (l,r) -> joinResultSelector(l,r) 300 | ) 301 | // 和下面的一样 302 | .groupJoin( 303 | right, 304 | leftDuration 305 | rightDuration, 306 | (l, rs) -> rs.map(r -> joinResultSelector(l,r)) 307 | ) 308 | .flatMap(i -> i) 309 | ``` 310 | 311 | 通过 join 和 groupBy 也可以实现 groupJoin。在示例代码中有这个实现,感兴趣的可以去看看。 312 | -------------------------------------------------------------------------------- /RxJava2.0/给初学者的RxJava2.0教程-6.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 在上一节中, 我们找到了上下游流速不均衡从而导致BackPressureException出现的源头 , 在这一节里我们将学习如何去治理它 . 可能很多看过其他人写的文章的朋友都会觉得只有`Flowable`才能解决 , 所以大家对这个`Flowable`都抱有很大的期许 , 其实呐 , 你们毕竟图样图森破 , 今天我们先抛开`Flowable`, 仅仅依靠我们自己的`双手和智慧` , 来看看我们如何去治理 , 通过本节的学习之后我们再来看`Flowable`, 你会发现它其实并没有想象中那么牛叉, 它只是被其他人过度神化了. 4 | 5 | ## 正题 6 | 7 | 我们接着来看上一节的这个例子: 8 | 9 | ```java 10 | Observable.create(new ObservableOnSubscribe() { 11 | @Override 12 | public void subscribe(ObservableEmitter emitter) throws Exception { 13 | for (int i = 0; ; i++) { //无限循环发送事件 14 | emitter.onNext(i); 15 | } 16 | } 17 | }).subscribeOn(Schedulers.io()) 18 | .observeOn(AndroidSchedulers.mainThread()) 19 | .subscribe(new Consumer() { 20 | @Override 21 | public void accept(Integer integer) throws Exception { 22 | Log.d(TAG, "" + integer); 23 | } 24 | }); 25 | ``` 26 | 27 | 上一节中我们看到了它的运行结果是直接爆掉了内存, 也明白它为什么就爆掉了内存, 那么我们能做些什么, 才能不让这种情况发生呢. 28 | 29 | 之前我们说了, 上游发送的所有事件都放到水缸里了, 所以瞬间水缸就满了, 那我们可以只放我们需要的事件到水缸里呀, 只放一部分数据到水缸里, 这样不就不会溢出来了吗, 因此, 我们把上面的代码修改一下: 30 | 31 | ```java 32 | Observable.create(new ObservableOnSubscribe() { 33 | @Override 34 | public void subscribe(ObservableEmitter emitter) throws Exception { 35 | for (int i = 0; ; i++) { 36 | emitter.onNext(i); 37 | } 38 | } 39 | }).subscribeOn(Schedulers.io()) 40 | .filter(new Predicate() { 41 | @Override 42 | public boolean test(Integer integer) throws Exception { 43 | return integer % 10 == 0; 44 | } 45 | }) 46 | .observeOn(AndroidSchedulers.mainThread()) 47 | .subscribe(new Consumer() { 48 | @Override 49 | public void accept(Integer integer) throws Exception { 50 | Log.d(TAG, "" + integer); 51 | } 52 | }); 53 | ``` 54 | 55 | 在这段代码中我们增加了一个filter, 只允许能被10整除的事件通过, 再来看看运行结果: 56 | 57 | ![filter.gif](images/RxJava2_20.gif) 58 | 59 | 可以看到, 虽然内存依然在增长, 但是增长速度相比之前, 已经减少了太多了, 至少在我录完GIF之前还没有爆掉内存, 大家可以试着改成能被100整除试试. 60 | 61 | 可以看到, 通过减少进入水缸的事件数量的确可以缓解上下游流速不均衡的问题, 但是力度还不够, 我们再来看一段代码: 62 | 63 | ```java 64 | Observable.create(new ObservableOnSubscribe() { 65 | @Override 66 | public void subscribe(ObservableEmitter emitter) throws Exception { 67 | for (int i = 0; ; i++) { 68 | emitter.onNext(i); 69 | } 70 | } 71 | }).subscribeOn(Schedulers.io()) 72 | .sample(2, TimeUnit.SECONDS) //sample取样 73 | .observeOn(AndroidSchedulers.mainThread()) 74 | .subscribe(new Consumer() { 75 | @Override 76 | public void accept(Integer integer) throws Exception { 77 | Log.d(TAG, "" + integer); 78 | } 79 | }); 80 | ``` 81 | 82 | 这里用了一个`sample`操作符, 简单做个介绍, 这个操作符每隔指定的时间就从上游中取出一个事件发送给下游. 这里我们让它每隔2秒取一个事件给下游, 来看看这次的运行结果吧: 83 | 84 | ![sample.gif](images/RxJava2_21.gif) 85 | 86 | 这次我们可以看到, 虽然上游仍然一直在不停的发事件, 但是我们只是`每隔一定时间`取一个放进水缸里, 并没有全部放进水缸里, 因此这次内存仅仅只占用了5M. 87 | 88 | > 大家以后可以出去吹牛逼了: 我曾经通过技术手段去优化一个程序, 最终使得内存占用从300多M变成不到5M. 89 | 90 | 前面这两种方法归根到底其实就是减少放进水缸的事件的数量, 是以`数量`取胜, 但是这个方法有个`缺点`, 就是`丢失了大部分的事件`. 91 | 92 | 那么我们换一个角度来思考, 既然上游发送事件的速度太快, 那我们就适当减慢发送事件的速度, 从`速度`上取胜, 听上去不错, 我们来试试: 93 | 94 | ```java 95 | Observable.create(new ObservableOnSubscribe() { 96 | @Override 97 | public void subscribe(ObservableEmitter emitter) throws Exception { 98 | for (int i = 0; ; i++) { 99 | emitter.onNext(i); 100 | Thread.sleep(2000); //每次发送完事件延时2秒 101 | } 102 | } 103 | }).subscribeOn(Schedulers.io()) 104 | .observeOn(AndroidSchedulers.mainThread()) 105 | .subscribe(new Consumer() { 106 | @Override 107 | public void accept(Integer integer) throws Exception { 108 | Log.d(TAG, "" + integer); 109 | } 110 | }); 111 | ``` 112 | 113 | 这次我们让上游每次发送完事件后都延时了2秒, 来看看运行结果: 114 | 115 | ![sleep.gif](images/RxJava2_22.gif) 116 | 117 | 完美 ! 一切都是那么完美 ! 118 | 119 | 可以看到, 我们给上游加上延时了之后, 瞬间一头发情的公牛就变得跟只小绵羊一样, 如此温顺, 如此平静, 如此平稳的内存线, 美妙极了. 而且`事件也没有丢失`, `上游`通过适当的`延时`, 不但`减缓了`事件进入水缸的`速度`, 也可以让`下游`有`充足的时间`从水缸里取出事件来处理 , 这样一来, 就不至于导致大量的事件涌进水缸, 也就不会OOM啦. 120 | 121 | 到目前为止, 我们没有依靠任何其他的工具, 就轻易解决了上下游流速不均衡的问题. 122 | 123 | 因此我们总结一下, 本节中的治理的办法就两种: 124 | 125 | - 一是从数量上进行治理, 减少发送进水缸里的事件 126 | - 二是从速度上进行治理, 减缓事件发送进水缸的速度 127 | 128 | 大家一定没忘记, 在上一节还有个Zip的例子, 这个例子也爆了我们的内存, 现学现用, 我们用刚学到的办法来试试能不能惩奸除恶, 先来看看第一种办法. 129 | 130 | 先来减少进入水缸的事件的数量: 131 | 132 | ```java 133 | Observable observable1 = Observable.create(new ObservableOnSubscribe() { 134 | @Override 135 | public void subscribe(ObservableEmitter emitter) throws Exception { 136 | for (int i = 0; ; i++) { 137 | emitter.onNext(i); 138 | } 139 | } 140 | }).subscribeOn(Schedulers.io()).sample(2, TimeUnit.SECONDS); //进行sample采样 141 | 142 | Observable observable2 = Observable.create(new ObservableOnSubscribe() { 143 | @Override 144 | public void subscribe(ObservableEmitter emitter) throws Exception { 145 | emitter.onNext("A"); 146 | } 147 | }).subscribeOn(Schedulers.io()); 148 | 149 | Observable.zip(observable1, observable2, new BiFunction() { 150 | @Override 151 | public String apply(Integer integer, String s) throws Exception { 152 | return integer + s; 153 | } 154 | }).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer() { 155 | @Override 156 | public void accept(String s) throws Exception { 157 | Log.d(TAG, s); 158 | } 159 | }, new Consumer() { 160 | @Override 161 | public void accept(Throwable throwable) throws Exception { 162 | Log.w(TAG, throwable); 163 | } 164 | }); 165 | ``` 166 | 167 | 来试试运行结果吧: 168 | 169 | ![zip_sample.gif](images/RxJava2_23.gif) 170 | 171 | 哈哈, 成功了吧, 再来用第二种办法试试. 172 | 173 | 这次我们来减缓速度: 174 | 175 | ```java 176 | Observable observable1 = Observable.create(new ObservableOnSubscribe() { 177 | @Override 178 | public void subscribe(ObservableEmitter emitter) throws Exception { 179 | for (int i = 0; ; i++) { 180 | emitter.onNext(i); 181 | Thread.sleep(2000); //发送事件之后延时2秒 182 | } 183 | } 184 | }).subscribeOn(Schedulers.io()); 185 | 186 | Observable observable2 = Observable.create(new ObservableOnSubscribe() { 187 | @Override 188 | public void subscribe(ObservableEmitter emitter) throws Exception { 189 | emitter.onNext("A"); 190 | } 191 | }).subscribeOn(Schedulers.io()); 192 | 193 | Observable.zip(observable1, observable2, new BiFunction() { 194 | @Override 195 | public String apply(Integer integer, String s) throws Exception { 196 | return integer + s; 197 | } 198 | }).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer() { 199 | @Override 200 | public void accept(String s) throws Exception { 201 | Log.d(TAG, s); 202 | } 203 | }, new Consumer() { 204 | @Override 205 | public void accept(Throwable throwable) throws Exception { 206 | Log.w(TAG, throwable); 207 | } 208 | }); 209 | ``` 210 | 211 | 来看看运行结果吧: 212 | 213 | ![zip_sleep.gif](images/RxJava2_24.gif) 214 | 215 | 果然也成功了, 这里只打印出了下游收到的事件, 所以只有一个. 如果你对这个结果看不懂, 请自觉掉头看前面几篇文章. 216 | 217 | 通过本节的学习, 大家应该对如何处理上下游流速不均衡已经有了基本的认识了, 大家也可以看到, 我们并没有使用`Flowable`, 所以很多时候仔细去分析问题, 找到问题的原因, 从源头去解决才是最根本的办法. 后面我们讲到`Flowable`的时候, 大家就会发现它其实没什么神秘的, 它用到的办法和我们本节所讲的基本上是一样的, 只是它稍微做了点封装. 218 | 219 | 好了, 今天的教程就到这里吧, 下一节中我们就会来学习你们喜闻乐见的`Flowable` -------------------------------------------------------------------------------- /RxJava2.0/给初学者的RxJava2.0教程-3.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 上一节讲解了线程调度, 并且举了两个实际中的例子, 其中有一个登录的例子, 不知大家有没有想过这么一个问题, 如果是一个新用户, 必须先注册, 等注册成功之后再自动登录该怎么做呢. 4 | 5 | 很明显, 这是一个嵌套的网络请求, 首先需要去请求注册, 待注册成功回调了再去请求登录的接口. 6 | 7 | 我们当然可以想当然的写成这样: 8 | 9 | ```java 10 | private void login() { 11 | api.login(new LoginRequest()) 12 | .subscribeOn(Schedulers.io()) //在IO线程进行网络请求 13 | .observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求结果 14 | .subscribe(new Consumer() { 15 | @Override 16 | public void accept(LoginResponse loginResponse) throws Exception { 17 | Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show(); 18 | } 19 | }, new Consumer() { 20 | @Override 21 | public void accept(Throwable throwable) throws Exception { 22 | Toast.makeText(MainActivity.this, "登录失败", Toast.LENGTH_SHORT).show(); 23 | } 24 | }); 25 | } 26 | 27 | private void register() { 28 | api.register(new RegisterRequest()) 29 | .subscribeOn(Schedulers.io()) //在IO线程进行网络请求 30 | .observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求结果 31 | .subscribe(new Consumer() { 32 | @Override 33 | public void accept(RegisterResponse registerResponse) throws Exception { 34 | Toast.makeText(MainActivity.this, "注册成功", Toast.LENGTH_SHORT).show(); 35 | login(); //注册成功, 调用登录的方法 36 | } 37 | }, new Consumer() { 38 | @Override 39 | public void accept(Throwable throwable) throws Exception { 40 | Toast.makeText(MainActivity.this, "注册失败", Toast.LENGTH_SHORT).show(); 41 | } 42 | }); 43 | } 44 | ``` 45 | 46 | (其实能写成这样的代码的人也很不错了, 至少没写到一起...) 47 | 48 | 这样的代码能够工作, 但不够优雅, 通过本节的学习, 可以让我们用一种更优雅的方式来解决这个问题. 49 | 50 | ## 正题 51 | 52 | 先来看看最简单的变换操作符map吧 53 | 54 | ### Map 55 | 56 | map是RxJava中最简单的一个变换操作符了, 它的作用就是对上游发送的每一个事件应用一个函数, 使得每一个事件都按照指定的函数去变化. 用事件图表示如下: 57 | 58 | ![RxJava](images/RxJava2_07.png) 59 | 60 | 图中map中的函数作用是将圆形事件转换为矩形事件, 从而导致下游接收到的事件就变为了矩形.用代码来表示这个例子就是: 61 | 62 | ```java 63 | Observable.create(new ObservableOnSubscribe() { 64 | @Override 65 | public void subscribe(ObservableEmitter emitter) throws Exception { 66 | emitter.onNext(1); 67 | emitter.onNext(2); 68 | emitter.onNext(3); 69 | } 70 | }).map(new Function() { 71 | @Override 72 | public String apply(Integer integer) throws Exception { 73 | return "This is result " + integer; 74 | } 75 | }).subscribe(new Consumer() { 76 | @Override 77 | public void accept(String s) throws Exception { 78 | Log.d(TAG, s); 79 | } 80 | }); 81 | ``` 82 | 83 | 在上游我们发送的是数字类型, 而在下游我们接收的是String类型, 中间起转换作用的就是map操作符, 运行结果为: 84 | 85 | ``` 86 | D/TAG: This is result 1 87 | D/TAG: This is result 2 88 | D/TAG: This is result 3 89 | ``` 90 | 91 | 通过Map, 可以将上游发来的事件转换为任意的类型, 可以是一个Object, 也可以是一个集合, 如此强大的操作符你难道不想试试? 92 | 93 | 接下来我们来看另外一个广为人知的操作符flatMap. 94 | 95 | ### FlatMap 96 | 97 | flatMap是一个非常强大的操作符, 先用一个比较难懂的概念说明一下: 98 | 99 | `FlatMap`将一个发送事件的上游Observable变换为多个发送事件的Observables,然后将它们发射的事件合并后放进一个单独的Observable里. 100 | 101 | 这句话比较难以理解, 我们先通俗易懂的图片来详细的讲解一下, 首先先来看看整体的一个图片: 102 | 103 | ![RxJava](images/RxJava2_08.png) 104 | 105 | 先看看上游, 上游发送了三个事件, 分别是1,2,3, 注意它们的颜色. 106 | 107 | 中间flatMap的作用是将圆形的事件转换为一个发送矩形事件和三角形事件的新的上游Observable. 108 | 109 | 还是不能理解? 别急, 再来看看分解动作: 110 | 111 | ![RxJava](images/RxJava2_09.png) 112 | 113 | 这样就很好理解了吧 !!! 114 | 115 | 上游每发送一个事件, flatMap都将创建一个新的水管, 然后发送转换之后的新的事件, 下游接收到的就是这些新的水管发送的数据. **这里需要注意的是, flatMap并不保证事件的顺序,** 也就是图中所看到的, 并不是事件1就在事件2的前面. 如果需要保证顺序则需要使用`concatMap`. 116 | 117 | 说了原理, 我们还是来看看实际中的代码如何写吧: 118 | 119 | ```java 120 | Observable.create(new ObservableOnSubscribe() { 121 | @Override 122 | public void subscribe(ObservableEmitter emitter) throws Exception { 123 | emitter.onNext(1); 124 | emitter.onNext(2); 125 | emitter.onNext(3); 126 | } 127 | }).flatMap(new Function>() { 128 | @Override 129 | public ObservableSource apply(Integer integer) throws Exception { 130 | final List list = new ArrayList<>(); 131 | for (int i = 0; i < 3; i++) { 132 | list.add("I am value " + integer); 133 | } 134 | return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS); 135 | } 136 | }).subscribe(new Consumer() { 137 | @Override 138 | public void accept(String s) throws Exception { 139 | Log.d(TAG, s); 140 | } 141 | }); 142 | ``` 143 | 144 | 如代码所示, 我们在flatMap中将上游发来的每个事件转换为一个新的发送三个String事件的水管, 为了看到flatMap结果是无序的,所以加了10毫秒的延时, 来看看运行结果吧: 145 | 146 | ``` 147 | D/TAG: I am value 1 148 | D/TAG: I am value 1 149 | D/TAG: I am value 1 150 | D/TAG: I am value 3 151 | D/TAG: I am value 3 152 | D/TAG: I am value 3 153 | D/TAG: I am value 2 154 | D/TAG: I am value 2 155 | D/TAG: I am value 2 156 | ``` 157 | 158 | 结果也确实验证了我们之前所说. 159 | 160 | 这里也简单说一下`concatMap`吧, 它和flatMap的作用几乎一模一样, 只是它的结果是严格按照上游发送的顺序来发送的, 来看个代码吧: 161 | 162 | ```java 163 | Observable.create(new ObservableOnSubscribe() { 164 | @Override 165 | public void subscribe(ObservableEmitter emitter) throws Exception { 166 | emitter.onNext(1); 167 | emitter.onNext(2); 168 | emitter.onNext(3); 169 | } 170 | }).concatMap(new Function>() { 171 | @Override 172 | public ObservableSource apply(Integer integer) throws Exception { 173 | final List list = new ArrayList<>(); 174 | for (int i = 0; i < 3; i++) { 175 | list.add("I am value " + integer); 176 | } 177 | return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS); 178 | } 179 | }).subscribe(new Consumer() { 180 | @Override 181 | public void accept(String s) throws Exception { 182 | Log.d(TAG, s); 183 | } 184 | }); 185 | ``` 186 | 187 | 只是将之前的flatMap改为了concatMap, 其余原封不动, 运行结果如下: 188 | 189 | ``` 190 | D/TAG: I am value 1 191 | D/TAG: I am value 1 192 | D/TAG: I am value 1 193 | D/TAG: I am value 2 194 | D/TAG: I am value 2 195 | D/TAG: I am value 2 196 | D/TAG: I am value 3 197 | D/TAG: I am value 3 198 | D/TAG: I am value 3 199 | ``` 200 | 201 | 可以看到, 结果仍然是有序的. 202 | 203 | 好了关于RxJava的操作符最基本的使用就讲解到这里了, RxJava中内置了许许多多的操作符, 这里通过讲解`map`和`flatMap`只是起到一个抛砖引玉的作用, 关于其他的操作符只要大家按照本文的思路去理解, 再仔细阅读文档, 应该是没有问题的了, 如果大家有需要也可以将需要讲解的操作符列举出来, 我可以根据大家的需求讲解一下. 204 | 205 | ## 实践 206 | 207 | 学习了FlatMap操作符, 我们就可以回答文章开头提出的那个问题了. 208 | 209 | 如何优雅的解决嵌套请求, 只需要用flatMap转换一下就行了. 210 | 211 | 先回顾一下上一节的请求接口: 212 | 213 | ```java 214 | public interface Api { 215 | @GET 216 | Observable login(@Body LoginRequest request); 217 | 218 | @GET 219 | Observable register(@Body RegisterRequest request); 220 | } 221 | ``` 222 | 223 | 可以看到登录和注册返回的都是一个上游Observable, 而我们的flatMap操作符的作用就是把一个Observable转换为另一个Observable, 因此结果就很显而易见了: 224 | 225 | ```java 226 | api.register(new RegisterRequest()) //发起注册请求 227 | .subscribeOn(Schedulers.io()) //在IO线程进行网络请求 228 | .observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求注册结果 229 | .doOnNext(new Consumer() { 230 | @Override 231 | public void accept(RegisterResponse registerResponse) throws Exception { 232 | //先根据注册的响应结果去做一些操作 233 | } 234 | }) 235 | .observeOn(Schedulers.io()) //回到IO线程去发起登录请求 236 | .flatMap(new Function>() { 237 | @Override 238 | public ObservableSource apply(RegisterResponse registerResponse) throws Exception { 239 | return api.login(new LoginRequest()); 240 | } 241 | }) 242 | .observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求登录的结果 243 | .subscribe(new Consumer() { 244 | @Override 245 | public void accept(LoginResponse loginResponse) throws Exception { 246 | Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show(); 247 | } 248 | }, new Consumer() { 249 | @Override 250 | public void accept(Throwable throwable) throws Exception { 251 | Toast.makeText(MainActivity.this, "登录失败", Toast.LENGTH_SHORT).show(); 252 | } 253 | }); 254 | ``` 255 | 256 | 从这个例子也可以看到我们切换线程是多么简单. 257 | 258 | 好了本次的教程就到这里了. 下一节我们将会学到`Flowable` 以及理解`Backpressure`背压的概念. 敬请期待 -------------------------------------------------------------------------------- /RxJava2.0/给初学者的RxJava2.0教程-5.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 大家喜闻乐见的`Backpressure`来啦. 4 | 5 | 上游生产速度大于下游处理速度,导致下游处理不及的状况,被称为 Backpressure(背压)。 6 | 7 | 对于可丢弃的事件,上游生产过快导致事件堆积,当堆积到超出 buffer 上限,就叫做 Backpressure 出现。 8 | 9 | 适合支持 Backpressure 的情况 10 | 11 | - 在线直播视频流 12 | - Log 13 | - 用户请求数超限丢弃(服务端) 14 | 15 | 这一节中我们将来学习`Backpressure`. 我看好多吃瓜群众早已坐不住了, 别急, 我们先来回顾一下上一节讲的`Zip`. 16 | 17 | ## 正题 18 | 19 | 上一节中我们说到Zip可以将多个上游发送的事件组合起来发送给下游, 那大家有没有想过一个问题, 如果其中一个`水管A`发送事件特别快, 而另一个`水管B` 发送事件特别慢, 那就可能出现这种情况, 发得快的`水管A` 已经发送了1000个事件了, 而发的慢的`水管B` 才发一个出来, 组合了一个之后`水管A` 还剩999个事件, 这些事件需要继续等待`水管B` 发送事件出来组合, 那么这么多的事件是放在哪里的呢? 总有一个地方保存吧? 没错, Zip给我们的每一根水管都弄了一个`水缸` , 用来保存这些事件, 用通俗易懂的图片来表示就是: 20 | 21 | ![zip2.png](images/RxJava2_14.png) 22 | 23 | 如图中所示, 其中蓝色的框框就是`zip`给我们的`水缸`! 它将每根水管发出的事件保存起来, 等两个水缸都有事件了之后就分别从水缸中取出一个事件来组合, 当其中一个水缸是空的时候就处于等待的状态. 24 | 25 | 题外话: 大家来分析一下这个水缸有什么特点呢? 它是按顺序保存的, 先进来的事件先取出来, 这个特点是不是很熟悉呀? 没错, 这就是我们熟知的队列, 这个水缸在Zip内部的实现就是用的队列, 感兴趣的可以翻看源码查看. 26 | 27 | 好了回到正题上来, 这个水缸有大小限制吗? 要是一直往里存会怎样? 我们来看个例子: 28 | 29 | ```java 30 | Observable observable1 = Observable.create(new ObservableOnSubscribe() { 31 | @Override 32 | public void subscribe(ObservableEmitter emitter) throws Exception { 33 | for (int i = 0; ; i++) { //无限循环发事件 34 | emitter.onNext(i); 35 | } 36 | } 37 | }).subscribeOn(Schedulers.io()); 38 | 39 | Observable observable2 = Observable.create(new ObservableOnSubscribe() { 40 | @Override 41 | public void subscribe(ObservableEmitter emitter) throws Exception { 42 | emitter.onNext("A"); 43 | } 44 | }).subscribeOn(Schedulers.io()); 45 | 46 | Observable.zip(observable1, observable2, new BiFunction() { 47 | @Override 48 | public String apply(Integer integer, String s) throws Exception { 49 | return integer + s; 50 | } 51 | }).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer() { 52 | @Override 53 | public void accept(String s) throws Exception { 54 | Log.d(TAG, s); 55 | } 56 | }, new Consumer() { 57 | @Override 58 | public void accept(Throwable throwable) throws Exception { 59 | Log.w(TAG, throwable); 60 | } 61 | }); 62 | ``` 63 | 64 | 在这个例子中, 我们分别创建了两根水管, 第一根水管用机器指令的执行速度来无限循环发送事件, 第二根水管随便发送点什么, 由于我们没有发送`Complete`事件, 因此第一根水管会一直发事件到它对应的水缸里去, 我们来看看运行结果是什么样. 65 | 66 | 运行结果GIF图: 67 | 68 | ![zip2.gif](images/RxJava2_15.gif) 69 | 70 | 我勒个草, 内存占用以斜率为1的直线迅速上涨, 几秒钟就300多M , 最终报出了OOM: 71 | 72 | ``` 73 | zlc.season.rxjava2demo W/art: Throwing OutOfMemoryError "Failed to allocate a 28 byte allocation with 74 | 4194304 free bytes and 8MB until OOM; 75 | zlc.season.rxjava2demo W/art: "main" prio=5 tid=1 Runnable 76 | zlc.season.rxjava2demo W/art: | group="main" sCount=0 dsCount=0 obj=0x75188710 self=0x7fc0efe7ba00 77 | zlc.season.rxjava2demo W/art: | sysTid=32686 nice=0 cgrp=default sched=0/0 handle=0x7fc0f37dc200 78 | zlc.season.rxjava2demo W/art: | state=R schedstat=( 0 0 0 ) utm=948 stm=120 core=1 HZ=100 79 | zlc.season.rxjava2demo W/art: | stack=0x7fff971e8000-0x7fff971ea000 stackSize=8MB 80 | zlc.season.rxjava2demo W/art: | held mutexes= "mutator lock"(shared held) 81 | zlc.season.rxjava2demo W/art: at java.lang.Integer.valueOf(Integer.java:742) 82 | ``` 83 | 84 | 出现这种情况肯定是我们不想看见的, 这里就可以引出我们的`Backpressure`了, 所谓的`Backpressure`其实就是为了控制流量, 水缸存储的能力毕竟有限, 因此我们还得从`源头`去解决问题, 既然你发那么快, 数据量那么大, 那我就想办法不让你发那么快呗. 85 | 86 | 那么这个`源头`到底在哪里, 究竟什么时候会出现这种情况, 这里只是说的Zip这一个例子, 其他的地方会出现吗? 带着这个问题我们来探究一下. 87 | 88 | 我们让事情变得简单一点, 从一个单一的`Observable`说起. 89 | 90 | 来看段代码: 91 | 92 | ```java 93 | Observable.create(new ObservableOnSubscribe() { 94 | @Override 95 | public void subscribe(ObservableEmitter emitter) throws Exception { 96 | for (int i = 0; ; i++) { //无限循环发事件 97 | emitter.onNext(i); 98 | } 99 | } 100 | }).subscribe(new Consumer() { 101 | @Override 102 | public void accept(Integer integer) throws Exception { 103 | Thread.sleep(2000); 104 | Log.d(TAG, "" + integer); 105 | } 106 | }); 107 | ``` 108 | 109 | 这段代码很简单, 上游同样无限循环的发送事件, 在下游每次接收事件前延时2秒. 上下游工作在`同一个线程`里, 来看下运行结果: 110 | 111 | ![peace.gif](images/RxJava2_16.gif) 112 | 113 | 哎卧槽, 怎么如此平静, 感觉像是走错了片场. 114 | 115 | 为什么呢, 因为上下游工作在`同一个线程`呀骚年们! 这个时候上游每次调用`emitter.onNext(i)`其实就相当于直接调用了Consumer中的: 116 | 117 | ```java 118 | public void accept(Integer integer) throws Exception { 119 | Thread.sleep(2000); 120 | Log.d(TAG, "" + integer); 121 | } 122 | ``` 123 | 124 | 所以这个时候其实就是上游每延时2秒发送一次. 最终的结果也说明了这一切. 125 | 126 | 那我们加个线程呢, 改成这样: 127 | 128 | ```java 129 | Observable.create(new ObservableOnSubscribe() { 130 | @Override 131 | public void subscribe(ObservableEmitter emitter) throws Exception { 132 | for (int i = 0; ; i++) { //无限循环发事件 133 | emitter.onNext(i); 134 | } 135 | } 136 | }).subscribeOn(Schedulers.io()) 137 | .observeOn(AndroidSchedulers.mainThread()) 138 | .subscribe(new Consumer() { 139 | @Override 140 | public void accept(Integer integer) throws Exception { 141 | Thread.sleep(2000); 142 | Log.d(TAG, "" + integer); 143 | } 144 | }); 145 | ``` 146 | 147 | 这个时候把上游切换到了IO线程中去, 下游到主线程去接收, 来看看运行结果呢: 148 | 149 | ![violence.gif](images/RxJava2_17.gif) 150 | 151 | 可以看到, 给上游加了个线程之后, 它就像脱缰的野马一样, 内存又爆掉了. 152 | 153 | 为什么不加线程和加上线程区别这么大呢, 这就涉及了`同步`和`异步`的知识了. 154 | 155 | 当上下游工作在`同一个线程`中时, 这时候是一个`同步`的订阅关系, 也就是说`上游`每发送一个事件`必须`等到`下游`接收处理完了以后才能接着发送下一个事件. 156 | 157 | 当上下游工作在`不同的线程`中时, 这时候是一个`异步`的订阅关系, 这个时候`上游`发送数据`不需要`等待`下游`接收, 为什么呢, 因为两个线程并不能直接进行通信, 因此上游发送的事件并不能直接到下游里去, 这个时候就需要一个田螺姑娘来帮助它们俩, 这个田螺姑娘就是我们刚才说的`水缸`! 上游把事件发送到水缸里去, 下游从水缸里取出事件来处理, 因此, 当上游发事件的速度太快, 下游取事件的速度太慢, 水缸就会迅速装满, 然后溢出来, 最后就OOM了. 158 | 159 | 这两种情况用图片来表示如下: 160 | 161 | 同步: 162 | 163 | ![同步.png](images/RxJava2_18.png) 164 | 165 | 异步: 166 | 167 | ![异步.png](images/RxJava2_19.png) 168 | 169 | 从图中我们可以看出, `同步和异步`的区别仅仅在于是否有`水缸`. 170 | 171 | 相信通过这个例子大家对线程之间的通信也有了比较清楚的认知和理解. 172 | 173 | 源头找到了, 只要有`水缸`, 就会出现上下游发送事件速度不平衡的情况, 因此当我们以后遇到这种情况时, 仔细思考一下水缸在哪里, 找到水缸, 你就找到了解决问题的办法. 174 | 175 | 既然源头找到了, 那么下一节我们就要来学习如何去解决了. 下节见 -------------------------------------------------------------------------------- /RxJava系列教程/9.RxJava 教程第三部分:驯服数据流之副作用.md: -------------------------------------------------------------------------------- 1 | # 9. 驯服数据流之副作用 2 | 3 | 前面两部分,我们学习到了如何创建 Observable 以及如何从 Observable 中获取数据。本部分将介绍一些更高级的用法,以及一些在大型项目中的最佳实践。 4 | 5 | ## Side effects(副作用) 6 | 7 | 没有副作用的函数通过参数和返回值来程序中的其他函数相互调用。当一个函数中的操作会影响其他函数中的返回结果时,我们称该函数有副作用。写数据到磁盘、记录日志、打印调试信息都是常见的副作用表现。Java 中在一个函数里面修改另外一个函数使用的对象的值是合法的。 8 | 9 | 副作用有时候很有用也有必要使用。但是有时候也有很多坑。 Rx 开发者应避免非必要的副作用,如果必须使用副作用的时候,则应该写下详细的说明文档。 10 | 11 | ### 副作用的问题 12 | 13 | Functional programming 通常避免副作用。带有副作用的函数(尤其是可以修改参数状态的)要求开发者了解更多实现的细节。增加了函数的复杂度并且导致函数被错误理解和使用,并且难以维护。副作用有故意的和无意的。可以通过封装或者使用不可变对象来避免副作用。有一些明智的封装规则可以显著的提高你 Rx 代码的可维护性。 14 | 15 | 我们使用一个带有副作用的示例来演示。 Java 中不可以在 Lambda 或者 匿名函数中引用外层的非 final 变量。 但是 Java 中的 final 变量只是保证了该编译引用的对象地址不变,但是对象本身的状态还是可以改变的。例如,下面是一个用来计数的一个类: 16 | 17 | ```java 18 | class Inc { 19 | private int count = 0; 20 | public void inc() { 21 | count++; 22 | } 23 | public int getCount() { 24 | return count; 25 | } 26 | } 27 | ``` 28 | 29 | 即使是一个 final 的 Inc 变量,还是可以通过调用其函数来修改他的状态。 注意 Java 并没有强制显式使用 final ,如果在你 Lambda 表达式中修改外层变量的引用对象地址(把外层变量重新复制为其他对象),则会出错。 30 | 31 | ```java 32 | Observable values = Observable.just("请", "不要", "有", "副作用"); 33 | 34 | Inc index = new Inc(); 35 | Observable indexed = 36 | values.map(w -> { 37 | index.inc(); 38 | return w; 39 | }); 40 | indexed.subscribe(w -> System.out.println(index.getCount() + ": " + w)); 41 | ``` 42 | 43 | 结果: 44 | 45 | ``` 46 | 1: 请 47 | 2: 不要 48 | 3: 有 49 | 4: 副作用 50 | ``` 51 | 52 | 目前还来看不出来问题。但是如果我们在该 Observable 上再次订阅一个 subscriber,则问题就出来了。 53 | 54 | ```java 55 | Observable values = Observable.just("请", "不要", "有", "副作用"); 56 | 57 | Inc index = new Inc(); 58 | Observable indexed = 59 | values.map(w -> { 60 | index.inc(); 61 | return w; 62 | }); 63 | indexed.subscribe(w -> System.out.println("1st observer: " + index.getCount() + ": " + w)); 64 | indexed.subscribe(w -> System.out.println("2nd observer: " + index.getCount() + ": " + w)); 65 | ``` 66 | 67 | 结果: 68 | 69 | ``` 70 | 1st observer: 1: 请 71 | 1st observer: 2: 不要 72 | 1st observer: 3: 有 73 | 1st observer: 4: 副作用 74 | 2nd observer: 5: 请 75 | 2nd observer: 6: 不要 76 | 2nd observer: 7: 有 77 | 2nd observer: 8: 副作用 78 | ``` 79 | 80 | 第二个 Subscriber 的索引是从 5 开始的。这明显不是我们想要的结果。这里的副作用很容易发现,但是真实应用中的副作用有些很难发现。 81 | 82 | ### 在数据流中组织数据 83 | 84 | 可以通过 scan 函数来计算每个数据的发射顺序: 85 | 86 | ```java 87 | class Indexed { 88 | public final int index; 89 | public final T item; 90 | public Indexed(int index, T item) { 91 | this.index = index; 92 | this.item = item; 93 | } 94 | } 95 | ``` 96 | 97 | ```java 98 | Observable values = Observable.just("No", "side", "effects", "please"); 99 | 100 | Observable> indexed = 101 | values.scan( 102 | new Indexed(0, null), 103 | (prev,v) -> new Indexed(prev.index+1, v)) 104 | .skip(1); 105 | indexed.subscribe(w -> System.out.println("1st observer: " + w.index + ": " + w.item)); 106 | indexed.subscribe(w -> System.out.println("2nd observer: " + w.index + ": " + w.item)); 107 | ``` 108 | 109 | 结果: 110 | 111 | ``` 112 | 1st observer: 1: No 113 | 1st observer: 2: side 114 | 1st observer: 3: effects 115 | 1st observer: 4: please 116 | 2nd observer: 1: No 117 | 2nd observer: 2: side 118 | 2nd observer: 3: effects 119 | 2nd observer: 4: please 120 | ``` 121 | 122 | 上面的结果为正确的。 我们把两个 Subscriber 共享的属性给删除了,这样他们就没法相互影响了。 123 | 124 | ### do 125 | 126 | 像记录日志这样的情况是需要副作用的。subscribe 总是有副作用,否则的话这个函数就没啥用了。虽然可以在 subscriber 中记录日志信息,但是这样做有缺点: 127 | 128 | 1. 在核心业务代码中混合了不太重要的日志代码 129 | 2. 如果想记录数据流中数据的中间状态,比如 执行某个操作之前和之后,则需要一个额外的 Subscriber 来实现。这样可能会导致最终 Subscriber 和 日志 Subscriber 看到的状态是不一样的。 130 | 131 | 下面的这些函数让我们可以更加简洁的实现需要的功能: 132 | 133 | ```java 134 | public final Observable doOnCompleted(Action0 onCompleted) 135 | public final Observable doOnEach(Action1> onNotification) 136 | public final Observable doOnEach(Observer observer) 137 | public final Observable doOnError(Action1 onError) 138 | public final Observable doOnNext(Action1 onNext) 139 | public final Observable doOnTerminate(Action0 onTerminate) 140 | ``` 141 | 142 | 这些函数在 Observable 每次事件发生的时候执行,并且返回 Observable。 这些函数明确的表明了他们有副作用,使用起来更加不易混淆: 143 | 144 | ```java 145 | Observable values = Observable.just("side", "effects"); 146 | 147 | values 148 | .doOnEach(new PrintSubscriber("Log")) 149 | .map(s -> s.toUpperCase()) 150 | .subscribe(new PrintSubscriber("Process")); 151 | ``` 152 | 153 | 结果: 154 | 155 | ``` 156 | Log: side 157 | Process: SIDE 158 | Log: effects 159 | Process: EFFECTS 160 | Log: Completed 161 | Process: Completed 162 | ``` 163 | 164 | 这里使用了上一章使用的帮助类 PrintSubscriber 。这些 do 开头的函数并不影响最终的 Subscriber。 例如: 165 | 166 | ```java 167 | static Observable service() { 168 | return Observable.just("First", "Second", "Third") 169 | .doOnEach(new PrintSubscriber("Log")); 170 | } 171 | ``` 172 | 173 | 可以这样使用该函数: 174 | 175 | ```java 176 | service() 177 | .map(s -> s.toUpperCase()) 178 | .filter(s -> s.length() > 5) 179 | .subscribe(new PrintSubscriber("Process")); 180 | ``` 181 | 182 | 结果: 183 | 184 | ``` 185 | Log: First 186 | Log: Second 187 | Process: SECOND 188 | Log: Third 189 | Log: Completed 190 | Process: Completed 191 | ``` 192 | 193 | 即便最终使用的时候过滤了一些数据,但是我们记录了服务器返回的所有结果。 194 | 这些函数中 doOnTerminate 在 Observable 结束发射数据之前发生。不管是因为 onCompleted 还是 onError 导致数据流结束。 另外还有一个 finallyDo 函数在 Observable 结束发射之后发生。 195 | 196 | ### doOnSubscribe, doOnUnsubscribe 197 | 198 | ```java 199 | public final Observable doOnSubscribe(Action0 subscribe) 200 | public final Observable doOnUnsubscribe(Action0 unsubscribe) 201 | ``` 202 | 203 | Subscription 和 unsubscription 并不是 Observable 发射的事件。而是 该 Observable 被 Observer 订阅和取消订阅的事件。 204 | 205 | ```java 206 | ReplaySubject subject = ReplaySubject.create(); 207 | Observable values = subject 208 | .doOnSubscribe(() -> System.out.println("New subscription")) 209 | .doOnUnsubscribe(() -> System.out.println("Subscription over")); 210 | 211 | Subscription s1 = values.subscribe(new PrintSubscriber("1st")); 212 | subject.onNext(0); 213 | Subscription s2 = values.subscribe(new PrintSubscriber("2st")); 214 | subject.onNext(1); 215 | s1.unsubscribe(); 216 | subject.onNext(2); 217 | subject.onNext(3); 218 | subject.onCompleted(); 219 | ``` 220 | 221 | 结果: 222 | 223 | ``` 224 | New subscription 225 | 1st: 0 226 | New subscription 227 | 2st: 0 228 | 1st: 1 229 | 2st: 1 230 | Subscription over 231 | 2st: 2 232 | 2st: 3 233 | 2st: Completed 234 | Subscription over 235 | ``` 236 | 237 | ## 使用 AsObservable 函数来封装 238 | 239 | Rx 使用面向对象的 Java 语言来实现 functional programming 风格编码。 需要注意面向对象中的问题。 例如下面一个天真版的返回 observable 的服务: 240 | 241 | ```java 242 | public class BrakeableService { 243 | public BehaviorSubject items = BehaviorSubject.create("Greet"); 244 | public void play() { 245 | items.onNext("Hello"); 246 | items.onNext("and"); 247 | items.onNext("goodbye"); 248 | } 249 | } 250 | ``` 251 | 252 | 上面的实现中, 调用者可以自己修改 items 引用的对象,也可以修改 Observable 发射的数据。所以需要对调用者隐藏 Subject 接口,只暴露 Observable 接口: 253 | 254 | ```java 255 | public class BrakeableService { 256 | private final BehaviorSubject items = BehaviorSubject.create("Greet"); 257 | 258 | public Observable getValues() { 259 | return items; 260 | } 261 | 262 | public void play() { 263 | items.onNext("Hello"); 264 | items.onNext("and"); 265 | items.onNext("goodbye"); 266 | } 267 | } 268 | ``` 269 | 270 | 上面的改进版本,看起来我们返回的是一个 Observable,但该返回的对象是不安全的,返回的其实是一个 Subject。 271 | 272 | ### asObservable 273 | 274 | 由于 Observable 是不可变的,所以 asObservable 函数是为了把一个 Observable 对象包装起来并安全的分享给其他人使用。 275 | 276 | ```java 277 | public Observable getValues() { 278 | return items.asObservable(); 279 | } 280 | ``` 281 | 282 | 这样的话,我们的 Subject 对象就被合理的保护起来了。这样其他恶意人员也无法修改你的 Observable 返回的数据了,在使用的过程中也可以避免出现错误了。 283 | 284 | ### 无法保护可变对象 285 | 286 | 在 RxJava 中, Rx 传递的是对象引用 而不是 对象的副本。在一个 地方修改了对象,在传递路径的其他地方上也是可见的。例如下面一个可变的对象: 287 | 288 | ```java 289 | class Data { 290 | public int id; 291 | public String name; 292 | public Data(int id, String name) { 293 | this.id = id; 294 | this.name = name; 295 | } 296 | } 297 | ``` 298 | 299 | 使用该对象的一个 Observable 和两个 Subscriber: 300 | 301 | ```java 302 | Observable data = Observable.just( 303 | new Data(1, "Microsoft"), 304 | new Data(2, "Netflix") 305 | ); 306 | 307 | data.subscribe(d -> d.name = "Garbage"); 308 | data.subscribe(d -> System.out.println(d.id + ": " + d.name)); 309 | ``` 310 | 311 | 结果: 312 | 313 | ``` 314 | 1: Garbage 315 | 2: Garbage 316 | ``` 317 | 318 | 第一个 Subscriber 先处理每个数据,在第一个 Subscriber 完成后第二个 Subscriber 开始处理数据,由于 Observable 在传递路径中使用的是对象引用,所以 第一个 Subscriber 中对对象做的修改,第二个 Subscriber 也会看到。 319 | -------------------------------------------------------------------------------- /RxJava系列教程/6.RxJava 教程第二部分:事件流基础之 检查数据.md: -------------------------------------------------------------------------------- 1 | ## 6. 事件流基础之检查数据 2 | 3 | Inspection 检查数据是否符合某一条件 4 | 5 | 前面一节介绍了如何过滤掉我们不关心的数据。有时候我们需要了解该数据流中的数据是否符合某一条件。本节来介绍一些检查数据流中数据的函数。 6 | 7 | ### all 8 | 9 | all 函数用来判断 observable 中发射的所有数据是否都满足一个条件。 10 | 11 | ```java 12 | public final Observable all(Func1 predicate)1 13 | ``` 14 | 15 | ```java 16 | Observable values = Observable.create(o -> { 17 | o.onNext(0); 18 | o.onNext(10); 19 | o.onNext(10); 20 | o.onNext(2); 21 | o.onCompleted(); 22 | }); 23 | 24 | 25 | Subscription evenNumbers = values 26 | .all(i -> i % 2 == 0) 27 | .subscribe( 28 | v -> System.out.println(v), 29 | e -> System.out.println("Error: " + e), 30 | () -> System.out.println("Completed") 31 | ); 32 | ``` 33 | 34 | 结果: 35 | 36 | ``` 37 | true 38 | Completed 39 | ``` 40 | 41 | all 函数返回的是一个发射一个布尔值的 Observable,而不是直接返回一个布尔值。原因在于我们并不知道源 Observable 何时才结束数据流的发射,只有当源 Observable 发射结束的时候, all 函数才知道结果是否都满足条件。只要遇到一个不满足条件的数据,all 函数就立刻返回 false。 只有当源 Observable 结束发射并且所发射的所有数据都满足条件的时候才会产生 true。在 observable 内返回结果可以方便的实现非阻塞操作。 在下个示例中可以看到 all 在遇到不满足的数据的时候就立刻结束了。 42 | 43 | ```java 44 | Observable values = Observable.interval(150, TimeUnit.MILLISECONDS).take(5); 45 | 46 | Subscription subscription = values 47 | .all(i -> i<3) // Will fail eventually 48 | .subscribe( 49 | v -> System.out.println("All: " + v), 50 | e -> System.out.println("All: Error: " + e), 51 | () -> System.out.println("All: Completed") 52 | ); 53 | Subscription subscription2 = values 54 | .subscribe( 55 | v -> System.out.println(v), 56 | e -> System.out.println("Error: " + e), 57 | () -> System.out.println("Completed") 58 | ); 59 | ``` 60 | 61 | 结果: 62 | 63 | ``` 64 | 0 65 | 1 66 | 2 67 | All: false 68 | All: Completed 69 | 3 70 | 4 71 | Completed 72 | ``` 73 | 74 | 如果源 Observable 出现了错误,则 all 操作就没有意义了,all 会直接发射一个 error 然后结束。 75 | 76 | ```java 77 | Observable values = Observable.create(o -> { 78 | o.onNext(0); 79 | o.onNext(2); 80 | o.onError(new Exception()); 81 | }); 82 | 83 | Subscription subscription = values 84 | .all(i -> i % 2 == 0) 85 | .subscribe( 86 | v -> System.out.println(v), 87 | e -> System.out.println("Error: " + e), 88 | () -> System.out.println("Completed") 89 | ); 90 | ``` 91 | 92 | 结果: 93 | 94 | ``` 95 | Error: java.lang.Exception 96 | ``` 97 | 98 | 如果源 Observable 在出错之前就发射了一个不满足条件的数据,则 源 Observable 的错误对 all 没有影响( all 遇到不满足条件的数据就结束了,结束的Observable 无法再继续发射数据了)。 99 | 100 | ```java 101 | Observable values = Observable.create(o -> { 102 | o.onNext(1); 103 | o.onNext(2); 104 | o.onError(new Exception()); 105 | }); 106 | 107 | Subscription subscription = values 108 | .all(i -> i % 2 == 0) 109 | .subscribe( 110 | v -> System.out.println(v), 111 | e -> System.out.println("Error: " + e), 112 | () -> System.out.println("Completed") 113 | ); 114 | ``` 115 | 116 | 结果: 117 | 118 | ``` 119 | false 120 | Completed 121 | ``` 122 | 123 | ### exists 124 | 125 | 如果源 exists 发射的数据中有一个满足条件,则 exists 就返回 true。 exists 和 all 一样也是返回一个 Observable 而不是直接返回 布尔值。 126 | 127 | ![RxJava](images/检查数据_01.png) 128 | 129 | ```java 130 | Observable values = Observable.range(0, 2); 131 | 132 | Subscription subscription = values 133 | .exists(i -> i > 2) 134 | .subscribe( 135 | v -> System.out.println(v), 136 | e -> System.out.println("Error: " + e), 137 | () -> System.out.println("Completed") 138 | ); 139 | ``` 140 | 141 | 结果: 142 | 143 | ``` 144 | false 145 | Completed12 146 | ``` 147 | 148 | 上面示例中只发射了 0 和1 两个数据,而这两个数据都不满足大于2的条件,所以返回结果为 false。 149 | 如果我们多发射几个数据,则就会满足条件了。 150 | 151 | ```java 152 | Observable values = Observable.range(0, 4); 153 | ``` 154 | 155 | 结果: 156 | 157 | ``` 158 | true 159 | Completed 160 | ``` 161 | 162 | ### isEmpty 163 | 164 | 顾名思义,判断一个 Observable 是否是空的,也就是没有发射任何数据就结束了。 165 | 166 | ![RxJava](images/检查数据_02.png) 167 | 168 | ```java 169 | Observable values = Observable.timer(1000, TimeUnit.MILLISECONDS); 170 | 171 | Subscription subscription = values 172 | .isEmpty() 173 | .subscribe( 174 | v -> System.out.println(v), 175 | e -> System.out.println("Error: " + e), 176 | () -> System.out.println("Completed") 177 | ); 178 | ``` 179 | 180 | 结果: 181 | 182 | ``` 183 | false 184 | Completed 185 | ``` 186 | 187 | 只要源 Observable 发射了一个数据,isEmpty 就立刻返回 false, 只有当源 Observable 完成了并且没有发射数据,isEmpty 才返回 true。 188 | 189 | ### contains 190 | 191 | contains 使用 Object.equals 函数来判断源 Observable 是否发射了相同的数据。只要遇到相同的数据,则 contains 就立刻返回。 192 | 193 | ![RxJava](images/检查数据_03.png) 194 | 195 | ```java 196 | Observable values = Observable.interval(100, TimeUnit.MILLISECONDS); 197 | 198 | Subscription subscription = values 199 | .contains(4L) 200 | .subscribe( 201 | v -> System.out.println(v), 202 | e -> System.out.println("Error: " + e), 203 | () -> System.out.println("Completed") 204 | ); 205 | ``` 206 | 207 | 结果: 208 | 209 | ``` 210 | true 211 | Completed 212 | ``` 213 | 214 | 注意上面使用的是 contains(4L), 而不是 contains(4), 由于 values 是 Observable 类型的, 所以需要使用 Long 类型而不能是 Integer 类型。 215 | 216 | 如果使用 contains(4) 则什么都不会打印出来, 由于 values 是一个无限的数据流,所以 contains 一直在等待一个相同的数据发射出来,但是在 values 里面是没有一样的数据的,导致 contains 一直等待下去。 217 | 218 | ### defaultIfEmpty 219 | 220 | 如果你不想单独处理没有发射任何数据的情况(需要用 isEmpty 函数来检查是否为空),则可以使用 defaultIfEmpty 函数来强制一个空的 Observable 发射一个默认数据。 221 | 222 | ![RxJava](images/检查数据_04.png) 223 | 224 | ```java 225 | Observable values = Observable.empty(); 226 | 227 | Subscription subscription = values 228 | .defaultIfEmpty(2) 229 | .subscribe( 230 | v -> System.out.println(v), 231 | e -> System.out.println("Error: " + e), 232 | () -> System.out.println("Completed") 233 | ); 234 | ``` 235 | 236 | 结果: 237 | 238 | ``` 239 | 2 240 | Completed 241 | ``` 242 | 243 | 只有当 onCompleted 事件发生了,并且 Observable 没有发射任何数据的时候,才会使用默认值;否则不会使用默认值。 如果发生了错误,则还会有错误的结果。 244 | 245 | ```java 246 | Observable values = Observable.error(new Exception()); 247 | 248 | Subscription subscription = values 249 | .defaultIfEmpty(2) 250 | .subscribe( 251 | v -> System.out.println(v), 252 | e -> System.out.println("Error: " + e), 253 | () -> System.out.println("Completed") 254 | ); 255 | ``` 256 | 257 | 结果: 258 | 259 | ``` 260 | Error: java.lang.Exception1 261 | ``` 262 | 263 | ### elementAt 264 | 265 | 从特定的位置选择一个数据发射。 266 | 267 | ![RxJava](images/检查数据_05.png) 268 | 269 | ```java 270 | Observable values = Observable.range(100, 10); 271 | 272 | Subscription subscription = values 273 | .elementAt(2) 274 | .subscribe( 275 | v -> System.out.println(v), 276 | e -> System.out.println("Error: " + e), 277 | () -> System.out.println("Completed") 278 | ); 279 | ``` 280 | 281 | 结果: 282 | 283 | ``` 284 | 102 285 | Completed 286 | ``` 287 | 288 | 该函数和访问数组或者集合类似,如果 Observable 发射的数据个数没有这么多,则会抛出 java.lang.IndexOutOfBoundsException 。可以使用一个默认值(elementAtOrDefault)来避免抛出 java.lang.IndexOutOfBoundsException。 289 | 290 | ```java 291 | Observable values = Observable.range(100, 10); 292 | 293 | Subscription subscription = values 294 | .elementAtOrDefault(22, 0) 295 | .subscribe( 296 | v -> System.out.println(v), 297 | e -> System.out.println("Error: " + e), 298 | () -> System.out.println("Completed") 299 | ); 300 | ``` 301 | 302 | 结果: 303 | 304 | ``` 305 | 0 306 | Completed 307 | ``` 308 | 309 | ### sequenceEqual 310 | 311 | 本节最后这个操作函数是用来比较两个 Observable 发射的数据是否是一样的,同样位置的数据是一样的。要求两个 Observable 发射的数据个数是一样的并且每个位置上的数据也是一样的。 该函数内部用 Object.equals 来比较数据,当然你也可以自己指定一个比较函数。 312 | 313 | ```java 314 | Observable strings = Observable.just("1", "2", "3"); 315 | Observable ints = Observable.just(1, 2, 3); 316 | 317 | Observable.sequenceEqual(strings, ints, (s,i) -> s.equals(i.toString())) 318 | //Observable.sequenceEqual(strings, ints) 319 | .subscribe( 320 | v -> System.out.println(v), 321 | e -> System.out.println("Error: " + e), 322 | () -> System.out.println("Completed") 323 | ); 324 | ``` 325 | 326 | 结果: 327 | 328 | ``` 329 | true 330 | Completed 331 | ``` 332 | 333 | 上面使用了自定义的比较函数,所以结果是一样的。 如果不指定自定义的 比较函数的话, 使用默认的 Object.equals 来比较,则返回的结果就是 False 了。 334 | 335 | 如果一个源 Observable 出现了错误,则比较结果的 Observable 也会出现错误并结束。 336 | 337 | ```java 338 | Observable values = Observable.create(o -> { 339 | o.onNext(1); 340 | o.onNext(2); 341 | o.onError(new Exception()); 342 | }); 343 | 344 | Observable.sequenceEqual(values, values) 345 | .subscribe( 346 | v -> System.out.println(v), 347 | e -> System.out.println("Error: " + e), 348 | () -> System.out.println("Completed") 349 | ); 350 | ``` 351 | 352 | 结果: 353 | 354 | ``` 355 | Error: java.lang.Exception 356 | ``` 357 | -------------------------------------------------------------------------------- /RxJava系列教程/10.RxJava 教程第三部分:驯服数据流之 避免 monad.md: -------------------------------------------------------------------------------- 1 | # 10. 驯服数据流之避免 monad 2 | 3 | monad 是函数式编程中的抽象概念,是一种高度的数学抽象,关于 monad 的详细介绍请看这里: [Functors, Applicatives, And Monads In Pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html) ,不要百度搜索其他的资料, 关于 monad 的介绍,在网上有 90% 都是错误的,误导人的。 4 | 5 | 在 [www.introtorx.com](http://www.introtorx.com/) 中也有一个简短的定义: 6 | 7 | Monad 是一种在模型域对象中封装了计算逻辑而不是数据的一种抽象数据构造类型。 8 | 9 | > Monads are a kind of abstract data type constructor that encapsulate program logic instead of data in the domain model. 10 | > Observable 就是一个 monad。Rx 代码定义了需要完成的任务,但是实际执行任务的过程确在 Rx 执行代码之外执行。本节中的 monad 我们只是指代 Observable。 11 | 12 | ### 为什么要避免 monad 13 | 14 | 主要有两个原因:第一个原因是 Rx 新手还是习惯传统的编码方式。使用另外一种方式(paradigm )来计算部分结果或许可以让你获取到正确的结果,但是你依然在尝试搞明白 Rx 是如何工作的。第二个原因是 我们使用的第三方库和组件并没有按照 Rx 的方法来设计。当重构现有的代码使用 Rx, 让 Rx 继续使用阻塞的方式工作也许是最好的选择。 15 | 16 | ### BlockingObservable 17 | 18 | 使用 BlockingObservable 可以把 Observable 中的数据通过阻塞的方式发射出来。任何一个 Observable 都可以使用下面两种方式来转换为阻塞的 Observable。 19 | 20 | ```java 21 | public final BlockingObservable toBlocking() 22 | ``` 23 | 24 | ```java 25 | public static BlockingObservable from(Observable o) 26 | ``` 27 | 28 | BlockingObservable 并没有继承 Observable,所以无法使用常用的操作函数。他自己实现了一部分功能,可以通过阻塞的方式来从中获取数据。里面有很多我们已经见到过的函数的阻塞实现。 29 | 30 | ### forEach 31 | 32 | Observable 有个函数叫做 forEach。 forEach 为 subscribe 的一个没有返回Subscription 的别名。 33 | 例如下面的例子: 34 | 35 | ```java 36 | Observable values = Observable.interval(100, TimeUnit.MILLISECONDS); 37 | 38 | values 39 | .take(5) 40 | .forEach(v -> System.out.println(v)); 41 | System.out.println("Subscribed"); 42 | ``` 43 | 44 | 结果: 45 | 46 | ``` 47 | Subscribed 48 | 0 49 | 1 50 | 2 51 | 3 52 | 4 53 | ``` 54 | 55 | 通过 forEach 可以处理 Observable 每个发射出来的数据。由于是非阻塞执行的,所以结果先答应出来 Subscribed,然后是每个发射的数字。 56 | 57 | BlockingObservable 没有 subscribe 函数,但是有这个 forEach 函数。 58 | 59 | ```java 60 | Observable values = Observable.interval(100, TimeUnit.MILLISECONDS); 61 | 62 | values 63 | .take(5) 64 | .toBlocking() 65 | .forEach(v -> System.out.println(v)); 66 | System.out.println("Subscribed"); 67 | ``` 68 | 69 | 结果: 70 | 71 | ``` 72 | 0 73 | 1 74 | 2 75 | 3 76 | 4 77 | Subscribed 78 | ``` 79 | 80 | 这里由于使用的是阻塞的 Observable,所以当 forEach 执行完后,才会执行后面的打印 Subscribed 的代码。同时阻塞的 Observable 也没有 onError 和 onCompleted 函数。当执行完成的时候,就执行完了;当错误发生的时候,异常就直接就地抛出了; 81 | 82 | ```java 83 | Observable values = Observable.error(new Exception("Oops")); 84 | 85 | try { 86 | values 87 | .take(5) 88 | .toBlocking() 89 | .forEach(v -> System.out.println(v)); 90 | } 91 | catch (Exception e) { 92 | System.out.println("Caught: " + e.getMessage()); 93 | } 94 | System.out.println("Subscribed"); 95 | ``` 96 | 97 | 结果: 98 | 99 | ``` 100 | Caught: java.lang.Exception: Oops 101 | Subscribed 102 | ``` 103 | 104 | ### first, last, single 105 | 106 | BlockingObservable 还有这3个函数,以及带有默认值的另外三个函数:firstOrDefault, lastOrDefault 和 singleOrDefault. 107 | 108 | 这些函数会阻塞当前的线程直到有数据发射出来并返回符合结果的数据: 109 | 110 | ```java 111 | Observable values = Observable.interval(100, TimeUnit.MILLISECONDS); 112 | 113 | long value = values 114 | .take(5) 115 | .toBlocking() 116 | .first(i -> i>2); 117 | System.out.println(value); 118 | ``` 119 | 120 | 结果: 121 | 122 | ``` 123 | 3 124 | ``` 125 | 126 | first 会一直阻塞,直到有数据发射并返回符合条件的数据。 127 | 和 forEach 一样,错误发生了也是就地抛出: 128 | 129 | ```java 130 | Observable values = Observable.interval(100, TimeUnit.MILLISECONDS); 131 | 132 | try { 133 | long value = values 134 | .take(5) 135 | .toBlocking() 136 | .single(i -> i>2); 137 | System.out.println(value); 138 | } 139 | catch (Exception e) { 140 | System.out.println("Caught: " + e); 141 | } 142 | ``` 143 | 144 | 结果: 145 | 146 | ``` 147 | Caught: java.lang.IllegalArgumentException: Sequence contains too many elements 148 | ``` 149 | 150 | ### To Iterable 151 | 152 | 还可以使用 BlockingObservable 上的一些方法把 Observable 转换为 iterables ,然后可以传统的 Java 方式来遍历这些集合。当需要处理数据的时候,就调用 Iterator 的 next() 函数,如果有数据 next() 就直接返回;如果没有数据 next() 函数就阻塞直到有数据产生。 153 | 154 | 有多种方式把 BlockingObservable 转换为 Iterable ,每种方式都有不同的区别。 155 | 156 | ### toIterable 157 | 158 | ```java 159 | public java.lang.Iterable toIterable() 160 | ``` 161 | 162 | ![RxJava](images/monad_01.png) 163 | 164 | 这种实现方式,把 Observable 所发射的所有数据给收集起来并缓存到一个集合中。由于缓存的存在,所以不会丢失数据。一单有下一个数据 next() 函数就返回。否则的话就阻塞到数据可用。注意 上图画的有点问题,看起来好像等 Observable 发射完后来返回集合。 165 | 166 | ```java 167 | Observable values = Observable.interval(500, TimeUnit.MILLISECONDS); 168 | 169 | Iterable iterable = values.take(5).toBlocking().toIterable(); 170 | for (long l : iterable) { 171 | System.out.println(l); 172 | } 173 | ``` 174 | 175 | 结果: 176 | 177 | ``` 178 | 0 179 | 1 180 | 2 181 | 3 182 | 4 183 | ``` 184 | 185 | 注意: iterable 的 hasNext() 或者 next() 函数都会阻塞直到有数据可用。如果 Observable 完成了, hasNext 返回 false, next 抛出异常:java.util.NoSuchElementException。 186 | 187 | ### next 188 | 189 | ```java 190 | public java.lang.Iterable next() 191 | ``` 192 | 193 | ![RxJava](images/monad_02.png) 194 | 195 | 这种实现数据没有缓存。 iterator 总是等待下一个数据并立刻返回。 196 | 197 | ```java 198 | Observable values = Observable.interval(500, TimeUnit.MILLISECONDS); 199 | 200 | values.take(5) 201 | .subscribe(v -> System.out.println("Emitted: " + v)); 202 | 203 | Iterable iterable = values.take(5).toBlocking().next(); 204 | for (long l : iterable) { 205 | System.out.println(l); 206 | Thread.sleep(750); 207 | } 208 | ``` 209 | 210 | 结果: 211 | 212 | ``` 213 | Emitted: 0 214 | 0 215 | Emitted: 1 216 | Emitted: 2 217 | 2 218 | Emitted: 3 219 | Emitted: 4 220 | 221 | ``` 222 | 223 | 这里的示例中, 打印语句(消费者)处理的速度比数据发射的速度慢。所以消费者会错过一些数据。 224 | 225 | ### latest 226 | 227 | ```java 228 | public java.lang.Iterable latest()1 229 | ``` 230 | 231 | latest 和 next 类似,区别就是 latest 会缓存一个数据。 232 | 233 | ```java 234 | Observable values = Observable.interval(500, TimeUnit.MILLISECONDS); 235 | 236 | values.take(5) 237 | .subscribe(v -> System.out.println("Emitted: " + v)); 238 | 239 | Iterable iterable = values.take(5).toBlocking().latest(); 240 | for (long l : iterable) { 241 | System.out.println(l); 242 | Thread.sleep(750); 243 | } 244 | ``` 245 | 246 | 结果: 247 | 248 | ``` 249 | Emitted: 0 250 | 0 251 | Emitted: 1 252 | 1 253 | Emitted: 2 254 | Emitted: 3 255 | 3 256 | Emitted: 4 257 | ``` 258 | 259 | 使用 latest 的时候,如果在下一个数据发射之前,当前的数据还没有被消费者消费,则当前的值就会丢失。如果 消费者比 生产者(Observable)发射的数据快,则 iterator 会阻塞并且等待下一个数据。 260 | 261 | 上面示例中的最后一个数据 4 并没有被消费掉。由于 onCompleted 是立刻结束的,导致下一次消费者通过 next 获取数据的时候,看到的是一个已经结束的 Observable,而 iterator.hasNext() 如果发现是一个已经结束的 Observable 则返回 false,尽管还有一个数据还没有被消费。 262 | 263 | ### mostRecent 264 | 265 | ```java 266 | public java.lang.Iterable mostRecent(T initialValue) 267 | ``` 268 | 269 | ![RxJava](images/monad_03.png) 270 | 271 | mostRecent 返回的 iterator 从来不会阻塞。他会缓存最近一个值,如果消费者比 生产者处理的速度慢,则有数据会丢失。和 latest 不一样的是, 只要消费者需要数据,则缓存的数据就会直接返回。这样,如果消费者处理数据的速度快,则消费者就会看到重复的数据。所以为了实现不阻塞的操作,该函数需要一个初始化的值。如果 Observable 还没有发射数据,消费者这个时候看到的就是这个初始化的值。 272 | 273 | ```java 274 | Observable values = Observable.interval(500, TimeUnit.MILLISECONDS); 275 | 276 | values.take(5) 277 | .subscribe(v -> System.out.println("Emitted: " + v)); 278 | 279 | Iterable iterable = values.take(5).toBlocking().mostRecent(-1L); 280 | for (long l : iterable) { 281 | System.out.println(l); 282 | Thread.sleep(400); 283 | } 284 | ``` 285 | 286 | 结果: 287 | 288 | ``` 289 | -1 290 | -1 291 | Emitted: 0 292 | 0 293 | Emitted: 1 294 | 1 295 | Emitted: 2 296 | 2 297 | Emitted: 3 298 | 3 299 | 3 300 | Emitted: 4 301 | ``` 302 | 303 | ### Future 304 | 305 | 使用 toFuture 函数也可以把 BlockingObservable 转换为一个 Future,该方法只是创建一个 Future 并返回,不会阻塞。Future 可以让消费者决定如何处理异步操作。Future 也可以处理异常情况。 306 | 307 | ```java 308 | Observable values = Observable.timer(500, TimeUnit.MILLISECONDS); 309 | 310 | values.subscribe(v -> System.out.println("Emitted: " + v)); 311 | 312 | Future future = values.toBlocking().toFuture(); 313 | System.out.println(future.get()); 314 | ``` 315 | 316 | 结果: 317 | 318 | ``` 319 | Emitted: 0 320 | 0 321 | ``` 322 | 323 | 通过这种方式创建的 Future,要求 Observable 只发射一个数据,和 single 函数要求的一样。如果发射了多个数据,则 Future 会抛出 java.lang.IllegalArgumentException. 324 | 325 | ### Locks 326 | 327 | #### Deadlocks 328 | 329 | 到目前为止我们都选择忽略可能导致死锁的情况。 Rx 的非阻塞特性导致很难创建非必要的死锁。然后本节中我们把 Observable 转换为 阻塞的操作,这样又导致死锁很容易出现了。 330 | 例如: 331 | 332 | ```java 333 | ReplaySubject subject = ReplaySubject.create(); 334 | 335 | subject.toBlocking().forEach(v -> System.out.println(v)); 336 | subject.onNext(1); 337 | subject.onNext(2); 338 | subject.onCompleted(); 339 | ``` 340 | 341 | forEach 只有当 Observable 结束发射的时候才返回。而后面的 onNext 和 onCompleted 需要 forEach 返回后才能执行,这样就导致了死锁。所以 forEach 会一直等待下去。 342 | 343 | ### 没有结束的Observable 344 | 345 | 有些阻塞操作(比如 last() )需要 Observable 结束发射数据才能返回。而 有些操作(比如 first() )需要 Observable 需要至少发射一个数据才能返回。所以在 BlockingObservable 上使用这些函数需要注意 ,如果 Observable 不满足条件则可能会导致该操作永远阻塞。所以为了避免永远阻塞的问题,可以指定一个超时时间间隔, 在后面的 Timeshifter 数据流部分会介绍如何做。 346 | -------------------------------------------------------------------------------- /RxJava2.0/给初学者的RxJava2.0教程-1.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 3 | 上个月RxJava2正式版发布了,但目前国内的资料还比较少,以前使用过RxJava1只需要看看更新文档就知道怎么使用了,但还有一些以前没用过RxJava的朋友可能就不知道怎么办了,不知道该看RxJava1还是直接跳到RxJava2。所以写下这个教程, 帮助那些没有用过RxJava的朋友入门。 4 | 5 | > 注:如果你觉得写得不好,请直接批评指出。 6 | 7 | 我先回答这个问题:学习RxJava2需要先学习RxJava1吗? 8 | 9 | 这个问题就像论坛经常问学Java前需要先学习C语言吗,这里就不引战了!(PHP是世界上最好的语言!!) 10 | 11 | 答案明显不是,如果你以前学过RxJava1,那么对于RxJava2只需要看看更新了哪些东西就行了,其最核心的思想并没有变化,如果你没学过RxJava1,没有关系,直接学习RxJava2。所以作为一个RxJava2的教程,本文中所有的名词都属于RxJava2中,并不涉及RxJava1。 12 | 13 | 要在Android中使用RxJava2, 先添加Gradle配置: 14 | 15 | ```gradle 16 | compile 'io.reactivex.rxjava2:rxjava:2.0.1' 17 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 18 | ``` 19 | 20 | ## 正题 21 | 22 | 在开始学习之前,先来介绍点原理性的东西。 23 | 24 | 网上也有很多介绍RxJava原理的文章,通常这些文章都从观察者模式开始,先讲观察者,被观察者,订阅关系巴拉巴拉一大堆,说实话,当我第一次看到这些文章的时候已经被这些名词给绕晕了,用了很长的时间才理清楚它们之间的关系。可能是我太蠢了,境界不够,领会不到那么多高大上的名词. 25 | 26 | 今天我用两根水管代替观察者和被观察者, 试图用通俗易懂的话把它们的关系解释清楚, 在这里我将从事件流这个角度来说明RxJava的基本工作原理。 27 | 28 | 先假设有两根水管: 29 | 30 | ![RxJava](images/RxJava2_01.png) 31 | 32 | 上面一根水管为事件产生的水管,叫它`上游`吧,下面一根水管为事件接收的水管叫它`下游`吧。 33 | 34 | 两根水管通过一定的方式连接起来,使得上游每产生一个事件,下游就能收到该事件。注意这里和官网的事件图是反过来的, 这里的事件发送的顺序是先1,后2,后3这样的顺序, 事件接收的顺序也是先1,后2,后3的顺序, 我觉得这样更符合我们普通人的思维, 简单明了 35 | 36 | 这里的`上游`和`下游`就分别对应着RxJava中的`Observable`和`Observer`,它们之间的连接就对应着`subscribe()`,因此这个关系用RxJava来表示就是 37 | 38 | ```java 39 | //创建一个上游 Observable: 40 | Observable observable = Observable.create(new ObservableOnSubscribe() { 41 | @Override 42 | public void subscribe(ObservableEmitter emitter) throws Exception { 43 | emitter.onNext(1); 44 | emitter.onNext(2); 45 | emitter.onNext(3); 46 | emitter.onComplete(); 47 | } 48 | }); 49 | //创建一个下游 Observer 50 | Observer observer = new Observer() { 51 | @Override 52 | public void onSubscribe(Disposable d) { 53 | Log.d(TAG, "subscribe"); 54 | } 55 | 56 | @Override 57 | public void onNext(Integer value) { 58 | Log.d(TAG, "" + value); 59 | } 60 | 61 | @Override 62 | public void onError(Throwable e) { 63 | Log.d(TAG, "error"); 64 | } 65 | 66 | @Override 67 | public void onComplete() { 68 | Log.d(TAG, "complete"); 69 | } 70 | }; 71 | //建立连接 72 | observable.subscribe(observer); 73 | ``` 74 | 75 | 这个运行的结果就是: 76 | 77 | ``` 78 | 12-02 03:37:17.818 4166-4166/zlc.season.rxjava2demo D/TAG: subscribe 79 | 12-02 03:37:17.819 4166-4166/zlc.season.rxjava2demo D/TAG: 1 80 | 12-02 03:37:17.819 4166-4166/zlc.season.rxjava2demo D/TAG: 2 81 | 12-02 03:37:17.819 4166-4166/zlc.season.rxjava2demo D/TAG: 3 82 | 12-02 03:37:17.819 4166-4166/zlc.season.rxjava2demo D/TAG: complete 83 | ``` 84 | 85 | > 注意: 只有当上游和下游建立连接之后, 上游才会开始发送事件. 也就是调用了`subscribe()`方法之后才开始发送事件. 86 | 87 | 把这段代码连起来写就成了RxJava引以为傲的链式操作: 88 | 89 | ```java 90 | Observable.create(new ObservableOnSubscribe() { 91 | @Override 92 | public void subscribe(ObservableEmitter emitter) throws Exception { 93 | emitter.onNext(1); 94 | emitter.onNext(2); 95 | emitter.onNext(3); 96 | emitter.onComplete(); 97 | } 98 | }).subscribe(new Observer() { 99 | @Override 100 | public void onSubscribe(Disposable d) { 101 | Log.d(TAG, "subscribe"); 102 | } 103 | 104 | @Override 105 | public void onNext(Integer value) { 106 | Log.d(TAG, "" + value); 107 | } 108 | 109 | @Override 110 | public void onError(Throwable e) { 111 | Log.d(TAG, "error"); 112 | } 113 | 114 | @Override 115 | public void onComplete() { 116 | Log.d(TAG, "complete"); 117 | } 118 | }); 119 | ``` 120 | 121 | 接下来解释一下其中两个陌生的玩意:`ObservableEmitter`和`Disposable` 122 | 123 | ObservableEmitter: Emitter是发射器的意思,那就很好猜了,这个就是用来发出事件的,它可以发出三种类型的事件,通过调用emitter的`onNext(T value)`、`onComplete()`和`onError(Throwable error)`就可以分别发出next事件、complete事件和error事件 124 | 125 | 但是,请注意,并不意味着你可以随意乱七八糟发射事件,需要满足一定的规则: 126 | 127 | - 上游可以发送无限个onNext, 下游也可以接收无限个onNext 128 | - 当上游发送了一个onComplete后, 上游onComplete之后的事件将会`继续`发送, 而下游收到onComplete事件之后将`不再继续`接收事件 129 | - 当上游发送了一个onError后, 上游onError之后的事件将`继续`发送, 而下游收到onError事件之后将`不再继续`接收事件. 130 | - 上游可以不发送onComplete或onError 131 | - 最为关键的是onComplete和onError必须唯一并且互斥, 即不能发多个onComplete, 也不能发多个onError, 也不能先发一个onComplete, 然后再发一个onError, 反之亦然 132 | 133 | > 注: 关于onComplete和onError唯一并且互斥这一点, 是需要自行在代码中进行控制, 如果你的代码逻辑中违背了这个规则, **并不一定会导致程序崩溃. **比如发送多个onComplete是可以正常运行的, 依然是收到第一个onComplete就不再接收了, 但若是发送多个onError, 则收到第二个onError事件会导致程序会崩溃. 134 | 135 | 以上几个规则用示意图表示如下: 136 | 137 | | 事件 | 示意图 | 138 | | -------------- | -------------------------------- | 139 | | 只发送onNext事件 | ![RxJava](images/RxJava2_02.png) | 140 | | 发送onComplete事件 | ![RxJava](images/RxJava2_03.png) | 141 | | 发送onError事件 | ![RxJava](images/RxJava2_04.png) | 142 | 143 | 介绍了ObservableEmitter, 接下来介绍Disposable, 这个单词的字面意思是一次性用品,用完即可丢弃的. 那么在RxJava中怎么去理解它呢, 对应于上面的水管的例子, 我们可以把它理解成两根管道之间的一个机关, 当调用它的`dispose()`方法时, 它就会将两根管道切断, 从而导致下游收不到事件 144 | 145 | > 注意: 调用dispose()并不会导致上游不再继续发送事件, 上游会继续发送剩余的事件. 146 | 147 | 来看个例子, 我们让上游依次发送`1,2,3,complete,4`,在下游收到第二个事件之后, 切断水管, 看看运行结果: 148 | 149 | ```java 150 | Observable.create(new ObservableOnSubscribe() { 151 | @Override 152 | public void subscribe(ObservableEmitter emitter) throws Exception { 153 | Log.d(TAG, "emit 1"); 154 | emitter.onNext(1); 155 | Log.d(TAG, "emit 2"); 156 | emitter.onNext(2); 157 | Log.d(TAG, "emit 3"); 158 | emitter.onNext(3); 159 | Log.d(TAG, "emit complete"); 160 | emitter.onComplete(); 161 | Log.d(TAG, "emit 4"); 162 | emitter.onNext(4); 163 | } 164 | }).subscribe(new Observer() { 165 | private Disposable mDisposable; 166 | private int i; 167 | 168 | @Override 169 | public void onSubscribe(Disposable d) { 170 | Log.d(TAG, "subscribe"); 171 | mDisposable = d; 172 | } 173 | 174 | @Override 175 | public void onNext(Integer value) { 176 | Log.d(TAG, "onNext: " + value); 177 | i++; 178 | if (i == 2) { 179 | Log.d(TAG, "dispose"); 180 | mDisposable.dispose(); 181 | Log.d(TAG, "isDisposed : " + mDisposable.isDisposed()); 182 | } 183 | } 184 | 185 | @Override 186 | public void onError(Throwable e) { 187 | Log.d(TAG, "error"); 188 | } 189 | 190 | @Override 191 | public void onComplete() { 192 | Log.d(TAG, "complete"); 193 | } 194 | }); 195 | ``` 196 | 197 | 运行结果为: 198 | 199 | ``` 200 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: subscribe 201 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 1 202 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: onNext: 1 203 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 2 204 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: onNext: 2 205 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: dispose 206 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: isDisposed : true 207 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 3 208 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit complete 209 | 12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 4 210 | ``` 211 | 212 | 从运行结果我们看到, 在收到onNext 2这个事件后, 切断了水管, 但是上游仍然发送了3, complete, 4这几个事件, 而且上游并没有因为发送了onComplete而停止. 同时可以看到下游的`onSubscribe()`方法是最先调用的 213 | 214 | Disposable的用处不止这些, 后面讲解到了线程的调度之后, 我们会发现它的重要性. 随着后续深入的讲解, 我们会在更多的地方发现它的身影 215 | 216 | 另外, `subscribe()`有多个重载的方法: 217 | 218 | ```java 219 | public final Disposable subscribe() {} 220 | public final Disposable subscribe(Consumer onNext) {} 221 | public final Disposable subscribe(Consumer onNext, Consumer onError) {} 222 | public final Disposable subscribe(Consumer onNext, Consumer onError, Action onComplete) {} 223 | public final Disposable subscribe(Consumer onNext, Consumer onError, Action onComplete, Consumer onSubscribe) {} 224 | public final void subscribe(Observer observer) {} 225 | ``` 226 | 227 | 最后一个带有`Observer`参数的我们已经使用过了,这里对其他几个方法进行说明. 228 | 229 | - 不带任何参数的`subscribe()` 表示下游不关心任何事件,你上游尽管发你的数据去吧, 老子可不管你发什么. 230 | - 带有一个`Consumer`参数的方法表示下游只关心onNext事件, 其他的事件我假装没看见, 因此我们如果只需要onNext事件可以这么写: 231 | 232 | ```java 233 | Observable.create(new ObservableOnSubscribe() { 234 | @Override 235 | public void subscribe(ObservableEmitter emitter) throws Exception { 236 | Log.d(TAG, "emit 1"); 237 | emitter.onNext(1); 238 | Log.d(TAG, "emit 2"); 239 | emitter.onNext(2); 240 | Log.d(TAG, "emit 3"); 241 | emitter.onNext(3); 242 | Log.d(TAG, "emit complete"); 243 | emitter.onComplete(); 244 | Log.d(TAG, "emit 4"); 245 | emitter.onNext(4); 246 | } 247 | }).subscribe(new Consumer() { 248 | @Override 249 | public void accept(Integer integer) throws Exception { 250 | Log.d(TAG, "onNext: " + integer); 251 | } 252 | }); 253 | ``` 254 | 255 | - 其他几个方法同理, 这里就不一一解释了 256 | 257 | 好了本次的教程到此结束, 下一节中我们将会学习RxJava强大的线程调度 -------------------------------------------------------------------------------- /RxJava系列教程/4.RxJava 教程第二部分:事件流基础之 创建事件流.md: -------------------------------------------------------------------------------- 1 | # 4. 事件流基础之创建事件流 2 | 3 | 了解了 Rx 的概念,就要了解怎么创建和操作事件流了。操作事件流的原始实现是基于 C# 的 LINQ,而 LINQ 是受到 functional programming 启发的。如果你了解 LINQ 更容易理解本节内容,如果不了解也没关系。我们将从最简单的内容开始介绍。大部分的 Rx 操作函数(operators )用来操作已经存在的事件流。在介绍操作函数之前,先来看看如何创建一个 Observable。 4 | 5 | ## 创建一个事件流 6 | 7 | 在第一部分示例中,我们使用 Subject 来手工的把数据推送给他并创建一个事件流。我们使用这种方式来示例一些核心的概念和Rx 中一些核心的函数 subscribe。大部分时候使用 Subject 都不是创建 Observable 的最佳方式。下面将介绍如何创建 Observable 来发射事件流。 8 | 9 | 简单的工厂方法 10 | 11 | Observable 有很多工厂方法可以创建一个事件流。 12 | 13 | #### Observable.just 14 | 15 | just 函数创建一个发射预定义好的数据的 Observable ,发射完这些数据后,事件流就结束了。 16 | 17 | ```java 18 | Observable values = Observable.just("one", "two", "three"); 19 | Subscription subscription = values.subscribe( 20 | v -> System.out.println("Received: " + v), 21 | e -> System.out.println("Error: " + e), 22 | () -> System.out.println("Completed") 23 | ); 24 | ``` 25 | 26 | 输出结果: 27 | 28 | ``` 29 | Received: one 30 | Received: two 31 | Received: three 32 | Completed 33 | ``` 34 | 35 | #### Observable.empty 36 | 37 | 这个函数创建的 Observable 只发射一个 onCompleted 事件就结束了。 38 | 39 | ```java 40 | Observable values = Observable.empty(); 41 | Subscription subscription = values.subscribe( 42 | v -> System.out.println("Received: " + v), 43 | e -> System.out.println("Error: " + e), 44 | () -> System.out.println("Completed") 45 | ); 46 | ``` 47 | 48 | 输出结果: 49 | 50 | ``` 51 | Completed 52 | ``` 53 | 54 | #### Observable.never 55 | 56 | 这个 Observable 将不会发射任何事件和数据。 57 | 58 | ```java 59 | Observable values = Observable.never(); 60 | Subscription subscription = values.subscribe( 61 | v -> System.out.println("Received: " + v), 62 | e -> System.out.println("Error: " + e), 63 | () -> System.out.println("Completed") 64 | ); 65 | ``` 66 | 67 | 上面的代码不会打印任何东西。但是这个代码并没有阻塞住,实际上上面的代码立刻就执行完了。 68 | 69 | #### Observable.error 70 | 71 | 这个 Observable 将会发射一个 error 事件,然后结束。 72 | 73 | ```java 74 | Observable values = Observable.error(new Exception("Oops")); 75 | Subscription subscription = values.subscribe( 76 | v -> System.out.println("Received: " + v), 77 | e -> System.out.println("Error: " + e), 78 | () -> System.out.println("Completed") 79 | ); 80 | ``` 81 | 82 | 输出结果: 83 | 84 | ``` 85 | Error: java.lang.Exception: Oops1 86 | ``` 87 | 88 | #### Observable.defer 89 | 90 | defer 并没有定义一个新的 Observable,defer 只是用来声明当 Subscriber 订阅到一个 Observable 上时,该 Observable 应该如何创建。例如,如果我们想创建一个发射当前时间然后就结束的 Observable,发射一个数据然后结束,看起来用 just 实现即可: 91 | 92 | ```java 93 | Observable now = Observable.just(System.currentTimeMillis()); 94 | 95 | now.subscribe(System.out::println); 96 | Thread.sleep(1000); 97 | now.subscribe(System.out::println); 98 | ``` 99 | 100 | 输出结果: 101 | 102 | ``` 103 | 1431443908375 104 | 1431443908375 105 | ``` 106 | 107 | 注意上面两个 subscriber 相隔1秒订阅这个 Observable,但是他们收到的时间数据是一样的!这是因为当订阅的时候,时间数据只调用一次。其实你希望的是,当一个 subscriber 订阅的时候才去获取当前的时间。 defer 的参数是一个返回一个 Observable 对象的函数。该函数返回的 Observable 对象就是 defer 返回的 Observable 对象。 重点是,每当一个新的 Subscriber 订阅的时候,这个函数就重新执行一次。 108 | 109 | ```java 110 | Observable now = Observable.defer(() -> 111 | Observable.just(System.currentTimeMillis())); 112 | 113 | now.subscribe(System.out::println); 114 | Thread.sleep(1000); 115 | now.subscribe(System.out::println); 116 | ``` 117 | 118 | 输出结果: 119 | 120 | ``` 121 | 1431444107854 122 | 1431444108858 123 | ``` 124 | 125 | #### Observable.create 126 | 127 | create 是非常强大的一个函数。可以创建任何你需要的 Observable。 128 | 129 | ```java 130 | static Observable create(Observable.OnSubscribe f) 131 | ``` 132 | 133 | 上面是 create 函数的定义,参数 Observable.OnSubscribe 看起来很简单。OnSubscribe 只有一个函数其参数为 Subscriber。在该函数内我们可以手工的发射事件和数据到 subscriber。 134 | 135 | ```java 136 | Observable values = Observable.create(o -> { 137 | o.onNext("Hello"); 138 | o.onCompleted(); 139 | }); 140 | Subscription subscription = values.subscribe( 141 | v -> System.out.println("Received: " + v), 142 | e -> System.out.println("Error: " + e), 143 | () -> System.out.println("Completed") 144 | ); 145 | ``` 146 | 147 | 输出结果: 148 | 149 | ``` 150 | Received: Hello 151 | Completed 152 | ``` 153 | 154 | 当有 Subscriber 订阅到这个 Observable 时(上面示例中的 values ),这个 Subscriber 对象就是你实现的函数中的参数 Subscriber。然后你可以在你的代码中把数据发射到这个 subscriber 中。注意,当数据发射完后,你需要手工的调用 onCompleted 来表明发射完成了。 155 | 156 | 如果之前的所有方法都不满足你的要求时,这个函数应当作为你创建自定义 Observable 的最佳方式。其实现方式和 第一部分我们通过 Subject 来发射事件类似,但是有几点非常重要的区别。首先:数据源被封装起来了,并和不相关的代码隔离开了。其次:Subject 有一些不太明显的问题,通过使用 Subject 你自己在管理状态,并且任何访问该 Subject 对象的人都可以往里面发送数据然后改变事件流。 157 | 158 | 还一个主要的区别是执行代码的时机,使用 create 创建的 Observable,当 Observable 创建的时候,你的函数还没有执行,只有当有 Subscriber 订阅的时候才执行。这就意味着每次当有 Subscriber 订阅的时候,该函数就执行一次。和 defer 的功能类似。结果和 ReplaySubject 类似, ReplaySubject 会缓存结果 当有新的 Subscriber 订阅的时候,把缓存的结果在发射给新的 Subscriber。如果要使用 ReplaySubject 来实现和 create 类似的功能,如果 create 中创建数据的函数是阻塞的话,则 ReplaySubject 在创建的时候线程会阻塞住知道 创建函数执行完。如果不想阻塞当前线程的话,则需要手工创建一个线程来初始化数据。其实 Rx 有更加优雅的方式解决这个问题。 159 | 160 | 其实使用 Observable.create 可以实现前面几个工厂方法的功能。比如 上面的 create 函数的功能和 Observable.just(“hello”) 的功能是一样的。 161 | 162 | ## Functional unfolds 163 | 164 | 在 functional programming(函数式编程)中,创建一系列数字是非常常见的。 RxJava 也提供了一些工厂方法来创建这样的序列。 165 | 166 | #### Observable.range 167 | 168 | 做过函数式编码的程序员都了解这个函数的意思。 该函数发射一个整数序列: 169 | 170 | ```java 171 | Observable values = Observable.range(10, 15); 172 | ``` 173 | 174 | 上面示例将生成一个从 10 到 24 的数字序列(从 10 开始,发射 15个数字)。 175 | 176 | #### Observable.interval 177 | 178 | 创建一个无限的计时序列,每隔一段时间发射一个数字,从 0 开始: 179 | 180 | ```java 181 | Observable values = Observable.interval(1000, TimeUnit.MILLISECONDS); 182 | Subscription subscription = values.subscribe( 183 | v -> System.out.println("Received: " + v), 184 | e -> System.out.println("Error: " + e), 185 | () -> System.out.println("Completed") 186 | ); 187 | System.in.read(); 188 | ``` 189 | 190 | 输出结果: 191 | 192 | ``` 193 | Received: 0 194 | Received: 1 195 | Received: 2 196 | Received: 3 197 | ... 198 | ``` 199 | 200 | 如果我们不调用 unsubscribe 的话,这个序列是不会停止的。 201 | 202 | 上面的代码在最后有个 System.in.read(); 阻塞语句,这个语句是有必要的,不然的话,程序不会打印任何内容就退出了。原因是我们的操作不是阻塞的:我们创建了一个每隔一段时间就发射数据的 Observable,然后我们注册了一个 Subscriber 来打印收到的数据。这两个操作都是非阻塞的,而 发射数据的计时器是运行在另外一个线程的,但是这个线程不会阻止 JVM 结束当前的程序,所以 如果没有 System.in.read(); 这个阻塞操作,还没发射数据则程序就已经结束运行了。 203 | 204 | #### Observable.timer 205 | 206 | Observable.timer 有两个重载函数。第一个示例创建了一个 Observable, 该 Observable 等待一段时间,然后发射数据 0 ,然后就结束了。 207 | 208 | ```java 209 | Observable values = Observable.timer(1, TimeUnit.SECONDS); 210 | Subscription subscription = values.subscribe( 211 | v -> System.out.println("Received: " + v), 212 | e -> System.out.println("Error: " + e), 213 | () -> System.out.println("Completed") 214 | ); 215 | ``` 216 | 217 | 输出结果: 218 | 219 | ``` 220 | Received: 0 221 | Completed 222 | ``` 223 | 224 | 另外一个示例是,先等待一段时间,然后开始按照间隔的时间一直发射数据: 225 | 226 | ```java 227 | Observable values = Observable.timer(2, 1, TimeUnit.SECONDS); 228 | Subscription subscription = values.subscribe( 229 | v -> System.out.println("Received: " + v), 230 | e -> System.out.println("Error: " + e), 231 | () -> System.out.println("Completed") 232 | ); 233 | ``` 234 | 235 | 输出结果: 236 | 237 | ``` 238 | Received: 0 239 | Received: 1 240 | Received: 2 241 | ... 242 | ``` 243 | 244 | 上面的示例,先等待2秒,然后每隔一秒开始发射数据。 245 | 246 | ## 转换为 Observable 247 | 248 | 已经有很多工具来处理序列数据、集合和异步事件了,但是这些工具可能无法直接在 Rx 中使用。下面来介绍几个方法可以把这些工具产生的结果转换到你的 Rx 代码中。 249 | 250 | 如果你在使用类似 JavaFX 中的异步事件处理,则可以使用 Observable.create 把异步事件转换到 Observable 中: 251 | 252 | ```java 253 | Observable events = Observable.create(o -> { 254 | button2.setOnAction(new EventHandler() { 255 | @Override public void handle(ActionEvent e) { 256 | o.onNext(e) 257 | } 258 | }); 259 | }) 260 | ``` 261 | 262 | 根据事件的不同,事件的类型(上面的示例中事件类型为 ActionEvent)可能适合作为 Observable 发射数据的类型。 如果你需要的是该事件类型的属性(比如事件发射的位置),则获取该属性并把获取到的结果发射给最终的 Subscriber ,Subscriber 接受到结果可能和事件发生时的数据不一样。为了避免这种问题,在 Observable 中传递的数据应该保持其状态不变。 263 | 264 | #### Observable.from 265 | 266 | 在 Java 并发框架中经常使用 Future 来获取异步结果。 通过使用 from 可以把 Future 的结果发射到 Observable 中: 267 | 268 | ```java 269 | FutureTask f = new FutureTask(() -> { 270 | Thread.sleep(2000); 271 | return 21; 272 | }); 273 | new Thread(f).start(); 274 | 275 | Observable values = Observable.from(f); 276 | 277 | Subscription subscription = values.subscribe( 278 | v -> System.out.println("Received: " + v), 279 | e -> System.out.println("Error: " + e), 280 | () -> System.out.println("Completed") 281 | ); 282 | ``` 283 | 284 | 输出结果: 285 | 286 | ``` 287 | Received: 21 288 | Completed 289 | ``` 290 | 291 | 当 FutureTask 执行完后, Observable 发射 Future 获取到的结果然后结束。如果任务 取消了,则 Observable 会发射一个 java.util.concurrent.CancellationException 错误信息。 292 | 293 | 你还可以对 Future 设置超时时间: 294 | 295 | ```java 296 | Observable values = Observable.from(f, 1000, TimeUnit.MILLISECONDS); 297 | ``` 298 | 299 | 当过了超时时间后, Future 还是没有返回结果, Observable 可以忽略其结果并 发射一个 TimeoutException。 300 | 301 | Observable.from 还有重载的函数可以用一个数据集合或者一个可以遍历的iterable 的数据来生成一个 Observable。逐个发射集合中的数据,最后发射一个 onCompleted 事件。 302 | 303 | ```java 304 | Integer[] array = {1,2,3}; 305 | Observable values = Observable.from(array); 306 | Subscription subscription = values.subscribe( 307 | v -> System.out.println("Received: " + v), 308 | e -> System.out.println("Error: " + e), 309 | () -> System.out.println("Completed") 310 | ); 311 | ``` 312 | 313 | 输出结果如下: 314 | 315 | ``` 316 | Received: 1 317 | Received: 2 318 | Received: 3 319 | Completed 320 | ``` 321 | 322 | Observable 和 Iterable 或者 Stream 是不能通用的。Observables 是基于 push 模型的,而 Iterable 是基于 pull 模型的。 -------------------------------------------------------------------------------- /RxJava系列教程/15.RxJava 教程第三部分:驯服数据流之 hot & cold Observable.md: -------------------------------------------------------------------------------- 1 | # 15. 驯服数据流之 hot & cold Observable 2 | 3 | Observable 数据流有两种类型:hot 和 cold。这两种类型有很大的不同。本节介绍他们的区别,以及作为 Rx 开发者应该如何正确的使用他们。 4 | 5 | ## Cold observables 6 | 7 | 只有当有订阅者订阅的时候, Cold Observable 才开始执行发射数据流的代码。并且每个订阅者订阅的时候都独立的执行一遍数据流代码。 Observable.interval 就是一个 Cold Observable。每一个订阅者都会独立的收到他们的数据流。 8 | 9 | ```java 10 | Observable cold = Observable.interval(200, TimeUnit.MILLISECONDS); 11 | 12 | cold.subscribe(i -> System.out.println("First: " + i)); 13 | Thread.sleep(500); 14 | cold.subscribe(i -> System.out.println("Second: " + i)); 15 | 16 | ``` 17 | 18 | 结果: 19 | 20 | ``` 21 | First: 0 22 | First: 1 23 | First: 2 24 | Second: 0 25 | First: 3 26 | Second: 1 27 | First: 4 28 | Second: 2 29 | ... 30 | ``` 31 | 32 | 虽然这两个 Subscriber 订阅到同一个Observable 上,只是订阅的时间不同,他们都收到同样的数据流,但是同一时刻收到的数据是不同的。 33 | 34 | 在本教程中之前所见到的 Observable 都是 Cold Observable。 Observable.create 创建的也是 Cold Observable,而 just, range, timer 和 from 这些创建的同样是 Cold Observable。 35 | 36 | ## Hot observables 37 | 38 | Hot observable 不管有没有订阅者订阅,他们创建后就开发发射数据流。 一个比较好的示例就是 鼠标事件。 不管系统有没有订阅者监听鼠标事件,鼠标事件一直在发生,当有订阅者订阅后,从订阅后的事件开始发送给这个订阅者,之前的事件这个订阅者是接受不到的;如果订阅者取消订阅了,鼠标事件依然继续发射。 39 | 40 | ## Publish 41 | 42 | Cold Observable 和 Hot Observable 之间可以相互转化。使用 publish 操作函数可以把 Cold Observable 转化为 Hot Observable。 43 | 44 | ```java 45 | public final ConnectableObservable publish() 46 | ``` 47 | 48 | ![RxJava](images/hot & cold Observable_01.png) 49 | 50 | publish 返回一个 ConnectableObservable 对象,这个对象是 Observable 的之类,多了三个函数: 51 | 52 | ```java 53 | public final Subscription connect() 54 | public abstract void connect(Action1 connection) 55 | public Observable refCount() 56 | ``` 57 | 58 | 另外还有一个重载函数,可以在发射数据之前对数据做些处理: 59 | 60 | ```java 61 | public final Observable publish(Func1,? extends Observable> selector) 62 | ``` 63 | 64 | 之前介绍的所有对 Observable 的操作都可以在 selector 中使用。你可以通过 selector 参数创建一个 Subscription ,后来的订阅者都订阅到这一个 Subscription 上,这样可以确保所有的订阅者都在同一时刻收到同样的数据。 65 | 66 | 这个重载函数返回的是 Observable 而不是 ConnectableObservable, 所以下面讨论的操作函数无法在这个重载函数返回值上使用。 67 | 68 | ## connect 69 | 70 | ConnectableObservable 如果不调用 connect 函数则不会触发数据流的执行。当调用 connect 函数以后,会创建一个新的 subscription 并订阅到源 Observable (调用 publish 的那个 Observable)。这个 subscription 开始接收数据并把它接收到的数据转发给所有的订阅者。这样,所有的订阅者在同一时刻都可以收到同样的数据。 71 | 72 | ```java 73 | ConnectableObservable cold = Observable.interval(200, TimeUnit.MILLISECONDS).publish(); 74 | cold.connect(); 75 | 76 | cold.subscribe(i -> System.out.println("First: " + i)); 77 | Thread.sleep(500); 78 | cold.subscribe(i -> System.out.println("Second: " + i)); 79 | ``` 80 | 81 | 结果: 82 | 83 | ``` 84 | First: 0 85 | First: 1 86 | First: 2 87 | Second: 2 88 | First: 3 89 | Second: 3 90 | First: 4 91 | Second: 4 92 | First: 5 93 | Second: 5 94 | ``` 95 | 96 | ## Disconnecting 97 | 98 | connect 函数返回的是一个 Subscription,和 Observable.subscribe返回的结果一样。 可以使用这个 Subscription 来取消订阅到 ConnectableObservable。 如果调用 这个 Subscription 的 unsubscribe 函数,可以停止把数据转发给 Observer,但是这些 Observer 并没有从 ConnectableObservable 上取消注册,只是停止接收数据了。如果再次调用 connect , 则 ConnectableObservable 开始一个新的订阅,在 ConnectableObservable 上订阅的 Observer 会再次开始接收数据。 99 | 100 | ```java 101 | ConnectableObservable connectable = Observable.interval(200, TimeUnit.MILLISECONDS).publish(); 102 | Subscription s = connectable.connect(); 103 | 104 | connectable.subscribe(i -> System.out.println(i)); 105 | 106 | Thread.sleep(1000); 107 | System.out.println("Closing connection"); 108 | s.unsubscribe(); 109 | 110 | Thread.sleep(1000); 111 | System.out.println("Reconnecting"); 112 | s = connectable.connect(); 113 | ``` 114 | 115 | 结果: 116 | 117 | ``` 118 | 0 119 | 1 120 | 2 121 | 3 122 | 4 123 | Closing connection 124 | Reconnecting 125 | 0 126 | 1 127 | 2 128 | ... 129 | ``` 130 | 131 | 通过调用 connect 来重新开始订阅,会创建一个新的订阅。如果源 Observable 为 Cold Observable 则数据流会重新执行一遍。 132 | 133 | 如果你不想结束数据流,只想从 publish 返回的 Hot Observable 上取消注册,则可以使用 subscribe 函数返回的 Subscription 对象。 134 | 135 | ```java 136 | ConnectableObservable connectable = Observable.interval(200, TimeUnit.MILLISECONDS).publish(); 137 | Subscription s = connectable.connect(); 138 | 139 | Subscription s1 = connectable.subscribe(i -> System.out.println("First: " + i)); 140 | Thread.sleep(500); 141 | Subscription s2 = connectable.subscribe(i -> System.out.println("Second: " + i)); 142 | 143 | Thread.sleep(500); 144 | System.out.println("Unsubscribing second"); 145 | s2.unsubscribe(); 146 | ``` 147 | 148 | 结果: 149 | 150 | ``` 151 | First: 0 152 | First: 1 153 | First: 2 154 | Second: 2 155 | First: 3 156 | Second: 3 157 | First: 4 158 | Second: 4 159 | Unsubscribing second 160 | First: 5 161 | First: 6 162 | ``` 163 | 164 | ## refCount 165 | 166 | ConnectableObservable.refCount 返回一个特殊的 Observable, 这个 Observable 只要有订阅者就会继续发射数据。 167 | 168 | ```java 169 | Observable cold = Observable.interval(200, TimeUnit.MILLISECONDS).publish().refCount(); 170 | 171 | Subscription s1 = cold.subscribe(i -> System.out.println("First: " + i)); 172 | Thread.sleep(500); 173 | Subscription s2 = cold.subscribe(i -> System.out.println("Second: " + i)); 174 | Thread.sleep(500); 175 | System.out.println("Unsubscribe second"); 176 | s2.unsubscribe(); 177 | Thread.sleep(500); 178 | System.out.println("Unsubscribe first"); 179 | s1.unsubscribe(); 180 | 181 | System.out.println("First connection again"); 182 | Thread.sleep(500); 183 | s1 = cold.subscribe(i -> System.out.println("First: " + i)); 184 | ``` 185 | 186 | 结果: 187 | 188 | ``` 189 | First: 0 190 | First: 1 191 | First: 2 192 | Second: 2 193 | First: 3 194 | Second: 3 195 | Unsubscribe second 196 | First: 4 197 | First: 5 198 | First: 6 199 | Unsubscribe first 200 | First connection again 201 | First: 0 202 | First: 1 203 | First: 2 204 | First: 3 205 | First: 4 206 | ``` 207 | 208 | 如果没有订阅者订阅到 refCount 返回的 Observable,则不会执行数据流的代码。如果所有的订阅者都取消订阅了,则数据流停止。重新订阅再回重新开始数据流。 209 | 210 | ## replay 211 | 212 | ```java 213 | public final ConnectableObservable replay() 214 | ``` 215 | 216 | ![RxJava](images/hot & cold Observable_02.png) 217 | 218 | replay 和 ReplaySubject 类似。当和源 Observable 链接后,开始收集数据。当有 Observer 订阅的时候,就把收集到的数据线发给 Observer。然后和其他 Observer 同时接受数据。 219 | 220 | ```java 221 | ConnectableObservable cold = Observable.interval(200, TimeUnit.MILLISECONDS).replay(); 222 | Subscription s = cold.connect(); 223 | 224 | System.out.println("Subscribe first"); 225 | Subscription s1 = cold.subscribe(i -> System.out.println("First: " + i)); 226 | Thread.sleep(700); 227 | System.out.println("Subscribe second"); 228 | Subscription s2 = cold.subscribe(i -> System.out.println("Second: " + i)); 229 | Thread.sleep(500); 230 | ``` 231 | 232 | 结果: 233 | 234 | ``` 235 | Subscribe first 236 | First: 0 237 | First: 1 238 | First: 2 239 | Subscribe second 240 | Second: 0 241 | Second: 1 242 | Second: 2 243 | First: 3 244 | Second: 3 245 | ``` 246 | 247 | replay 和 publish 一样也返回一个 ConnectableObservable 。所以我们可以在上面使用 refCount 来创建新的 Observable 也可以取消注册。 248 | 249 | replay 有 8个重载函数: 250 | 251 | ```java 252 | ConnectableObservable replay() 253 | Observable replay(Func1,? extends Observable> selector) 254 | Observable replay(Func1,? extends Observable> selector, int bufferSize) 255 | Observable replay(Func1,? extends Observable> selector, int bufferSize, long time, java.util.concurrent.TimeUnit unit) 256 | Observable replay(Func1,? extends Observable> selector, long time, java.util.concurrent.TimeUnit unit) 257 | ConnectableObservable replay(int bufferSize) 258 | ConnectableObservable replay(int bufferSize, long time, java.util.concurrent.TimeUnit unit) 259 | ConnectableObservable replay(long time, java.util.concurrent.TimeUnit unit) 260 | ``` 261 | 262 | 有三个参数 bufferSize、 selector 和 time (以及指定时间单位的 unit) 263 | 264 | - bufferSize 用来指定缓存的最大数量。当新的 Observer 订阅的时候,最多只能收到 bufferSize 个之前缓存的数据。 265 | - time, unit 用来指定一个数据存货的时间,新订阅的 Observer 只能收到时间不超过这个参数的数据。 266 | - selector 和 publish(selector) 用来转换重复的 Observable。 267 | 268 | 下面是一个 bufferSize 的示例: 269 | 270 | ```java 271 | ConnectableObservable source = Observable.interval(1000, TimeUnit.MILLISECONDS) 272 | .take(5) 273 | .replay(2); 274 | 275 | source.connect(); 276 | Thread.sleep(4500); 277 | source.subscribe(System.out::println); 278 | ``` 279 | 280 | 结果: 281 | 282 | ``` 283 | 2 284 | 3 285 | 4 286 | ``` 287 | 288 | ## cache 289 | 290 | cache 操作函数和 replay 类似,但是隐藏了 ConnectableObservable ,并且不用管理 subscription 了。当第一个 Observer 订阅的时候,内部的 ConnectableObservable 订阅到源 Observable。后来的订阅者会收到之前缓存的数据,但是并不会重新订阅到源 Observable 上。 291 | 292 | ```java 293 | public final Observable cache() 294 | public final Observable cache(int capacity) 295 | ``` 296 | 297 | ![RxJava](images/hot & cold Observable_03.png) 298 | 299 | ```java 300 | Observable obs = Observable.interval(100, TimeUnit.MILLISECONDS) 301 | .take(5) 302 | .cache(); 303 | 304 | Thread.sleep(500); 305 | obs.subscribe(i -> System.out.println("First: " + i)); 306 | Thread.sleep(300); 307 | obs.subscribe(i -> System.out.println("Second: " + i)); 308 | ``` 309 | 310 | 结果: 311 | 312 | ``` 313 | First: 0 314 | First: 1 315 | First: 2 316 | Second: 0 317 | Second: 1 318 | Second: 2 319 | First: 3 320 | Second: 3 321 | First: 4 322 | Second: 4 323 | ``` 324 | 325 | 从上面示例中可以看到,只有当有订阅者订阅的时候,源 Observable 才开始执行。当第二个订阅者订阅的时候,会收到之前缓存的数据。 326 | 327 | 需要注意的是,如果所有的订阅者都取消订阅了 内部的 ConnectableObservable 不会取消订阅,这点和 refCount 不一样。只要第一个订阅者订阅了,内部的 ConnectableObservable 就链接到源 Observable上了并且不会取消订阅了。 这点非常重要,因为当我们一单订阅了,就没法取消源 Observable了, 直到源 Observable 结束或者程序内存溢出。 可以指定缓存个数的重载函数也没法解决这个问题,缓存限制只是作为一个优化的提示,并不会限制内部的缓存大小。 328 | 329 | ```java 330 | Observable obs = Observable.interval(100, TimeUnit.MILLISECONDS) 331 | .take(5) 332 | .doOnNext(System.out::println) 333 | .cache() 334 | .doOnSubscribe(() -> System.out.println("Subscribed")) 335 | .doOnUnsubscribe(() -> System.out.println("Unsubscribed")); 336 | 337 | Subscription subscription = obs.subscribe(); 338 | Thread.sleep(150); 339 | subscription.unsubscribe(); 340 | ``` 341 | 342 | 结果: 343 | 344 | ``` 345 | Subscribed 346 | 0 347 | Unsubscribed 348 | 1 349 | 2 350 | 3 351 | 4 352 | ``` 353 | 354 | 上面的示例中,doOnNext 打印源 Observable 发射的每个数据。而 doOnSubscribe 和doOnUnsubscribe 打印缓存后的 Observable 的订阅和取消订阅事件。可以看到当订阅者订阅的时候,数据流开始发射,取消订阅数据流并不会停止。 355 | 356 | ## Multicast 357 | 358 | share 函数是 Observable.publish().refCount() 的别名。可以让你的订阅者分享一个 subscription,只要还有订阅者在,这个 subscription 就继续工作。 359 | -------------------------------------------------------------------------------- /RxJava系列教程/17.RxJava 教程第四部分:并发 之线程调度.md: -------------------------------------------------------------------------------- 1 | ## 17. 并发 之线程调度 2 | 3 | 由于 Rx 目标是用在异步系统上并且 Rx 支持多线程处理,所以很多 Rx 开发者认为默认情况下 Rx 就是多线程的。 其实实际情况不是这样的,Rx 默认是单线程的。 4 | 5 | 除非你明确的指定线程,否则所有 onNext/onError/onCompleted 以及各个操作函数的调用都是在同一个线程中完成的。例如下面的示例: 6 | 7 | ```java 8 | final BehaviorSubject subject = BehaviorSubject.create(); 9 | subject.subscribe(i -> { 10 | System.out.println("Received " + i + " on " + Thread.currentThread().getId()); 11 | }); 12 | 13 | int[] i = {1}; // naughty side-effects for examples only ;) 14 | Runnable r = () -> { 15 | synchronized(i) { 16 | System.out.println("onNext(" + i[0] + ") on " + Thread.currentThread().getId()); 17 | subject.onNext(i[0]++); 18 | } 19 | }; 20 | 21 | r.run(); // Execute on main thread 22 | new Thread(r).start(); 23 | new Thread(r).start(); 24 | ``` 25 | 26 | 结果: 27 | 28 | ``` 29 | onNext(1) on 1 30 | Received 1 on 1 31 | onNext(2) on 11 32 | Received 2 on 11 33 | onNext(3) on 12 34 | Received 3 on 35 | ``` 36 | 37 | 上面在三个线程中分别调用 subject 的onNext 函数。和 Runnable 中的线程是同一个线程。不管用多少个操作函数串联调用,结果都是同一个线程。 38 | 39 | ### subscribeOn 和 observeOn 40 | 41 | subscribeOn 和 observeOn 分别用来控制 subscription 的调用线程和 接受事件通知(Observer 的 onNext/onError/onCompleted 函数)的线程。 42 | 43 | ```java 44 | public final Observable observeOn(Scheduler scheduler) 45 | public final Observable subscribeOn(Scheduler scheduler) 46 | ``` 47 | 48 | 在Rx 中你并不直接和 线程 打交道,而是通过 Scheduler 来处理多线程。 49 | 50 | ### subscribeOn 51 | 52 | subscribeOn 用来指定 Observable.create 中的代码在那个 Scheduler 中执行。即使你没有调用 create 函数,但是内部也有一个 create 实现。例如: 53 | 54 | ```java 55 | System.out.println("Main: " + Thread.currentThread().getId()); 56 | 57 | Observable.create(o -> { 58 | System.out.println("Created on " + Thread.currentThread().getId()); 59 | o.onNext(1); 60 | o.onNext(2); 61 | o.onCompleted(); 62 | }) 63 | //.subscribeOn(Schedulers.newThread()) 64 | .subscribe(i -> { 65 | System.out.println("Received " + i + " on " + Thread.currentThread().getId()); 66 | }); 67 | 68 | System.out.println("Finished main: " + Thread.currentThread().getId()); 69 | ``` 70 | 71 | 结果: 72 | 73 | ``` 74 | Main: 1 75 | Created on 1 76 | Received 1 on 1 77 | Received 2 on 1 78 | Finished main: 1 79 | ``` 80 | 81 | 可以看到上面的代码是在同一个线程中执行,并且是按循序执行的。subscribe 执行完后(包括create 函数里面的 Lambda 表达式的代码)才继续执行后面的代码。 82 | 83 | 如果你把上面的注释掉的代码 .subscribeOn(Schedulers.newThread()) 启用,这结果是这样的: 84 | 85 | ``` 86 | Main: 1 87 | Finished main: 1 88 | Created on 11 89 | Received 1 on 11 90 | Received 2 on 11 91 | ``` 92 | 93 | 这样 create 里面的 Lambda 表达式代码将会在 Schedulers.newThread() 返回的线程中执行。subscribe 不再是阻塞的了。后面的代码可以立即执行,而不用等待 subscribe 返回。 94 | 95 | 有些 Observable 内部会使用它们自己创建的线程。例如 Observable.interval 就是异步的。这种情况下,无需指定新的线程。 96 | 97 | ```java 98 | System.out.println("Main: " + Thread.currentThread().getId()); 99 | 100 | Observable.interval(100, TimeUnit.MILLISECONDS) 101 | .subscribe(i -> { 102 | System.out.println("Received " + i + " on " + Thread.currentThread().getId()); 103 | }); 104 | 105 | System.out.println("Finished main: " + Thread.currentThread().getId()); 106 | ``` 107 | 108 | 结果: 109 | 110 | ``` 111 | Main: 1 112 | Finished main: 1 113 | Received 0 on 11 114 | Received 1 on 11 115 | Received 2 on 11 116 | ``` 117 | 118 | ### observeOn 119 | 120 | observeOn 控制数据流的另外一端。你的 observer 如何收到事件。也就是在那个线程中回调 observer 的 onNext/onError/onCompleted 函数。 121 | 122 | ```java 123 | Observable.create(o -> { 124 | System.out.println("Created on " + Thread.currentThread().getId()); 125 | o.onNext(1); 126 | o.onNext(2); 127 | o.onCompleted(); 128 | }) 129 | .observeOn(Schedulers.newThread()) 130 | .subscribe(i -> 131 | System.out.println("Received " + i + " on " + Thread.currentThread().getId())); 132 | ``` 133 | 134 | 结果: 135 | 136 | ``` 137 | Created on 1 138 | Received 1 on 13 139 | Received 2 on 13 140 | ``` 141 | 142 | observeOn 只影响调用该函数以后的操作函数。你可以认为 observeOn 只是拦截了数据流并且对后续的操作有作用。例如: 143 | 144 | ```java 145 | Observable.create(o -> { 146 | System.out.println("Created on " + Thread.currentThread().getId()); 147 | o.onNext(1); 148 | o.onNext(2); 149 | o.onCompleted(); 150 | }) 151 | .doOnNext(i -> 152 | System.out.println("Before " + i + " on " + Thread.currentThread().getId())) 153 | .observeOn(Schedulers.newThread()) 154 | .doOnNext(i -> 155 | System.out.println("After " + i + " on " + Thread.currentThread().getId())) 156 | .subscribe(); 157 | ``` 158 | 159 | 结果: 160 | 161 | ``` 162 | Created on 1 163 | Before 1 on 1 164 | Before 2 on 1 165 | After 1 on 13 166 | After 2 on 13 167 | ``` 168 | 169 | 可以看到在遇到 observeOn 之前,所有的操作发生在一个线程,之后在另外一个线程。这样可以在 Rx 数据流中不同地方设置不同的线程。 170 | 171 | 如果你知道数据流处理在那些情况需要很长时间,则可以通过这个操作来避免阻塞生产者线程。 比如在 Android 开发过程中的 UI 线程,如果在该线程中读取文件,可能会导致 UI 卡死(ANR)无响应,通过该函数可以指定读取文件在另外一个线程中执行。 172 | 173 | ### unsubscribeOn 174 | 175 | 有些 Observable 会依赖一些资源,当该 Observable 完成后释放这些资源。如果释放资源比较耗时的话,可以通过 unsubscribeOn 来指定 释放资源代码执行的线程。 176 | 177 | ```java 178 | Observable source = Observable.using( 179 | () -> { 180 | System.out.println("Subscribed on " + Thread.currentThread().getId()); 181 | return Arrays.asList(1,2); 182 | }, 183 | (ints) -> { 184 | System.out.println("Producing on " + Thread.currentThread().getId()); 185 | return Observable.from(ints); 186 | }, 187 | (ints) -> { 188 | System.out.println("Unubscribed on " + Thread.currentThread().getId()); 189 | } 190 | ); 191 | 192 | source 193 | .unsubscribeOn(Schedulers.newThread()) 194 | .subscribe(System.out::println); 195 | ``` 196 | 197 | 结果: 198 | 199 | ``` 200 | Subscribed on 1 201 | Producing on 1 202 | 1 203 | 2 204 | Unubscribed on 11 205 | ``` 206 | 207 | ### Schedulers 208 | 209 | observeOn 和 subscribeOn 的参数为一个 Scheduler 对象。Scheduler 是用来协调任务执行的。 RxJava 包含了一些常用的 Scheduler,你也可以自定义 Scheduler。 通过调用 Schedulers 的工厂函数来获取标准的预定义的 Scheduler。 210 | 211 | RxJava 内置的 Scheduler 有: 212 | 213 | - immediate 同步执行 214 | - trampoline 把任务放到当前线程的队列中,等当前任务执行完了,再继续执行队列中的任务 215 | - newThread 对于每个任务创建一个新的线程去执行 216 | - computation 计算线程,用于需要大量 CPU 计算的任务 217 | - io 用于执行 io 操作的任务 218 | - test 用于测试和调试 219 | 220 | 当前 computation 和 io 的实现是类似的,他们两个主要用来确保调用的场景,相当于文档说明,来表明你当前的任务是何种类型的。 221 | 222 | 大部分的 Rx 操作函数内部都使用了schedulers 。并且大部分的 Observable 操作函数也都有一个使用 Scheduler 参数的重载函数。通过重载函数可以指定该操作函数执行的线程。 223 | 224 | ### scheduler 的高级特性 225 | 226 | Rx scheduler 的使用场景并没有限定在 Rx 中,也可以在普通 Java 代码中使用。 227 | 228 | ### 执行一个任务 229 | 230 | Scheduler 有个 createWorker 函数,用来创建一个可以执行的任务(Scheduler.Worker)。然后可以调度该任务: 231 | 232 | ```java 233 | Scheduler.Worker worker = scheduler.createWorker(); 234 | worker.schedule( 235 | () -> System.out.println("Action")); 236 | ``` 237 | 238 | 上面的任务被分配到其指定的线程中了。 239 | 240 | 还可以重复执行任务,或者只执行一次,也可以推迟任务执行: 241 | 242 | ```java 243 | Subscription schedule( 244 | Action0 action, 245 | long delayTime, 246 | java.util.concurrent.TimeUnit unit) 247 | Subscription schedulePeriodically( 248 | Action0 action, 249 | long initialDelay, 250 | long period, 251 | java.util.concurrent.TimeUnit unit) 252 | ``` 253 | 254 | ```java 255 | Scheduler scheduler = Schedulers.newThread(); 256 | long start = System.currentTimeMillis(); 257 | Scheduler.Worker worker = scheduler.createWorker(); 258 | worker.schedule( 259 | () -> System.out.println(System.currentTimeMillis()-start), 260 | 5, TimeUnit.SECONDS); 261 | worker.schedule( 262 | () -> System.out.println(System.currentTimeMillis()-start), 263 | 5, TimeUnit.SECONDS); 264 | ``` 265 | 266 | 结果: 267 | 268 | ``` 269 | 5033 270 | 5035 271 | ``` 272 | 273 | 上面示例中可以看到,推迟执行是从调度开始的时候计算时间的。 274 | 275 | ### 取消任务 276 | 277 | Scheduler.Worker 继承至 Subscription。调用 unsubscribe 函数可以取消队列中的任务: 278 | 279 | ```java 280 | Scheduler scheduler = Schedulers.newThread(); 281 | long start = System.currentTimeMillis(); 282 | Scheduler.Worker worker = scheduler.createWorker(); 283 | worker.schedule( 284 | () -> { 285 | System.out.println(System.currentTimeMillis()-start); 286 | worker.unsubscribe(); 287 | }, 288 | 5, TimeUnit.SECONDS); 289 | worker.schedule( 290 | () -> System.out.println(System.currentTimeMillis()-start), 291 | 5, TimeUnit.SECONDS); 292 | ``` 293 | 294 | 结果: 295 | 296 | ``` 297 | 5032 298 | ``` 299 | 300 | 第一个任务中调用了 unsubscribe,这样第二个任务被取消了。下面一个示例演示任务没有执行完,被取消的情况,会抛出一个 InterruptedException 异常: 301 | 302 | ```java 303 | Scheduler scheduler = Schedulers.newThread(); 304 | long start = System.currentTimeMillis(); 305 | Scheduler.Worker worker = scheduler.createWorker(); 306 | worker.schedule(() -> { 307 | try { 308 | Thread.sleep(2000); 309 | System.out.println("Action completed"); 310 | } catch (InterruptedException e) { 311 | System.out.println("Action interrupted"); 312 | } 313 | }); 314 | Thread.sleep(500); 315 | worker.unsubscribe(); 316 | ``` 317 | 318 | 结果: 319 | 320 | ``` 321 | Action interrupted 322 | ``` 323 | 324 | schedule 返回的是一个 Subscription 对象,可以在该对象上调用取消操作,这样可以只取消这一个任务,而不是取消所有任务。 325 | 326 | ### RxJava 中现有的 scheduler 327 | 328 | #### ImmediateScheduler 329 | 330 | ImmediateScheduler 并没有做任何线程调度。只是同步的执行任务。嵌套调用会导致任务被递归执行: 331 | 332 | ```java 333 | Scheduler scheduler = Schedulers.immediate(); 334 | Scheduler.Worker worker = scheduler.createWorker(); 335 | worker.schedule(() -> { 336 | System.out.println("Start"); 337 | worker.schedule(() -> System.out.println("Inner")); 338 | System.out.println("End"); 339 | }); 340 | ``` 341 | 342 | 结果: 343 | 344 | ``` 345 | Start 346 | Inner 347 | End 348 | ``` 349 | 350 | ### TrampolineScheduler 351 | 352 | TrampolineScheduler 也是同步执行,但是不嵌套任务。而是把后来的任务添加到任务队列中,等前面的任务执行完了 再执行后面的。 353 | 354 | ```java 355 | Scheduler scheduler = Schedulers.trampoline(); 356 | Scheduler.Worker worker = scheduler.createWorker(); 357 | worker.schedule(() -> { 358 | System.out.println("Start"); 359 | worker.schedule(() -> System.out.println("Inner")); 360 | System.out.println("End"); 361 | }); 362 | ``` 363 | 364 | 结果: 365 | 366 | ``` 367 | Start 368 | End 369 | Inner 370 | ``` 371 | 372 | TrampolineScheduler 把任务安排到第一次执行任务的那个线程中执行。这样,第一次调用 schedule 的操作是阻塞的,直到队列执行完。后续的任务,会在这个线程中一个一个的执行,并且后续的调用不会阻塞。 373 | 374 | ### NewThreadScheduler 375 | 376 | NewThreadScheduler 给每个任务创建一个新的线程。 377 | 定义一个打印线程信息的辅助函数: 378 | 379 | ```java 380 | public static void printThread(String message) { 381 | System.out.println(message + " on " + Thread.currentThread().getId()); 382 | } 383 | ``` 384 | 385 | 示例: 386 | 387 | ```java 388 | printThread("Main"); 389 | Scheduler scheduler = Schedulers.newThread(); 390 | Scheduler.Worker worker = scheduler.createWorker(); 391 | worker.schedule(() -> { 392 | printThread("Start"); 393 | worker.schedule(() -> printThread("Inner")); 394 | printThread("End"); 395 | }); 396 | Thread.sleep(500); 397 | worker.schedule(() -> printThread("Again")); 398 | ``` 399 | 400 | 结果: 401 | 402 | ``` 403 | Main on 1 404 | Start on 11 405 | End on 11 406 | Inner on 11 407 | Again on 11 408 | ``` 409 | --------------------------------------------------------------------------------