Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Reactive X 响应式编程

2,948 views

Published on

响应式编程, 原理, 结构以及使用方法

Published in: Technology
  • Be the first to comment

Reactive X 响应式编程

  1. 1. Reactive Extensions Programming 云应用 / 刘俊
  2. 2. # 关于我  魅族打杂工程师 Android Java Groovy Kotlin Lua C/C++ Gradle Mobile Game QQ音乐8级皇冠 QQ会员快要8级 游戏人生超过10K荣誉点 JweenLau[AT]gmail.com SNS golang python swift
  3. 3. 什么是 Reactive Extensions ?
  4. 4. # ReactiveX ReactiveX 是集 观察者模式, 迭代器 模式和函数式编程所有优点于一体 的编程思想(程序设计模式)
  5. 5. # 观察者模式  源: Observable  View.setOnClickListener(listener)  api.getData(参数, 回调)  观察者: Observer  OnClickListener  回调 源 观察者1 观察者2 观察者3
  6. 6. # 迭代器模式  集合: Iteratable  Collection  Set  中间层:  抽象集合  具体迭代实现  迭代器:  Iterator  Cursor 迭代器(抽象迭代器 / 游标Cursor等) next hasNext remove 迭代器具体实现 前序迭代 中序迭代 后续迭代 … 抽象集合 CRUD抽象接口 具体集合数据 数据结构 算法
  7. 7. # 函数式  函数是头等公民,可用于传参  Lambda表达式 和 闭包  递归 和 尾递归  空指针Wrapper  无状态,避免可变对象  High-Order函数: 函数输入/函数输出  (fun1 -> fun2) -> fun3  operators 、functionals  会以Kotlin / Groovy语言再分享一次 fun crop fun rotate ↓ ↓ ↑
  8. 8. # ReactiveX ReactiveX 是集 观察者模式, 迭代器 模式和函数式编程所有优点于一体 的编程思想 Reactive Programming – 响应式编程
  9. 9. # Reactive Programming V2.0  Message-Driven  Responsive  Resilient  Elastic 参考manifesto React to user React to load data React to failure React to events & messages
  10. 10. # ReactiveX  函数式  避免复杂的状态模式代码,使用最简单的输入/输出两种函数来处理数据流  少即时多  Operators大多数情况下可以将非常复杂的业务逻辑变成几行简单的代码  异步错误处理  异常强大的错误处理机制,远超try/catch  超简单的多线程模型  Rx的Observables和Schedulers可以让开发者不用关心底层复杂的线程处 理,锁等并发相关问题
  11. 11. # Rx的世界  Observable<Data>  Subscriber / Observer  Subscription  Schedulers  Operators Observable • -- data1 -- data2 -- data3 -- | ---------------------------> Subscriber • -- onNext(data1) -- onNext(data2) -- onNext(data3) • -- onComplete -> | • -- onError -> X Subscription • unsubscribe • isUnsubscribed
  12. 12. ## Observable  Observable可被Subscriber订阅  类似于 view可以被设置onClick监听器  Observable将数据push给所有subscribers  类似于 报社 每天早上给订阅的家庭 递送 报纸、杂志  集 迭代器 与 被观察者 于一体的数据stream或者sequence  Observable内是一个数据序列,依次push给subscribe到自己的观察者们
  13. 13. ## Observable Observable Stream A Observable Stream B Operator + Transformation subscriber. onNext subscriber. onError subscriber. onComplete
  14. 14. ## Subscriber  Subscriber/Observer 响应 Observable push过来的消息  Observable.subscribe(subscriber) 来订阅Observable  Subscriber  onNext: - data1 -- data2 -- data3-…  onError: --X (stream异常终止)  onComplete: --| (stream成功结束)
  15. 15. ### Show Me The Code! Observable. just(1, 2, 3). subscribe(new Action1<Integer>() { @Override public void call(Integer integer) { System.out.print("-" + integer + "-"); } }); Observable.just(1, 2, 3).subscribe{ print("-$it-") } -1--2--3-
  16. 16. ### Show Me The Code! Observable.just(1, 2, 3). subscribe( { print("-$it-") }, { println("-Error-X") }, { println("-Complete-|")} ) -1--2--3--Complete-|
  17. 17. ### Show Me The Code! Observable.just(1, 2, 3). doOnNext{ if (it == 3) throw IllegalStateException("3 is not welcome!") }. doOnError{ print("-${it.getMessage()}-") }. subscribe( { print("-$it-") }, { println("-Error-X") }, { println("-Complete-|")} ) -1--2--3 is not welcome!--Error-X
  18. 18. ## Subscription  Observable.subscribe(subscriber)  返回的就是订阅的 Subscription  Subscription  unsubscribe: 取消订阅  isUnsubscribed  CompositeSubscription  add(subscription)  unsubscribe()  remove(subscription)  clear()
  19. 19. ### Show Me The Code! val subscription = Observable.interval(1, TimeUnit.SECONDS). take(10). subscribe{ print("-$it-") } Observable.timer(4, TimeUnit.SECONDS). subscribe{ subscription.unsubscribe() println("-unsubscribe-;") } -0--1--2--3--unsubscribe-;
  20. 20. @ Amdahl’s Law  异构程度越高,程序 速度越快。  多线程或多核处理器 在1024之前,越多, 程序速度越快  业务95%异构并行度 的时候,相比单线程 处理任务,最高能提 升20倍的速度
  21. 21. ReactiveX 如何提升P与N ?
  22. 22. # Rx的世界  Observable<Data>  Subscriber / Observer  Subscription  Schedulers  Operators
  23. 23. ## Schedulers  Rx的线程池  Operator中执行的任务,可以指定线程池  Observable也可以通过subscribeOn来指定Observable的任务 在某线程池中执行  Observable可以通过observeOn来指定订阅者/观察者们,在哪 个线程执行onNext, onComplete, onError
  24. 24. ### Schedulers Scheduler purpose Schedulers.computation( ) CPU运算任务(event-loop, callback处理)专用线程 池,线程数量等于CPU核数 Schedulers.from(executor) 构造自定义的Schedulers Schedulers.immediate( ) 立刻在当前线程执行任务 Schedulers.io( ) 默认是CachedThreadScheduler,异步I/O操作,网络 请求,等阻塞UI的操作,都可以在这里执行,线程池 内线程数量根据需要增长,如果是一般的CPU运算任 务,使用Schedulers.computation( ) Schedulers.newThread( ) 为当前任务单独生成一个线程来执行 Schedulers.trampoline( ) 按任务队列在当前线程执行任务
  25. 25. ### Schedulers operator Scheduler take(time, unit) computation takeLast(time, unit) computation throttle computation timeInterval immediate timeout(timeoutSelector) immediate timeout(timeout, timeUnit) computation timer computation timestamp immediate window computation operator Scheduler buffer(timespan) computation debounce(timeout, unit) computation delay(delay, unit) computation interval computation repeat trampoline replay(time, unit) computation retry trampoline sample(period, unit) computation skip(time, unit) computation skipLast(time, unit) computation
  26. 26. ### Show Me The Code! Tips: worker集并行任务管理与subscriber能力于一体 val worker = Schedulers.newThread().createWorker() worker.schedule{ yourWork() } // some time later... worker.unsubscribe()
  27. 27. ### Show Me The Code! Tips: worker也可以将schedule任务的订阅状态解耦出来 val worker = Schedulers.newThread().createWorker() val mySubscription = worker.schedule{ while(!worker.isUnsubscribed()) { status = yourWork() if(QUIT == status) { worker.unsubscribe()} } } // some time later ... mySubscription.unsubscribe()
  28. 28. ### Show Me The Code! Tips: 递归/循环执行某任务, 直到取消订阅, 游戏循环绘制线程、socket读写线程等 worker.schedule(object : Action0 { override fun call() { yourDrawScreenWork() // recurse until unsubscribed (schedule will do nothing if unsubscribed) worker.schedule(this) } }) // some time later... worker.unsubscribe();
  29. 29. 然而我们 不需要写 Schedulers 代码!
  30. 30. ## Schedulers Scheduler purpose Schedulers.computation( ) CPU运算任务(event-loop, callback处理)专用线程 池,线程数量等于CPU核数 Schedulers.from(executor) 构造自定义的Schedulers Schedulers.immediate( ) 立刻在当前线程执行任务 Schedulers.io( ) 默认是CachedThreadScheduler,异步I/O操作,网络 请求,等阻塞UI的操作,都可以在这里执行,线程池 内线程数量根据需要增长,如果是一般的CPU运算任 务,使用Schedulers.computation( ) Schedulers.newThread( ) 为当前任务单独生成一个线程来执行 Schedulers.trampoline( ) 按任务队列在当前线程执行任务
  31. 31. ## Show Me The Code! Tips: interval take timer等Operator操作默认在 Schedulers.computation线程池中执行 val subscription = Observable.interval(1, TimeUnit.SECONDS). take(10). subscribe{ print("-$it-") } Observable.timer(4, TimeUnit.SECONDS). subscribe{ subscription.unsubscribe() println("-unsubscribe-;") } Schedulers. computation -0--1--2--3--unsubscribe-;
  32. 32. ## Show Me The Code! Tips: -0--1--2--3- 是在主线程中输出的 observeOn->指定Subscriber的监听函数在哪个线程池中执行 subscribeOn->指定当前Observable的数据stream处理操作在哪个 线程池中执行 val subscription = Observable.interval(1, TimeUnit.SECONDS). take(10). subscribeOn(Schedulers.io()). observeOn(AndroidSchedulers.mainThread()). subscribe{ print("-$it-") } Observable.timer(4, TimeUnit.SECONDS).subscribe{ subscription.unsubscribe() println("-unsubscribe-;") } Subscriber. onNext -0--1--2--3--unsubscribe-;
  33. 33. ## Schedulers operator Scheduler take(time, unit) computation takeLast(time, unit) computation throttle computation timeInterval immediate timeout(timeoutSelector) immediate timeout(timeout, timeUnit) computation timer computation timestamp immediate window computation operator Scheduler buffer(timespan) computation debounce(timeout, unit) computation delay(delay, unit) computation interval computation repeat trampoline replay(time, unit) computation retry trampoline sample(period, unit) computation skip(time, unit) computation skipLast(time, unit) computation
  34. 34. ## Operators  各种花式创建Observable  将一个Observable转换成另一个Observable  对Observable的数据进行 过滤 / 改变 / 组装 等等  对Observable进行 联接 / 合并 / 转换 / 筛选 等等  Rx提供了大量非常方便的Operators  你也可以自己自定义Operator
  35. 35. ### 创建Observable  just: 将几个特定数据创建成Observable  from: 将Array/List/Future等数据结构创建成Observable  create: 创建自定义数据的Observable  timer: 延迟一段时间后,传递0:Number的Observable  interval: 每隔一段时间,传递以0开始的序列的Observable  range: 将某段数字范围当作序列创建Observable  repeat: 将某个序列循环重复的Observable  defer: 延迟返回某个Observable(将已有代码变成Observable)
  36. 36. ### Show Me The Code! val subscription = Observable.interval(1, TimeUnit.SECONDS). take(10). subscribe{ print("-$it-") } Observable.timer(4, TimeUnit.SECONDS).subscribe{ subscription.unsubscribe() println("-unsubscribe-;") } -0--1--2--3--unsubscribe-;
  37. 37. ### Show Me The Code! Observable.create {subscriber: Subscriber<in Int> -> subscriber.onNext(1) subscriber.onNext(2) subscriber.onNext(3) subscriber.onCompleted() }.subscribe { println("Got $it") } Observable.range(1, 3).subscribe{println("Got $it")} Got 1 Got 2 Got 3
  38. 38. ### Android Studio tips: Completion快捷键, 逆天的文档提示
  39. 39. ## Operator分类 分类 Operators 连接 join, startWith, merge, concat, zip… 条件判断 amb, skipUntil, skipWhile, takeUntil, takeWhile, defaultIfEmpty… 过滤 filter, first, last, take, skip, elementAt, sample, throttle, timeout, distinct, distinctUntilChange, ofType, ignoreElements, … 转换 map, flatMap, switchMap, scan, groupBy, buffer, window, … 聚合/集合 concat, count, reduce, collect, toList, toMap, toSortedList, … 创建Observable just, from, create, defer, timer, never, … 更详细的分类 戳我
  40. 40. ### map tips: 将Observable中的数据挨个儿转换
  41. 41. ### map Observable.range(97, 26). map(new Func1<Integer, Character>() { @Override public Character call(Integer integer) { return Character.valueOf( (char)(int)integer); } }). subscribe(new Action1<Character>() { @Override public void call(Character character) { System.out.print(character); } }); Observable.range(97, 26).map{it.toChar()}.subscribe{print(it)} 将26个小写英文字母ascii 码转换为字符输出 简直不忍直视 →_→ abcdefghijklmnopqrstuvwxyz
  42. 42. ### merge tips: 将两个Observable合并成一个
  43. 43. ### merge Next: 1 Next: 3 Next: 5 Next: 2 Next: 4 Next: 6 Sequence complete. val odds = Observable.just(1, 3, 5). subscribeOn(Schedulers.computation()) val evens = Observable.just(2, 4, 6) Observable.merge(odds, evens).subscribe( {println("Next: $it")}, {println("Error: ${it.getMessage()}")}, {println("Sequence complete.")} ) tips: odds.mergeWith(evens) 等价于 Observable.merge(odds, evens)
  44. 44. ### 组合操作 Observable. range(0, Int.MAX_VALUE). map{ "value_$it"}. skip(10). take(5). map{"${it}_xfrom" }
  45. 45. ### throttle / debounce tips: debounce-> 每一段时间内只取一个 Eg-> 250毫秒内只响应一次点击事件(Monkey测试|点击过滤) clickStream.debounce(250, TimeUnit.MILLISECONDS)
  46. 46. ### buffer tips: 每隔一段时间内, 最多只取x个
  47. 47. ### buffer tips: 以某个 observable 作为 终止边界选择器, 将每个终 止边界前的数据buffer成一 个list
  48. 48. ### 组合操作 clickStream. buffer{ clickStream.throttleWithTimeout( 250, TimeUnit.MILLISECONDS) }. map({it.size()}). filter({it >= 2}) 获取用户多次点击 行为
  49. 49. ## 更多  Subject: Observable 与 Subscriber 之间的一个代理, 同时扮演 Observable与Observer的角色, Subject观察一个或多个 Observable转换成另一种特定的Observable  Producer: 创建Observable的时候, 数据流的生产者  Single: Observable的stream中只有一个数据的时候, Single可 以把Subscriber缩减掉onCompleted, 只剩下onSuccess, onError
  50. 50. 恭喜你, 到此为止 RX已经可以开动
  51. 51. # 超级玛丽  时间是一个Observable Stream  键盘是一个Observable Stream  绘图事件纷发是一个Observable Stream  任何东西都可以是 Observable Stream
  52. 52. ## 时间是一个Observable Stream val clock = Observable.interval(1, TimeUnit.SECONDS) -0--1--2--3--4--5--... fun countDownClock(limit : Int) = clock.map{limit - it}.takeWhile{it > 0} -limit--(limit-1)--(limit-2)--…--3--2--1--|
  53. 53. ### takeWhile Tips: 还有 takeUntil, skipWhile, skipUntil take, skip, takeFirst, takeLast, skipFirst, skipLast
  54. 54. ## 超级玛丽倒计时太简单了 countDownClock(255).subscribe { timerView.setText("Time left: $it") } Tips: 你应该将界面刷新 bring到 绘制线程 countDownClock(255). observeOn(MyRenderScheduler.draw()) subscribe { timerView.setText("Time left: $it") } 例如: Android使用原生TextView的话 observeOn(AndroidScheduler.mainThread())
  55. 55. ## 键盘是一个Observable Stream override fun dispatchKeyEvent(e: KeyEvent): Boolean { keyboard.onNext(e) return super.dispatchKeyEvent(e) } val keyboard = PublishSubject.create<KeyEvent>()
  56. 56. ### PublishSubject 一个Hot Observable, 不管有没有subscriber订阅, 都在emit数 据, 每个subscriber订阅到PublishSubject的时候, 开始监听这 个时刻起的数据
  57. 57. ## Hot & Cold Observables Hot Observable Cold Observable 区别 创建之后就开始emit数据 当有subscriber订阅到此 Observable的时候, 才开 始emit数据 Observable 绝大多数Observable都是 Hot Observable ConnectableObservable Operators 绝大多数Operator不改变 Observable的特性 publish, refCount share = publish + refCount replay tips: Cold Observable常用于pull型逻辑, 比如Http请求 不过Rx更好的 Single 实现, 例如 Retrofit
  58. 58. ## 键盘与时间绑定 val sample : Observable<in Pair<Int, KeyEvent>> = clock.publish { _clock-> keyboard.map{key-> _clock.map{i-> Pair(i, key)} } }.switchMap{it} 对于每一次键盘按下 与时钟配对 每当有新按键事件, 切换到最新的 Tips: clock.publish 保证每一次匹配时间与键盘, 时间都是一直 延续下去的, 而不是每次都从零开始 (共享subscription)
  59. 59. ### 家庭教师 时间 t, 速度 v, 加速度 a, 位移 s 位移公式: s = v0 + ½ ·a·t² (我们按时间等分拆解) ∑ Δs = ∑ Δs · Δt Δs = Δv · Δt 加速度 Δa = Δv / Δt 那么 vn = vn-1 + Δa Δt Δs = vn-1 Δt+ Δa Δt² 当我们取 Δt = 1 的时候, 每个时间分片内的位移 Δs = Δvn = vn-1 + Δa
  60. 60. ## 向←向→ val arrows = keyboard.filter { intArrayOf(KeyEvent.ACTION_DOWN, KeyEvent.ACTION_UP). contains(it.getAction()) && intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT). contains(it.getKeyCode()) }.distinctUntilChanged() 过滤键盘 ← → 方向按键
  61. 61. ### distinctUtilChanged Tips: 过滤重复的数据
  62. 62. ## Δa:向←减速,向→加速 fun deltaAcceleration(acceleration : Int) = clock.publish { _clock -> arrows.map { key -> _clock.map { _ -> Int if (key.getAction() == KeyEvent.ACTION_UP) (-acceleration / 2) else if (key.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) -acceleration else acceleration } }.switchMap{it} } 模拟惯性减速 向←减速 向→加速 tips: 如果是格斗游戏, 按键还需要存放到 按键堆栈 中处理连招指令
  63. 63. ## Walk, Mario! tips: Mario总是在场景(Scene)中间, 走动的时候, 是场景往左 移动, 地图制作者或设计师管场景叫 关卡
  64. 64. ## 地板 public open class Ground { val tile : Bitmap = getImage("tile") val height = tile.getHeight() val nrTiles = Math.ceil(screenWidth / tile.getWidth()) as Int + 1 fun onDraw(canvas : Canvas) = (0..nrTiles).map { canvas.drawBitmap(tile, tile.getWidth() * it as Float, 0 as Float, paint) } }
  65. 65. ## 走起来 val v0 = 0 val s0 = 0 val acceleration = 3 deltaAcceleration(acceleration). scan(v0) { vi, a-> vi + a }. scan(s0) { si, vi-> si + vi }. subscribe { ground.setTranslateX(it) } Δa vi = vi-1 + Δa·Δt si = si-1 + vi · Δt
  66. 66. ### scan Tips: 前一次运行的输出结果, 保留为下一次运行的输入数据 类似动态语言(例如groovy)中的 inject 函数(high-order函数)
  67. 67. ### scan(R, fun) Tips: 接受一个初始化数据参与scan, 例如玛丽初始化速度v0, 参 与scan单位时间内加速度引起的速度变更, 初始化位移s0参与单 位时间内速度变化引起的位移变化
  68. 68. ## Jump, Mario! v0 v1 = v0 - g v2 = v1 – g = 0 v3 = v2 - g v4 = v3 – g = -v0
  69. 69. ## 跳起来 open class Mario{ // … val junmps : PublishSubject<in Int> = PublishSubject.create() fun init() { junmps.flatMap { v0 -> clock.scan(v0) { vi, _ -> vi - gravity}. takeUntil(junmps) }.subscribe { dy -> setTranslationY(dy as Float) } } } tips: 重力速度公式: vi = vi-1 + gΔt (此处Δt = 1, g = -g)
  70. 70. ## 触发Jump val keyPressed = keyboard.filter{it.getAction() == KeyEvent.ACTION_DOWN} val spaceBar = keyPressed.filter{it.getKeyCode() == KeyEvent.KEYCODE_SPACE} val jumpSpeed = 30 spaceBar.filter { mario.translateY >= mario.homeY }. doOnEach{/* SoundPool.play(R.raw.mario_jump) */ }. subscribe { mario.junmps.onNext(jumpSpeed) } 如果Mario在地上 通知Mario跳起来 跳的音效 空格按下的时候
  71. 71. ## 碰撞检测 Observable<Position> data class Position( val x : Int, val y : Int, val width : Int, val height : Int) Observable.combineLatest( mario.position, coin.position) { marioPos, coinPos -> collides(marioPos, coinPos) }.buffer(2, 1).filter { it.get(0) != it.get(1) }fun collides (posA, posB) : Boolean { // ….碰撞检测 }
  72. 72. ### combineLatest Tips: 组合两个Observable的最后数据根据特定规则生成新的数据 Excel表格 x + y = z 公式, 无论x, y怎么变动, z也跟着变动 对, 就是这样, RxJava中combineLatest最多支持9个Observable
  73. 73. ### buffer Tips: 缓存Observable的数据, 批量push 例如写数据库的时候, 可以buffer一批数据在一定时间限 制内一次写入, 批量操作, 回滚/回顾型逻辑, 都靠buffer
  74. 74. # 超级玛丽  好了, 大家已经学会 1 + 1 = 2 了  那么, 请大家尝试推导适用于26维 空间超弦定理公式
  75. 75. # 引用  http://reactivex.io/  http://kotlinlang.org/docs/reference/  http://www.slideshare.net/andreycheptsov/a-playful- introduction-to-rx  http://www.slideshare.net/allegrotech/rxjava-introduction- context  http://rxmarbles.com/#debounceWithSelector  http://www.mariouniverse.com/fg/supermario-scene-36
  76. 76. Q&A 云应用/刘俊

×