Rxjava 封装异步回调代码, 新时代来临
来源:互联网 发布:淘宝专业删差评 编辑:程序博客网 时间:2024/05/21 17:16
本文出自 云在千峰
本文永久链接: http://blog.chengyunfeng.com/?p=1019
Yammer 应用开发团队介绍了如何使用 RxJava v1.1.7 版本的 Observable.fromAsync() 函数来把异步回调操作数据发射到 RxJava 数据流中。
现有的 API 通常有同步阻塞 API 和异步非阻塞 API。通过 Observable.fromCallable() 函数可以把同步 API 封装为 Observable,
// wrapping synchronous operation in an RxJava ObservableObservable<Boolean> wipeContents(final SharedPreferences sharedPreferences) { return Observable.fromCallable(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return sharedPreferences.edit().clear().commit(); } });}
上面的示例中,使用 Observable.fromCallable 把针对 SharedPreferences 的操作封装为 Observable。而在 Android 中还有很多异步回调场景,在网络上到处可以看到使用 Observable.create() 来封装异步调用的示例,比如 把现有的库封装为 Observable,在RxJava中封装异步 API,还有这里。但是,使用 Observable.create() 有很多缺点,后面会介绍这些缺点。
下面使用一个示例来看看如何封装异步 API。假设我们用 SensorManager 来追踪设备的加速度。 普通的实现方式是这样的:
public class SensorActivity extends Activity { private static final String LOG_TAG = SensorActivity.class.getName(); private SensorManager sensorManager; private Sensor accelerometer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); } protected void onResume() { super.onResume(); sensorManager.registerListener(sensorListener, accelerometer, SensorManager.SENSOR_DELAY_NORMAL); } protected void onPause() { super.onPause(); sensorManager.unregisterListener(sensorListener); } private final SensorEventListener sensorListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent sensorEvent) { Log.d(LOG_TAG, "onSensorChanged() - sensorEvent.timestamp=" + sensorEvent.timestamp + ", sensorEvent.values=" + Arrays.toString(sensorEvent.values)); } @Override public void onAccuracyChanged(Sensor sensor, int i) { // ignored for this example } };}
下面是一种天真的封装为 RxJava Observable 的方式:
Observable<SensorEvent> naiveObserveSensorChanged(final SensorManager sensorManager, final Sensor sensor, final int samplingPreiodUs) { return Observable.create(new Observable.OnSubscribe<SensorEvent>() { @Override public void call(final Subscriber<? super SensorEvent> subscriber) { SensorEventListener sensorEventListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { subscriber.onNext(event); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // ignored for this example } }; sensorManager.registerListener(sensorEventListener, sensor, samplingPreiodUs); } }); }
由于加速度感应器为 hot Observable,所以上面的封装,没有调用 subscriber.onCompleted() 函数,只要 Subscriber 没有 unsubscribed,则一直有数据产生。
然后使用上面封装的代码:
public class SensorRxActivity extends Activity { private static final String LOG_TAG = SensorRxActivity.class.getName(); private SensorManager sensorManager; private Sensor accelerometer; private Subscription sensorChangedSubscription; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); } protected void onResume() { super.onResume(); sensorChangedSubscription = naiveObserveSensorChanged(sensorManager, accelerometer, SensorManager.SENSOR_DELAY_FASTEST) .subscribe(sensorChangedOnNext, sensorChangedOnError); } @Override protected void onPause() { super.onPause(); sensorChangedSubscription.unsubscribe(); } private final Action1<SensorEvent> sensorChangedOnNext = new Action1<SensorEvent>() { @Override public void call(SensorEvent sensorEvent) { Log.d(LOG_TAG, "sensorChangedOnNext - sensorEvent.timestamp=" + sensorEvent.timestamp + ", sensorEvent.values=" + Arrays.toString(sensorEvent.values)); } }; private final Action1<Throwable> sensorChangedOnError = new Action1<Throwable>() { @Override public void call(Throwable throwable) { Log.e(LOG_TAG, "sensorChangedOnError", throwable); } }; }
虽然上面的代码可以工作。但是距离一个符合 Observable 规范的实现来差很多, Observable 规范有如下几点:
- 如果 Subscriber 取消注册了,则需要取消注册加速度的监听器来避免内存泄露
- 需要捕获可能出现的异常,然后把异常传递到 Observable 的 onError() 函数来避免导致程序崩溃
- 在调用 onNext() 或者 onError() 之前,需要判断 Subscriber 是否还在监听事件,避免发射不必要的数据
- 需要处理 backpressure(数据发射的速度比 Subscriber 处理的速度要快的情况),防止 MissingBackpressureException 异常。
上面的 1~3 步,使用 Observable.create() 函数还是可以正确实现的,虽然麻烦一点:
public Observable<SensorEvent> naiveObserveSensorChanged(final SensorManager sensorManager, final Sensor sensor, final int samplingPreiodUs) { return Observable.create(new Observable.OnSubscribe<SensorEvent>() { @Override public void call(final Subscriber<? super SensorEvent> subscriber) { final SensorEventListener sensorEventListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { // (3) - checking for subscribers before emitting values if (!subscriber.isUnsubscribed()) { subscriber.onNext(event); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // ignored for this example } }; // (1) - unregistering listener when unsubscribed subscriber.add(Subscriptions.create(new Action0() { @Override public void call() { try { sensorManager.unregisterListener(sensorEventListener, sensor); } catch (Exception ex) { // (3) - checking for subscribers before emitting values if (!subscriber.isUnsubscribed()) { // (2) - reporting exceptions via onError() subscriber.onError(ex); } } } })); sensorManager.registerListener(sensorEventListener, sensor, samplingPreiodUs); } }); }
而实现 4 就没有这么简单了,毕竟 RxJava 中的 Backpressure 都可以出一本书来详细介绍其内容了。每次封装异步 API 都需要手工实现 Backpressure 则是非常痛苦的,也不是常人可以正确做到的。
因此,在 RxJava v1.1.7 版本中,聪明的 RxJava 开发人员提供了一个新的函数:Observable.fromAsync() 来处理异步 Api 的封装。
注意:在 1.2.0 版本中,该函数被重命名为 Observable.fromEmitter()
使用 Observable.fromAsync() 【Observable.fromEmitter()】
该函数的文档还很简单,直接看示例代码更加容易理解如何使用该函数:
public Observable<SensorEvent> observeSensorChanged(final SensorManager sensorManager, final Sensor sensor, final int samplingPeriodUs) { return Observable.fromAsync(new Action1<AsyncEmitter<SensorEvent>>() { @Override public void call(final AsyncEmitter<SensorEvent> sensorEventAsyncEmitter) { final SensorEventListener sensorListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent sensorEvent) { sensorEventAsyncEmitter.onNext(sensorEvent); } @Override public void onAccuracyChanged(Sensor originSensor, int i) { // ignored for this example } }; // (1) - unregistering listener when unsubscribed sensorEventAsyncEmitter.setCancellation(new AsyncEmitter.Cancellable() { @Override public void cancel() throws Exception { sensorManager.unregisterListener(sensorListener, sensor); } }); sensorManager.registerListener(sensorListener, sensor, samplingPeriodUs); } // (4) - specifying the backpressure strategy to use }, AsyncEmitter.BackpressureMode.BUFFER); }
注意上面的实现中,并没有处理 第 2、3 步骤,Observable.fromAsync() 自动处理了该问题。 通过 setCancellation() 来实现 第 1 个步骤,而第 4 个步骤只要指定 backpressure 策略就可以了。
处理 Backpressure
Backpressure 是用来描述,生产者生产数据的速度比消费者消费数据的速度快的一种情况。如果没有处理这种情况,则会出现 MissingBackpressureException 。
由于手工实现 Backpressure 是很困难的,如果使用 fromAsync() 函数则我们只需要理解各种 Backpressure 策略即可,不用自己实现。
BackpressureMode 有如下几种策略:
BUFFER(缓存):使用无限个数的内部缓存(RxJava 默认使用 16 个元素的内部缓存),一开始会创建一个 128 个元素的缓冲对象,然后动态的扩展直到 JVM 内存不足。
使用 hot Observable 的时候通常会指定这种策略,比如上面的示例。
LATEST(使用最新的):只发射最新生成的数据,之前的旧的数据被丢弃。类似于使用缓冲个数为 1 的缓存。
cold Observable 通常可以使用这种策略。比如 Andorid 里面的电量变化、或者 最近的位置信息就可以使用这种策略。之前旧的数据已经为无效数据直接丢弃就好。
DROP(直接丢弃):只发射第一个数据,之后的所有数据就丢弃。
通常用来指生成一个数据的 Observable。
ERROR / NONE: 默认的不指定任何策略,会出现 MissingBackpressureException
在封装异步 API 的时候,根据异步 API 的特点,来选择合适的策略是非常重要的
###示例代码
AndroidRxFromAsyncSample 项目演示了本文中的内容。
结论
当需要封装现有 API 为 Observable 的时候,可以考虑:
- 如果为同步 API 则使用 Observable.fromCallable()
- 如果为异步 API 则:
- 避免使用 Observable.create()
- 使用 Observable.fromAsync() 并正确实现如下步骤
- 在合适的地方调用 onNext(), onCompleted(), 和 onError()
- 如果需要清理资源,则使用 setCancellation()
- 选择正确的 BackPressureMode 策略
- Rxjava 封装异步回调代码, 新时代来临
- 新时代的来临
- 网络新时代的来临
- 胡思乱想之:一个新时代的来临
- 新架构存储器FRAM、MRAM时代来临
- 一个新的时代即将来临
- 新时代来临——工业4.0时代有感
- RXJava异步代码
- 信息化普及程度逐年提高 ERP渠道新时代来临
- 信息化普及程度逐年提高 ERP渠道新时代来临
- 信息化普及程度逐年提高 ERP渠道新时代来临
- 谷歌人机大战与新时代的来临
- 教育部又发新文件,职教信息化时代全面来临!
- 蓝牙5新时代来临,或搭载iphone7,蓝牙新时代
- 器普语言代码欣赏-全中文编程时代来临
- 云计算时代来临
- Phone时代已经来临
- HTML5时代的来临
- RtlWcpDllDebugEntrypoint
- Servlet 服务器 HTTP 响应
- getHibernateTemplate().saveOrUpdate(t)可以插入不能更新
- Shell学习小结 - 深入认识变量
- MyBatis总结——加载mappers映射文件的三种方式
- Rxjava 封装异步回调代码, 新时代来临
- Servlet HTTP 状态码
- Java程序员岗位需求分析及人才标准的研究
- Servlet 编写过滤器
- 打印所有的汉字
- 1922: [Sdoi2010]大陆争霸
- 3.3V到4.50V版本下载地址
- Python3.5学习笔记
- 工作第十四周:整理收藏夹、旧文章有感