深入浅出RxJava(四:响应式安卓开发)

在前三节我讲解了Rxjava是如何工作的(简单的介绍了下)。但是作为一个Android开发者,怎么能在实际中能够运用呢?这里给Android开发者一些实际运用。

RxAndroid

RxAndroid是Rxjava对于Android的一个扩展。它包含一些特殊绑定让你的开发更简单。
首先,AndroidSchedulers提供了一个现成的Android线程系统。需要在UI线程上跑一些代码?没问题,只需要使用AndroidSchedulers.mainThread():

retrofitService.getImage(url)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

如果你需要自己处理,你可以利用HandlerThreadScheduler(实际上AndroidSchedulers.mainThread()在内部使用了一个HandlerThreadScheduler)创建一个调度器然后链接上去。
接下来说的是提供更多设施的AndroidObservable,除了可以使用AndroidSchedulers.mainThread()外,还可以使用bindActivity()和bindFragment()来进行观察,当你的Activity或Fragment完成,同样也会发出停止项目信号。

AndroidObservable.bindActivity(this, retrofitService.getImage(url))
    .subscribeOn(Schedulers.io())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap);

我同样喜欢用AndroidObservable.fromBroadcast(),它允许你创建一个类似BroadcastReceiver的Observable。这里有个网络连接发生变化都会通知的例子:

IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
AndroidObservable.fromBroadcast(context, filter)
    .subscribe(intent -> handleConnectivityChange(intent));

最后说一下ViewObservable,它在Views的基础上增加了两个绑定:ViewObservable.clicks()和ViewObservable.text()。ViewObservable.clicks()的作用是获得View每次单击的事件,ViewObservable.text()则是用来获取TextView每次改变内容的事件。

ViewObservable.clicks(mCardNameEditText, false)
    .subscribe(view -> handleClick(view));

Retrofit

有个著名的库支持Rxjava:Retrofit。它是一个在Android上流行的REST 客户端。通常情况下定义一个异步方法添加一个回调是这样的:

@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);

在Rxjava下,你可以用Observable代替:

@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);

现在你可以做很多事情,不光可以获取数据还可以改变数据。
Retrofit 支持Observable导致了将多个REST调用组合在一起变得简单了。例如,我们有一个调用去获取照片,第二个调用去获取原生数据,我们可以将结果一起打包。

Observable.zip(
    service.getUserPhoto(id),
    service.getPhotoMetadata(id),
    (photo, metadata) -> createPhotoWithData(photo, metadata))
    .subscribe(photoWithData -> showPhoto(photoWithData));

我在第二节的时候给你看过类似的代码(使用flatMap()),只是想证明通过RxJava + Retrofit可以让多个REST调用组合成一个变得更简单。

兼容老代码

Retrofit能够返回Observables是很优雅,但是如果你有一个不支持Observables的库呢?或者有一些内部代码你想转换成Observables?
最主要的问题是,要怎样不重写所有方法,让老代码和新代码正常运行?。
大多数情况下可以利用Observable.just()和Observable.from()从老代码中创建一个Observable:

private Object oldMethod() { ... }
public Observable<Object> newMethod() {
    return Observable.just(oldMethod());
}

如果oldMethod()够快则会运行得很好,但是如果它很慢呢?它会阻塞线程因为你在调用oldMethod()之前将它传给了Observable.just()。
为了解决这个问题,提供一个我一直用的技巧—利用defer()将慢的部分包装起来:

private Object slowBlockingMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}

现在,直到订阅了它,被返回的Observable才会调用slowBlockingMethod()。

生命周期

我把最难的留在了最后。你是如何处理Activity的生命周期的?有两个问题会一直出现:

  1. 在configuration改变(比如转屏)之后继续之前的Subscription
    假设你通过Retrofit使用了REST调用,然后想在ListView中显示结果。此时用户的屏幕旋转了,你还想继续同样的请求,但是要怎么做呢?

  2. 保留一个Context的副本会因为Observables导致内存泄露。
    通过创建一个subscription,并且保留下了Context,你和Views发生互动并不困难,但如果Observable不能按时完成,可能会保留很多额外的内存。

不幸的是,上面两个问题没有一种灵丹妙药可以解决,但有一些原则你可以遵循。
第一个问题可以通过RxJava内置的缓存机制来解决,你可以取消订阅/重复订阅同一个Observable。尤其是cache()(或者replay())将会继续从底层请求(甚至你取消订阅)。这意味着当Activity停止后你可以重新发起一个订阅。

Observable<Photo> request = service.getUserPhoto(id).cache();
Subscription sub = request.subscribe(photo -> handleUserPhoto(photo));

// ...When the Activity is being recreated...
sub.unsubscribe();

// ...Once the Activity is recreated...
request.subscribe(photo -> handleUserPhoto(photo));

注意在同一个案例中我们用了同一个request;这样底层调用就只会发生一次。和所有生命周期解决方案一样,request肯定在生命周期外的某个地方被存储了(一个保留片段,一个单例等等)。
第二个问题的解决办法是按照生命周期适当的从你的subscriptions中取消订阅。通常模式是使用CompositeSubscription保留你所有的Subscriptions,然后在onDestroy()或者onDestroyView()时马上取消所有订阅。

private CompositeSubscription mCompositeSubscription
    = new CompositeSubscription();

private void doSomething() {
    mCompositeSubscription.add(
        AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))
        .subscribe(s -> System.out.println(s)));
}

@Override
protected void onDestroy() {
    super.onDestroy();

    mCompositeSubscription.unsubscribe();
}

你可以创建一个带有CompositeSubscription的根Activity/Fragment,然后在里面加东西,之后会自动取消订阅。
警告!一旦你调用CompositeSubscription.unsubscribe(),对象就不可用了,因为它会自动取消后来添加的任何订阅。
如果你打算重新用这个模式,必须创建一个新的CompositeSubscription作为替换。
解决这两个问题都会添加代码;我希望有一天有个天才能够出现并且能够指出在没有这些样板代码的情况下解决问题。

结论?

Rxjava还是一门很新的技术,在Android上进行运用甚至是更新的技术。RxJava还在蓬勃发展,而且没有最好的样板方案。我敢打赌一年以后我的一些建议将会成为无稽之谈。
与此同时,我还发现Rxjava不但让代码更简单而且还带来了更多的乐趣。如果你还是持怀疑态度,我们可以坐下来喝杯啤酒互相探讨下。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值