分享

Rxjava3文档级教程三: 实战演练

 Naiveee 2020-09-11

商业转载请联系作者获得授权,非商业转载请注明出处。

Rxjava3文档级教程一: 介绍和基本使用

Rxjava3文档级教程二: 操作符全解

Rxjava3文档级教程三: 实战演练

目录

一 结合RxBinding

1.1 按钮防抖功能:

1.2 输入框输入实时网络请求步长控制:

1.3 联合/表单判断

1.4 定时器任务

二 

2.1 嵌套网络请求

三 防泄漏

3.1 Observable.unsubscribe

3.2 disposable.dispose

3.3 CompositeDisposable

3.4 Rxlifecycle

参考文章:


一 结合RxBinding

  RxBinding 的 GitHub 地址

RxBinding 能够把 Android 平台的兼容包内的 UI 控件变为 Observaber 对象. 可以把 UI 控件的事件当作 RxJava 中的数据流来使用。

依赖如下:

Platform bindings:

implementation 'com.jakewharton.rxbinding3:rxbinding:3.1.0'

AndroidX library bindings:

  1. implementation 'com.jakewharton.rxbinding3:rxbinding-core:3.1.0'
  2. implementation 'com.jakewharton.rxbinding3:rxbinding-appcompat:3.1.0'
  3. implementation 'com.jakewharton.rxbinding3:rxbinding-drawerlayout:3.1.0'
  4. implementation 'com.jakewharton.rxbinding3:rxbinding-leanback:3.1.0'
  5. implementation 'com.jakewharton.rxbinding3:rxbinding-recyclerview:3.1.0'
  6. implementation 'com.jakewharton.rxbinding3:rxbinding-slidingpanelayout:3.1.0'
  7. implementation 'com.jakewharton.rxbinding3:rxbinding-swiperefreshlayout:3.1.0'
  8. implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager:3.1.0'
  9. implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager2:3.1.0'

Google 'material' library bindings:

implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.1.0'

RxBinding 的优点:

  • 它是对 Android View 事件的扩展, 它使得开发者可以对 View 事件使用 RxJava 的各种操作。
  • 提供了与 Rxjava 一致的回调, 使得代码简洁明了。
  • 几乎支持所有的常用控件及事件, 还对 Kotlin 支持。
  • 可以应用于整个 App 的所有 UI 事件。

进阶案例:

1.1 按钮防抖功能:

相比之前的定时器或者标志位,来实现的快速点击下的防抖功能,采用RxView可以大大简化代码:

  1. /**
  2. * 按钮点击防抖
  3. */
  4. RxView.clicks(mBt)
  5. .throttleFirst(1, TimeUnit.SECONDS)
  6. .subscribeOn(AndroidSchedulers.mainThread())
  7. .subscribe(v -> Log.i(TAG, "点击按钮"));
  8. /**
  9. * 按钮长按
  10. */
  11. RxView.longClicks(mBt)...

1.2 输入框输入实时网络请求步长控制:

很多App都有这种顶部的搜索框,而现有的实现,一般都会在用户进行输入时,用EditText里自带的addTextChangedListener(new TextWatcher()来监听文本变化,进行实时搜索。

  1. etPriceBegin.addTextChangedListener(new TextWatcher() {
  2. @Override
  3. public void onTextChanged(CharSequence s, int start, int before,
  4. int count) {
  5. //s:变化后的所有字符
  6. Toast.makeText(getContext(), "变化:"+s+";"+start+";"+before+";"+count, Toast.LENGTH_SHORT).show();
  7. Log.i("Seachal:","变化:"+s+";"+start+";"+before+";"+count);
  8. }
  9. @Override
  10. public void beforeTextChanged(CharSequence s, int start, int count,
  11. int after) {
  12. //s:变化前的所有字符; start:字符开始的位置; count:变化前的总字节数;after:变化后的字节数
  13. Toast.makeText(getContext(), "变化前:"+s+";"+start+";"+count+";"+after, Toast.LENGTH_SHORT).show();
  14. Log.i("Seachal:","变化前:"+s+";"+start+";"+count+";"+after);
  15. }
  16. @Override
  17. public void afterTextChanged(Editable s) {
  18. //S:变化后的所有字符;start:字符起始的位置;before: 变化之前的总字节数;count:变化后的字节数
  19. Toast.makeText(getContext(), "变化后:"+s+";", Toast.LENGTH_SHORT).show();
  20. Log.i("Seachal:","变化后:"+s+";");
  21. }
  22. });

但有了RxView后,一切更加方便了起来。下面这个例子里,可以跳过用户的第一次输入不做处理, debounce(1, TimeUnit.SECONDS)又实现了用户在快速删除或者输入时,不会因为每次的实时变化都去进行网络请求或者相关操作,而是在间隔1s后才去进行这些操作。

  1. ed = findViewById(R.id.ed);
  2. RxTextView.textChanges(ed)
  3. .debounce(1, TimeUnit.SECONDS)
  4. //跳过第1次请求 因为初始输入框的空字符状态
  5. .skip(1)
  6. .observeOn(AndroidSchedulers.mainThread())
  7. .subscribe(new Observer<CharSequence>() {
  8. @Override
  9. public void onSubscribe(Disposable d) {
  10. }
  11. @Override
  12. public void onNext(CharSequence charSequence) {
  13. Log.i(TAG,"收到的字符: " + charSequence.toString());
  14. }
  15. @Override
  16. public void onError(Throwable e) {
  17. Log.d(TAG, "onError: " + e.getMessage() );
  18. }
  19. @Override
  20. public void onComplete() {
  21. Log.d(TAG, "onComplete");
  22. }
  23. });

1.3 联合/表单判断

  1. Observable<CharSequence> ObservableName = RxTextView.textChanges(mEtPhone);
  2. Observable<CharSequence> ObservablePassword = RxTextView.textChanges(mEtPassword);
  3. Observable.combineLatest(
  4. ObservableName, ObservablePassword,
  5. (phone, password) -> {
  6. return isPhoneValid(phone.toString()) && isPasswordValid(password.toString());
  7. })
  8. .subscribe(aBoolean -> {
  9. mBtLogin.setEnabled(aBoolean);
  10. // Toast.makeText(getApplicationContext(), "默认的Toast", Toast.LENGTH_SHORT).show();
  11. });
  12. //Lambda写法
  13. Observable.combineLatest(ObservableName, ObservablePassword
  14. , (phone, password) -> isPhoneValid(phone.toString()) && isPasswordValid(password.toString()))
  15. .subscribe(mBtLogin::setEnabled);

1.4 定时器任务

在我们的登录注册页,少不了一个获得验证码的倒计时定时器,一般开发者都会选择继承CountDownTimer类,神说:太麻烦了!于是有了Rx。下面的代码中,mBt在第一次点击后就处于setEnabled(false)的状态,不可点击。

  1. private Observable<Boolean> verifyCodeObservable;
  2. private static int SECOND = 20;
  3. verifyCodeObservable = RxView.clicks(mBt)
  4. .throttleFirst(SECOND, TimeUnit.SECONDS) //防止20秒内连续点击,或者只使用doOnNext部分
  5. .subscribeOn(AndroidSchedulers.mainThread())
  6. .map(o -> false)
  7. .doOnNext(mBt::setEnabled);
  8. verifyCodeObservable.subscribe(s -> {
  9. Observable.interval(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
  10. .take(SECOND)
  11. .subscribe(aLong -> {
  12. RxTextView.text(mBt).accept("剩余" + (SECOND - aLong) + "秒");
  13. }
  14. , Throwable::printStackTrace
  15. , () -> {
  16. RxTextView.text(mBt).accept("获取验证码");
  17. RxView.enabled(mBt).accept(true);
  18. });
  19. });

二 

2.1 嵌套网络请求

  1. // flatMap
  2. MyRxView.clicks(btn3)
  3. .throttleFirst(1000, TimeUnit.MILLISECONDS)
  4. .observeOn(Schedulers.io())
  5. .flatMap(new Function<Object, ObservableSource<ProjectBean>>() {
  6. @Override
  7. public ObservableSource<ProjectBean> apply(Object o) throws Exception {
  8. return wanAndroidApi.getProject();
  9. }
  10. })
  11. .flatMap(new Function<ProjectBean, ObservableSource<ProjectBean.DataBean>>() {
  12. @Override
  13. public ObservableSource<ProjectBean.DataBean> apply(ProjectBean projectBean) throws Exception {
  14. return Observable.fromIterable(projectBean.getData());
  15. }
  16. })
  17. .flatMap(new Function<ProjectBean.DataBean, ObservableSource<ProjectItem>>() {
  18. @Override
  19. public ObservableSource<ProjectItem> apply(ProjectBean.DataBean dataBean) throws Exception {
  20. return wanAndroidApi.getProjectItem(1,dataBean.getId());
  21. }
  22. })
  23. .observeOn(AndroidSchedulers.mainThread())
  24. .subscribe(new Consumer<ProjectItem>() {
  25. @Override
  26. public void accept(ProjectItem projectItem) throws Exception {
  27. Log.i(TAG, "accept: " + projectItem);
  28. }
  29. });
  30. //lambda方式
  31. // RxView.clicks(btn3)
  32. // .throttleFirst(1000, TimeUnit.MILLISECONDS)
  33. // .observeOn(Schedulers.io())
  34. // .flatMap( o -> wanAndroidApi.getProject())
  35. // .flatMap(projectBean -> Observable.fromIterable(projectBean.getData()))
  36. // .flatMap(dataBean -> wanAndroidApi.getProjectItem(1,dataBean.getId()))
  37. // .observeOn(AndroidSchedulers.mainThread())
  38. // .subscribe(projectItem -> Log.i(TAG, "projectItem: " + projectItem));

 

三 防泄漏

3.1 Observable.unsubscribe

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. Observable.unsubscribeOn(AndroidSchedulers.mainThread()); //防止泄露
  5. }

3.2 disposable.dispose

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. if(disposable!=null && !disposable.isDisposed()){
  5. disposable.dispose();
  6. }
  7. }

3.3 CompositeDisposable

如果在请求过程中,UI层destroy了怎么办,不及时取消订阅,可能会造成内存泄漏。因此,CompositeDisposable就上场了,它可以对我们订阅的请求进行统一管理。CompositeDisposable的clear方法内部,实际上就会调用Disposable的dispose方法。
大致三步走:
1、在UI层创建的时候(比如onCreate之类的),实例化CompositeDisposable;
2、把subscribe订阅返回的Disposable对象加入管理器;
3、UI销毁时清空订阅的对象。

  1. private CompositeDisposable mCompositeDisposable;
  2. // when create UI
  3. mCompositeDisposable = new CompositeDisposable();
  4. // when request data
  5. if (mCompositeDisposable != null && !mCompositeDisposable.isDisposed()) {
  6.     mCompositeDisposable.add(disposable);
  7. }
  8. // when destroy UI
  9. if (mCompositeDisposable != null) {
  10.     mCompositeDisposable.clear(); // clear时网络请求会随即cancel
  11.     mCompositeDisposable = null;
  12. }

这样我们就可以管理脱缰的网络请求了,相当于将它与UI的生命周期绑定。只要稍稍将上述模板封装一哈,就比较方便了。比如add操作可以封装一个方法,每次网络请求时add一发就好。
对于MVP架构的项目,CompositeDisposable完全可以封装到Presenter当中。这里就不展开了。

3.4 Rxlifecycle

github官网

我们可以利用Rxlifecycle来解决内存泄漏问题。

依赖:

  1. implementation 'com.trello.rxlifecycle3:rxlifecycle:3.1.0'
  2. // If you want to bind to Android-specific lifecycles
  3. implementation 'com.trello.rxlifecycle3:rxlifecycle-android:3.1.0'
  4. // If you want pre-written Activities and Fragments you can subclass as providers
  5. implementation 'com.trello.rxlifecycle3:rxlifecycle-components:3.1.0'
  6. // If you want pre-written support preference Fragments you can subclass as providers
  7. implementation 'com.trello.rxlifecycle3:rxlifecycle-components-preference:3.1.0'
  8. // If you want to use Android Lifecycle for providers
  9. implementation 'com.trello.rxlifecycle3:rxlifecycle-android-lifecycle:3.1.0'
  10. // If you want to use Kotlin syntax
  11. implementation 'com.trello.rxlifecycle3:rxlifecycle-kotlin:3.1.0'
  12. // If you want to use Kotlin syntax with Android Lifecycle
  13. implementation 'com.trello.rxlifecycle3:rxlifecycle-android-lifecycle-kotlin:3.1.0'
  14. // If you want to use Navi for providers
  15. // DEPRECATED: Use rxlifecycle-android-lifecycle instead. This will be removed in a future release.
  16. implementation 'com.trello.rxlifecycle3:rxlifecycle-navi:3.1.0'

You can bind when the lifecycle emits anything:

  1. myObservable
  2. .compose(RxLifecycle.bind(lifecycle))
  3. .subscribe();

Or you can bind to when a specific lifecyle event occurs:

  1. myObservable
  2. .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
  3. .subscribe();

Alternatively, you can let RxLifecycle determine the appropriate time to end the sequence:

  1. myObservable
  2. .compose(RxLifecycleAndroid.bindActivity(lifecycle))
  3. .subscribe();

例子:

  1. public class RxLifeActivity extends RxAppCompatActivity {
  2. /**
  3. * RxLifecycle使用:在当前activity中继承RxAppCompatActivity
  4. * <p>
  5. * ActivityEvent:手动设置指定在什么时候取消订阅,下列枚举
  6. * CREATE,
  7. * START,
  8. * RESUME,
  9. * PAUSE,
  10. * STOP,
  11. * DESTROY
  12. */
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_rx_life);
  17. Flowable.interval(3, 2, TimeUnit.SECONDS)
  18. .subscribeOn(Schedulers.io())
  19. .compose(this.bindUntilEvent(ActivityEvent.PAUSE)) //手动设置在activity onPause的时候取消订阅
  20. .observeOn(AndroidSchedulers.mainThread())
  21. .subscribe(aLong -> Log.e("liuqiang", "RxLifeActivityOnCreate:" + aLong));
  22. }
  23. @Override
  24. protected void onStart() {
  25. super.onStart();
  26. Flowable.interval(6, 3, TimeUnit.SECONDS)
  27. //bindToLifecycle的自动取消订阅示例,因为是在onStart的时候调用,所以在onStop的时候自动取消订阅
  28. .compose(this.bindToLifecycle()) //设置在默认的生命周期中取消订阅
  29. .subscribeOn(Schedulers.io())
  30. .observeOn(AndroidSchedulers.mainThread())
  31. .subscribe(aLong -> Log.e("liuqiang", "RxLifeActivityOnStart:" + aLong));
  32. }
  33. }

 

 

参考文章:

因为写RxJava系列的文章时进行了很多阅读和参考,因此不分一二三等,将全系列的参考引用统一如下:

RxJava3 Wiki:https://github.com/ReactiveX/RxJava/wiki

RxJava3官方github:https://github.com/ReactiveX/RxJava/wiki/What's-different-in-3.0

ReactiveX文档中文翻译:https://mcxiaoke./rxdocs/content/operators/Creating-Observables.html

single:http:///documentation/single.html

操作符系列讲的很好的文章:https://blog.csdn.net/weixin_42046829/article/details/104836592

基础介绍:https://blog.csdn.net/weixin_42046829/article/details/104833751

RxJava3的一些简介:https:///post/5d1eeffe6fb9a07f0870b4e8

观察者被观察者入门RxJava的一篇好文章:https:///post/580103f20e3dd90057fc3e6d

关于背压一个很好的介绍:https:///post/582d413c8ac24700619cceed

RxLifecycle:https://github.com/trello/RxLifecycle

刚哥平台的挺好很全:RxJava2 只看这一篇文章就够了https:///post/5b17560e6fb9a01e2862246f

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多