├── 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 | 
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 super T> onNext)
12 | Subscription subscribe(Action1 super T> onNext, Action1 onError)
13 | Subscription subscribe(Action1 super T> onNext, Action1 onError, Action0 onComplete)
14 | Subscription subscribe(Observer super T> observer)
15 | Subscription subscribe(Subscriber super T> 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 super List> 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 super Uri> 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 extends Uri> 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 | 
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 extends T> resumeSequence)
40 | public final Observable onErrorResumeNext(
41 | Func1> resumeFunction)
42 | ```
43 |
44 | 
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 | 
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 super Observable extends java.lang.Throwable>,? 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 super Resource,? extends Observable extends T>> observableFactory,
184 | Action1 super Resource> 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 | 
20 |
21 | 
22 |
23 | 
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 super T> 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 | 
14 |
15 | window :
16 |
17 | 
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 | 
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 | 
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 | 
117 |
118 | 信号 Observable 直接也可以相互传递事件。
119 |
120 | 
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 super T,? extends Observable> leftDuration,
251 | Func1 super T2,? extends Observable> rightDuration,
252 | Func2 super T,? super Observable,? 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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
104 |
105 | 先看看上游, 上游发送了三个事件, 分别是1,2,3, 注意它们的颜色.
106 |
107 | 中间flatMap的作用是将圆形的事件转换为一个发送矩形事件和三角形事件的新的上游Observable.
108 |
109 | 还是不能理解? 别急, 再来看看分解动作:
110 |
111 | 
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 | 
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 | 
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 | 
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 | 
150 |
151 | 可以看到, 给上游加了个线程之后, 它就像脱缰的野马一样, 内存又爆掉了.
152 |
153 | 为什么不加线程和加上线程区别这么大呢, 这就涉及了`同步`和`异步`的知识了.
154 |
155 | 当上下游工作在`同一个线程`中时, 这时候是一个`同步`的订阅关系, 也就是说`上游`每发送一个事件`必须`等到`下游`接收处理完了以后才能接着发送下一个事件.
156 |
157 | 当上下游工作在`不同的线程`中时, 这时候是一个`异步`的订阅关系, 这个时候`上游`发送数据`不需要`等待`下游`接收, 为什么呢, 因为两个线程并不能直接进行通信, 因此上游发送的事件并不能直接到下游里去, 这个时候就需要一个田螺姑娘来帮助它们俩, 这个田螺姑娘就是我们刚才说的`水缸`! 上游把事件发送到水缸里去, 下游从水缸里取出事件来处理, 因此, 当上游发事件的速度太快, 下游取事件的速度太慢, 水缸就会迅速装满, 然后溢出来, 最后就OOM了.
158 |
159 | 这两种情况用图片来表示如下:
160 |
161 | 同步:
162 |
163 | 
164 |
165 | 异步:
166 |
167 | 
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 super T> observer)
137 | public final Observable doOnError(Action1 onError)
138 | public final Observable doOnNext(Action1 super T> 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 super T,java.lang.Boolean> 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 | 
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 | 
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 | 
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 | 
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 | 
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 extends T> 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