理解RxJava:(四)Reactive Android

在前三部分,我在通用层面介绍了RxJava的工做原理。可是做为一个Android开发者,如何在工做中使用它呢?下面是一些给Android开发者的RxJava的具体应用。html

RxAndroid

RxAndroid是RxJava在Android开发中的拓展。它包含能节省咱们大量时间的特殊bindings。java

首先,其中有AndroidSchedulers,它能提供专门为Android线程系统提供的schedulers。须要在UI线程运行代码?没问题——只须要使用AndroidSchedulers.mainThread()方法便可:android

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

若是你拿到的是Handler,能够经过HandlerThreadScheduler建立一个scheduler绑定在Handler上。git

接下来介绍的是AndroidObservable,它能提供不少在Android生命周期中的特点功能。bindActivity()bindFragment()方法能中止发出items,在ActivityFragment结束的时候。另外会自动为订阅使用AndroidSchedulers.mainThread()。(所以你不须要在Activity或Fragment无效的时候来改变状态)。github

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添加bindings。若是你想要获得View每次被点击的事件,能够经过ViewObservable.clicks()方法。也能够经过ViewObservable.text()方法来监测TextView的内容发生的任何变化。网络

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

Retrofit

有一个知名而且支持RxJava的库:Retrofit,它是Android开发中的很是出名的REST风格的网络库。一般,咱们定义一个异步方法并添加Callback:异步

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

用了RxJava,能够用Observable代替Callback做为返回值。ide

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

如今能够对Observable作你想要的操做了,不只能够得到数据,也能变换它。this

Retrofit支持Observable,也使得合并多个REST请求变得容易。例如,假设咱们有一个请求得到图片,另外一个请求得到元数据(metadata)。咱们能够把结果组合到一块儿:

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

我在第二部分展现了类似的例子(使用flatMap())。这便证实使用RxJava+Retrofit合并多个REST请求有多简单。

旧的,耗时长的代码

Retrofit能返回Observerables这当然很是好,可是若是你用的其余的库不支持它呢?或者你想要将一些内部代码转换为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才会去调用slowBlockMethod()方法。

生命周期

我把最难的部分留在了最后。你是如何处理RxJava与Activity的生命周期的(配合使用的)?如下两个问题会屡次出现:

  • 1.Activity的配置发生变化后继续订阅一个Subscribtion

    假设你用Retrofit作了一次REST请求,想要把请求结果展现在ListView上。若是用户旋转了屏幕怎么办?若是你想要继续相同的请求,可是如何作呢?

  • 2.Observables持有Context引用会形成内存泄漏。

    这个问题是因为建立了一个以某种方式持有Contextsubscribtion,特别是你和Views交互的时候容易出现。若是Observable没有准时完成,最后可能持有很是多的额外内存。

不幸的是,两个问题都没有很好的解决办法。可是有一些能节约你时间的指导方针。

第一个问题能用一些RxJava内置的缓存机制解决,所以你能够取消订阅/再订阅同一个Observable,而不须要重复它以前的(准备)工做。特别的,cache()(或是replay())方法将继续在(方法)之下的请求(即便你取消订阅)。这意味着Activity从新生成时,你能从新开始新的subscription。

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),那种方式隐含的调用只会发生一次。存放请求的地方你本身决定,可是像全部的生命周期解决方案同样,它必须存放在生命周期以外的地方(一个保存的fragment,单例等等)。

第二问题能够经过根据生命周期正确的取消订阅来实现。通用的方法是用一个CompositeSubscription来持有全部的Subscription,而后在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();
}

更进一步,你能够建立一个根ActivityFragment,顺带添加一个CompositeSubscription,随后能够相应的取消订阅。

注意,只要你调用了CompositeSubscription.unsubscribe(),该对象(`CompositeSubscription`)就不能用了。由于它会自动取消订阅随后你添加的任何事物。若是你从此想要使用这种方法,你必须建立一个新的CompositeSubscription做为替代。

两个问题的解决方法都涉及到添加代码,我但愿有一天能出现不须要写这些样板代码就能解决这些问题的天才。

本文翻译自Grokking RxJava, Part 4: Reactive Android,著做权归原做者danlew全部。译文由JohnTsai翻译。转载请注明出处,并保留此段声明。

相关文章
相关标签/搜索