Más contenido relacionado La actualidad más candente (20) Similar a Reactive X 响应式编程 (20) Reactive X 响应式编程5. # 观察者模式
源: Observable
View.setOnClickListener(listener)
api.getData(参数, 回调)
观察者: Observer
OnClickListener
回调
源
观察者1 观察者2 观察者3
6. # 迭代器模式
集合: Iteratable
Collection
Set
中间层:
抽象集合
具体迭代实现
迭代器:
Iterator
Cursor
迭代器(抽象迭代器 / 游标Cursor等)
next
hasNext
remove
迭代器具体实现
前序迭代
中序迭代
后续迭代
…
抽象集合
CRUD抽象接口
具体集合数据
数据结构
算法
7. # 函数式
函数是头等公民,可用于传参
Lambda表达式 和 闭包
递归 和 尾递归
空指针Wrapper
无状态,避免可变对象
High-Order函数: 函数输入/函数输出
(fun1 -> fun2) -> fun3
operators 、functionals
会以Kotlin / Groovy语言再分享一次
fun crop
fun rotate
↓
↓
↑
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. # ReactiveX
函数式
避免复杂的状态模式代码,使用最简单的输入/输出两种函数来处理数据流
少即时多
Operators大多数情况下可以将非常复杂的业务逻辑变成几行简单的代码
异步错误处理
异常强大的错误处理机制,远超try/catch
超简单的多线程模型
Rx的Observables和Schedulers可以让开发者不用关心底层复杂的线程处
理,锁等并发相关问题
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. ## Observable
Observable可被Subscriber订阅
类似于 view可以被设置onClick监听器
Observable将数据push给所有subscribers
类似于 报社 每天早上给订阅的家庭 递送 报纸、杂志
集 迭代器 与 被观察者 于一体的数据stream或者sequence
Observable内是一个数据序列,依次push给subscribe到自己的观察者们
14. ## Subscriber
Subscriber/Observer 响应 Observable push过来的消息
Observable.subscribe(subscriber) 来订阅Observable
Subscriber
onNext: - data1 -- data2 -- data3-…
onError: --X (stream异常终止)
onComplete: --| (stream成功结束)
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. ### Show Me The Code!
Observable.just(1, 2, 3).
subscribe(
{ print("-$it-") },
{ println("-Error-X") },
{ println("-Complete-|")}
)
-1--2--3--Complete-|
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
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. @ Amdahl’s Law
异构程度越高,程序
速度越快。
多线程或多核处理器
在1024之前,越多,
程序速度越快
业务95%异构并行度
的时候,相比单线程
处理任务,最高能提
升20倍的速度
23. ## Schedulers
Rx的线程池
Operator中执行的任务,可以指定线程池
Observable也可以通过subscribeOn来指定Observable的任务
在某线程池中执行
Observable可以通过observeOn来指定订阅者/观察者们,在哪
个线程执行onNext, onComplete, onError
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. ### 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. ### Show Me The Code!
Tips: worker集并行任务管理与subscriber能力于一体
val worker = Schedulers.newThread().createWorker()
worker.schedule{
yourWork()
}
// some time later...
worker.unsubscribe()
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. ### 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();
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. ## 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. ## 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. ## 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. ## Operators
各种花式创建Observable
将一个Observable转换成另一个Observable
对Observable的数据进行 过滤 / 改变 / 组装 等等
对Observable进行 联接 / 合并 / 转换 / 筛选 等等
Rx提供了大量非常方便的Operators
你也可以自己自定义Operator
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. ### 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. ### 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
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, …
更详细的分类 戳我
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
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)
45. ### throttle / debounce
tips:
debounce-> 每一段时间内只取一个
Eg-> 250毫秒内只响应一次点击事件(Monkey测试|点击过滤)
clickStream.debounce(250, TimeUnit.MILLISECONDS)
49. ## 更多
Subject: Observable 与 Subscriber 之间的一个代理, 同时扮演
Observable与Observer的角色, Subject观察一个或多个
Observable转换成另一种特定的Observable
Producer: 创建Observable的时候, 数据流的生产者
Single: Observable的stream中只有一个数据的时候, Single可
以把Subscriber缩减掉onCompleted, 只剩下onSuccess,
onError
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. ### takeWhile
Tips: 还有 takeUntil, skipWhile, skipUntil
take, skip, takeFirst, takeLast, skipFirst, skipLast
55. ## 键盘是一个Observable Stream
override fun dispatchKeyEvent(e: KeyEvent): Boolean {
keyboard.onNext(e)
return super.dispatchKeyEvent(e)
}
val keyboard = PublishSubject.create<KeyEvent>()
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. ## 键盘与时间绑定
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. ### 家庭教师
时间 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. ## 向←向→
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()
过滤键盘 ← → 方向按键
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: 如果是格斗游戏, 按键还需要存放到 按键堆栈 中处理连招指令
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. ## 走起来
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
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. ## 触发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. ## 碰撞检测
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
{
// ….碰撞检测
}
74. # 超级玛丽
好了, 大家已经学会 1 + 1 = 2 了
那么, 请大家尝试推导适用于26维
空间超弦定理公式
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
Notas del editor 观察者 被动响应 被观察对象的消息/事件/变更等等过程 消息驱动,类似Event-Driven
异步传递: Asynchronous Message Passing
非阻塞: Non-blocking
对业务透明: Location Transparency, 错误, 异常也是一种消息
back-pressure: 透明性在必要的时候, 消息传递过程中出现异常, 可以不阻塞响应过程 Observable: 集迭代器与被观察者于一体的数据stream (1-P) 无法异步处理的部分
android上就是主线程
由于Android主线程做的事情太多, 所以这个值比较大, 所以流畅度不如iPhone
P/N: 处理器接近无穷多的时候, 速度提升倍数等于 1/(1-P)
golang语言: 以P=100%为目标