Rxjava 封装异步回调代码, 新时代来临

本文出自 云在千峰
本文永久链接: http://blog.chengyunfeng.com/?p=1019

看到评论有些同学问rxjava2 如何处理,给一个自己封装融云发送消息的例子 ,需要留意的是onError用的是 emitter.tryOnError,这样才能把错误往下传

Flowable<Object> flowable = Flowable.create(emitter ->

                        RongIM.getInstance().sendMessage(msg, "房源消息", RongIMHelper.getRongImExtra(), new IRongCallback.ISendMessageCallback() {
                            @Override
                            public void onAttached(Message message) {
                                System.out.println(message);
                            }

                            @Override
                            public void onSuccess(Message message) {
                                System.out.println(message);
                                message.setSentStatus(Message.SentStatus.SENT);
                                emitter.onComplete();
                            }

                            @Override
                            public void onError(Message message, RongIMClient.ErrorCode errorCode) {
//                                emitter.onError(new Exception(String.valueOf(errorCode)));
                                emitter.tryOnError(new Exception(String.valueOf(errorCode)));
                            }
                        })


                , BackpressureStrategy.BUFFER);

Yammer 应用开发团队介绍了如何使用 RxJava v1.1.7 版本的 Observable.fromAsync() 函数来把异步回调操作数据发射到 RxJava 数据流中。
现有的 API 通常有同步阻塞 API 和异步非阻塞 API。通过 Observable.fromCallable() 函数可以把同步 API 封装为 Observable,

// wrapping synchronous operation in an RxJava Observable
Observable<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 规范有如下几点:

  1. 如果 Subscriber 取消注册了,则需要取消注册加速度的监听器来避免内存泄露
  2. 需要捕获可能出现的异常,然后把异常传递到 Observable 的 onError() 函数来避免导致程序崩溃
  3. 在调用 onNext() 或者 onError() 之前,需要判断 Subscriber 是否还在监听事件,避免发射不必要的数据
  4. 需要处理 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 的时候,可以考虑:

  1. 如果为同步 API 则使用 Observable.fromCallable()
  2. 如果为异步 API 则:
  3. 避免使用 Observable.create()
  4. 使用 Observable.fromAsync() 并正确实现如下步骤
- 在合适的地方调用 onNext(), onCompleted(), 和 onError()
- 如果需要清理资源,则使用 setCancellation()
- 选择正确的 BackPressureMode 策略
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值