理解RxJava:(四)Reactive Android
2016-07-26 19:32
477 查看
在前三部分,我在通用层面介绍了RxJava的工作原理。但是作为一个Android开发者,如何在工作中使用它呢?下面是一些给Android开发者的RxJava的具体应用。
首先,其中有
如果你拿到的是
接下来介绍的是
我也喜欢
最后介绍的是
用了RxJava,可以用
现在可以对
Retrofit支持
我在第二部分展示了相似的例子(使用
这在
为了对付这个问题,以下是我一直使用的方法——使用
现在, 直到你订阅
1.Activity的配置发生变化后继续订阅一个
假设你用Retrofit做了一次REST请求,想要把请求结果展示在
2.
这个问题是由于创建了一个以某种方式持有
不幸的是,两个问题都没有很好的解决办法。但是有一些能节约你时间的指导方针。
第一个问题能用一些RxJava内置的缓存机制解决,因此你可以取消订阅/再订阅同一个
注意我们在两种情况下,用的是同一个缓存的请求(
第二问题可以通过根据生命周期正确的取消订阅来实现。通用的方法是用一个
更进一步,你可以创建一个根
注意,只要你调用了
两个问题的解决方法都涉及到添加代码,我希望有一天能出现不需要写这些样板代码就能解决这些问题的天才。
本文翻译自Grokking RxJava, Part 4: Reactive Android,著作权归原作者danlew所有。译文由JohnTsai翻译。转载请注明出处,并保留此段声明。
RxAndroid
RxAndroid是RxJava在Android开发中的拓展。它包含能节省我们大量时间的特殊bindings。首先,其中有
AndroidSchedulers,它能提供专门为Android线程系统提供的schedulers。需要在UI线程运行代码?没问题——只需要使用
AndroidSchedulers.mainThread()方法即可:
retrofitService.getImage(url) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
如果你拿到的是
Handler,可以通过
HandlerThreadScheduler创建一个scheduler绑定在Handler上。
接下来介绍的是
AndroidObservable,它能提供很多在Android生命周期中的特色功能。
bindActivity()和
bindFragment()方法能停止发出items,在
Activity或
Fragment结束的时候。另外会自动为订阅使用
AndroidSchedulers.mainThread()。(因此你不需要在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添加
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作为返回值。
@GET("/user/{id}/photo") Observable<Photo> getUserPhoto(@Path("id") int id);
现在可以对
Observable做你想要的操作了,不仅可以获得数据,也能变换它。
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引用会造成内存泄漏。
这个问题是由于创建了一个以某种方式持有
Context的
subscribtion,特别是你和
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(); }
更进一步,你可以创建一个根
Activity或
Fragment,顺带添加一个
CompositeSubscription,随后可以相应的取消订阅。
注意,只要你调用了
CompositeSubscription.unsubscribe(),该对象(`CompositeSubscription`)就不能用了。因为它会自动取消订阅随后你添加的任何事物。如果你今后想要使用这种方法,你必须创建一个新的
CompositeSubscription作为替代。
两个问题的解决方法都涉及到添加代码,我希望有一天能出现不需要写这些样板代码就能解决这些问题的天才。
本文翻译自Grokking RxJava, Part 4: Reactive Android,著作权归原作者danlew所有。译文由JohnTsai翻译。转载请注明出处,并保留此段声明。
相关文章推荐
- android背景选择器selector的用法
- Android 连接 SQL Server (jtds方式)——上
- Android提醒微技巧,你真的了解Dialog、Toast和Snackbar吗?
- Android Content Providers基础
- Android杂谈(8)关于自定义View的一些实践+遮罩理解
- Android 单元测试记录
- geekband android #5 第五周分享(数据持久化技术)
- Android基础总结(10)——手机多媒体的运用:通知、短信、相机、视频播放
- android stadio 设置编码区的字体
- Android基础总结(7)——异步消息处理
- Android MVP实战
- android—BroadcastReceiver 中的Context理解
- Android Studio 使用技巧(每日一更)
- Android图片加载神器之Fresco-加载图片基础[详细图解Fresco的使用]
- Android显示GIF动态图的三种方式
- Android 字符串拼接
- Android中的基础----在按钮上显示图像的方式
- android 设置statusbar颜色和actionbar颜色一样
- 安卓学习笔记---android RecyclerView一个通用的BaseAdapter
- android stadio 将背景设置为黑色或者白色