Rx全家桶之RxJava学习总结

版权声明:本文为博主原创文章,欢迎转载,转载请注明出处。 https://blog.csdn.net/jun5753/article/details/88897723

RxJava到底是什么?

先看RxJava在GitHub上的解释:(截止目前为止:最新版本2.2.8)

RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.

翻译过来为:

RxJava是一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库。

个人使用总结:

RxJava 是以观察者模式为核心,可以通过强大的操作符,对事件中的消息进行加工包装,并且可以轻松实现线程调度的一个框架。

关键点:异步

RxJava有什么好处?

关键点:链式调用、简洁

异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android 创造的 AsyncTaskHandler ,其实都是为了让异步代码更加简洁。RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。

基本概念:

RxJava是利用观察者模式来进行一系列操作,所以需要了解一下其他涉及到的基本概念,方便上车。

Observable :被观察者,用来生产发送事件;

Observer:观察者,接收被观察者传来的事件;

Event:包装事件发送中的消息,在事件的传递过程中,可以通过操作符对事件进行各种加工(转换,过滤,组合……);

Subscribe:被观察者和观察者通过订阅产生关系后,才具备事件发送和接收能力;

**Subscriber:**也是一种观察者,在2.0中 它与Observer没什么实质的区别,不同的是 Subscriber要与Flowable(也是一种被观察者)联合使用,该部分内容是2.0新增的。Obsesrver用于订阅Observable,而Subscriber用于订阅Flowable.

RxJava事件回调方法:

OnNext()onCompleted()onError()

  • onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。

  • onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。

  • 在一个正确运行的事件序列中, onCompleted()onError() 有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted()onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。

    RxJava 的观察者模式大致如下图:

AwGxxJ.png

RxJava中的各种操作符号

RxJava的强大性就来自于它所定义的操作符。主要分类:

RxJava 的操作符 说明 例如
创建操作 用于创建Observable的操作符 create、defer、from、just、start、repeat、 range
变换操作 用于对Observable发射的数据进行变换 buffer、window、map、flatMap、groupBy、scan
过滤操作 用于从Observable发射的数据中进行选择 debounce、distinct、filter、sample、skip、take
组合操作 用于将多个Observable组合成一个单一的Observable and、startwith、join、merge、switch、zip
异常处理 用于从错误通知中恢复 catch、retry
辅助操作 用于处理Observable的操作符 delay、do、observeOn、subscribeOn、subscribe
条件和布尔操作 all、amb、contains、skipUntil、takeUntil
算法和聚合操作 average、concat、count、max、min、sum、reduce
异步操作 start、toAsync、startFuture、FromAction、FromCallable、runAsync
连接操作 connect、publish、refcount、replay
转换操作 toFuture、toList、toIterable、toMap、toMultiMap
阻塞操作 forEach、first、last、mostRecent、next、single
字符串操作 byLine、decode、encode、from、join、split、stringConcat

常用分类:

常用操作符 说明
interval 延时几秒,每隔几秒开始执行
take 超过多少秒停止执行
map 类型转换
observeOn 在主线程运行
doOnSubscribe 在执行的过程中
subscribe 订阅

RxJava 线程调度器

调度器 Scheduler 用于控制操作符和被观察者事件所执行的线程,不同的调度器对应不同的线程。RxJava提供了5种调度器:

RxJava 线程调度器 说明
Schedulers.immediate() 默认线程,允许立即在当前线程执行所指定的工作。
Schedulers.newThread() 新建线程,总是启用新线程,并在新线程执行操作。
Schedulers.io() 适用于I/O操作,根据需要增长或缩减来自适应的线程池。多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。
Schedulers.computation() 适用于计算工作(CPU 密集型计算),即不会被 I/O 等操作限制性能的操作。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
Schedulers.trampoline() 当我们想在当前线程执行一个任务时,并不是立即,我们可以用.trampoline()将它入队。这个调度器将会处理它的队列并且按序运行队列中每一个任务。
AndroidSchedulers.mainThread() RxAndroid 提供的,它指定的操作将在 Android 主线程运行。

模拟发送验证码倒计时:

public void onCodeClick() {
    final long count = 60; // 设置60秒
    Observable.interval(0, 1, TimeUnit.SECONDS)
            .take(count + 1)
            .map(new Function<Long, Long>() {
                @Override
                public Long apply(@NonNull Long aLong) throws Exception {
                    return count - aLong; // 由于是倒计时,需要将倒计时的数字反过来
                }
            })
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSubscribe(new Consumer<Disposable>() {
                @Override
                public void accept(@NonNull Disposable disposable) throws Exception {
                    button.setEnabled(false);
                    button.setTextColor(Color.GRAY);
                }
            })
            .subscribe(new Observer<Long>() {
                @Override
                public void onSubscribe(Disposable d) {
                }
                @Override
                public void onNext(Long aLong) {
                    button.setText(aLong + "秒后重发");
                }
                @Override
                public void onError(Throwable e) {
                }
                @Override
                public void onComplete() {
                    button.setEnabled(true);
                    button.setTextColor(Color.RED);
                    button.setText("发送验证码");
                }
            });
}

再举一例,项目中使用实例:轮询,每隔1分钟 更新停车费和停车时长:

//定时刷新时间和费用
private var intervalRefreshDisposable: Disposable? = null

//启动轮询
  override fun onResume() {
        super.onResume()
        intervalRefreshDisposable = refreshTimeAndFeeInterval()
    }


private fun refreshTimeAndFeeInterval(): Disposable? {
        val disposable = Observable.interval(0, 1, TimeUnit.MINUTES)
            .applyScheduler()
            .subscribe({
                //do something 如刷新界面
       
            }, {
                LogUtil.printStackTrace(it)
            })
    //绑定生命周期
     disposable.attachToLifecycle(viewLifecycleOwner)
     return disposable
    }

//界面不可见时 解除订阅
    override fun OnDestory() {
        super.OnDestory()
        intervalRefreshDisposable?.safeDisposable()
    }

//用到的RxJava扩展工具类
/**
 * 安全解除订阅
 */
fun Disposable.safeDisposable() {
    if (!this.isDisposed) {
        this.dispose()
    }
}
/**
 * Observable 应用线程控制
 * @param subscribeScheduler 订阅时的线程,默认是 IO 线程
 * @param observeScheduler 观察时的线程,默认是主线程
 */
fun <T> Observable<T>.applyScheduler(
        subscribeScheduler: Scheduler = Schedulers.io(),
        observeScheduler: Scheduler = AndroidSchedulers.mainThread()
): Observable<T> {
    return this.subscribeOn(subscribeScheduler)
            .observeOn(observeScheduler)
            .unsubscribeOn(subscribeScheduler)
}

RxJava 系列框架

RxJava 框架 说明 开源地址
RxAndroid 针对 Android 平台的扩展框架,方便 RxJava 用于 Android 开发,目前 RxAndroid 主要的功能是对 Android 主线程的调度 AndroidSchedulers.mainThread()。 https://github.com/ReactiveX/RxAndroid
DataBinding DataBinding 是基于MVVM思想实现数据和UI绑定的的框架,支持双向绑定。 DataBinding 是一个support库,最低支持到Android 2.1
RxBinding 基于 RxJava 的用于绑定 Android UI 控件的框架,它可以异步获取并处理控件的各类事件(例如点击事件、文字变化、选中状态) https://github.com/JakeWharton/RxBinding
Retrofit 网络请求框架,Retrofit 结合 RxJava 简化请求流程。 https://github.com/square/retrofit
RxPermissions 动态权限管理框架,动态权限内容可参考Android 6.0+ 运行时权限处理。 https://github.com/tbruyelle/RxPermissions
RxLifecycle 生命周期绑定,提供了基于 Activity 和 Fragment 生命周期事件的自动完成队列,用于避免不完整回调导致的内存泄漏。 https://github.com/trello/RxLifecycle
RxBus 是一种基于RxJava实现事件总线的一种思想。可以替代EventBus/Otto,因为他们都依赖于观察者模式。 https://github.com/AndroidKnife/RxBus

RxJava的应用场景

1.与Retrofit的结合

Retrofit 除了提供了传统的 Callback 形式的 API,还有 RxJava 版本的 Observable 形式 API。下面我用对比的方式来介绍 Retrofit 的 RxJava 版 API 和传统版本的区别。

Callback 形式和 Observable 形式长得不太一样,但本质都差不多。RXJava在可读上要好得多,比如在网络嵌套上可以flatMap()实现链式调用,代码上更优雅。

测试接口:

https://api.douban.com/v2/movie/top250?start=0&count=10

首先我们要根据返回的结果封装一个Entity,暂命名为MovieEntity,代码就不贴了。

接下来我们要创建一个接口取名为MovieService,代码如下:

(1)只用Retrofit

public interface MovieService {
    @GET("top250")
    Call<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
}

在界面中调用 获取数据的接口getMovie方法:

//进行网络请求
private void getMovie(){
    String baseUrl = "https://api.douban.com/v2/movie/";

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    MovieService movieService = retrofit.create(MovieService.class);
    Call<MovieEntity> call = movieService.getTopMovie(0, 10);
    call.enqueue(new Callback<MovieEntity>() {
        @Override
        public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) {
            resultTV.setText(response.body().toString());
        }

        @Override
        public void onFailure(Call<MovieEntity> call, Throwable t) {
            resultTV.setText(t.getMessage());
        }
    });
}

(2)引入RxJava后

现在定义接口时不再是Call,而是Observable

public interface MovieService {
    @GET("top250")
    Observable<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
}

getMovie方法改为:

//进行网络请求
private void getMovie(){
    String baseUrl = "https://api.douban.com/v2/movie/";

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();

    MovieService movieService = retrofit.create(MovieService.class);

    movieService.getTopMovie(0, 10)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<MovieEntity>() {
                @Override
                public void onCompleted() {
                    Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onError(Throwable e) {
                    resultTV.setText(e.getMessage());
                }

                @Override
                public void onNext(MovieEntity movieEntity) {
                    resultTV.setText(movieEntity.toString());
                }
            });
}

以上实现了RxJava如何与Retrofit结合,当然还不完善,还有很多(如以下4点)需要封装,此处不再详细介绍,更多请查看RxJava 与 Retrofit 结合的最佳实践

  1. 相同格式的Http请求数据该如何封装
  2. 相同格式的Http请求数据统一进行预处理
  3. 如何取消一个Http请求 – 观察者之间的对决,Oberver VS Subscriber
  4. 一个需要ProgressDialog的Subscriber该有的样子
2.RxBinding

RxBinding 是 Jake Wharton 的一个开源库,它提供了一套在 Android 平台上的基于 RxJava 的 Binding API。所谓 Binding,就是类似设置 OnClickListener 、设置 TextWatcher 这样的注册绑定对象的 API。

(1)Button 防抖处理:

throttleFirst() ,用于去抖动,也就是消除手抖导致的快速连环点击:

RxView.clickEvents(button)
    .throttleFirst(500, TimeUnit.MILLISECONDS) //设置防抖间隔为 500ms
    .subscribe(clickAction);

在项目中的实际运用,项目中为防止重复点击,在凡是有点击事件时,都用View的扩展类onClick来实现(Kotlin)

// 点击时间间隔
private const val TIME_INTERVAL = 1000L

/**
 * View点击事件,限定时间之内只取第一个点击事件,防止重复点击
 */
fun View.onClick(time: Long = TIME_INTERVAL, click: (View) -> Unit) {
    this.clicks()
        .throttleFirst(time, TimeUnit.MILLISECONDS)
        .subscribe({
            click(this)
        }, {
            LogUtil.printStackTrace(it)
        })
}

//调用
ibBack.onClick {
    //do something
}

(2)按钮的长按时间监听

//监听长按时间
 RxView.longClicks( button)
      .subscribe(new Action1<Void>() {
          @Override
         public void call(Void aVoid) {
         Toast.makeText(MainActivity.this, "long click  !!", Toast.LENGTH_SHORT).show();
         }
     }) ;

//监听某按钮多次点击,比如双击
Observable<Void> observable = RxView.clicks(doubleClickBtn).share();
        observable.buffer(observable.debounce(400, TimeUnit.MILLISECONDS))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<Void>>() {
                    @Override
                    public void call(List<Void> voids) {
                        if (voids.size() >= 2) {
                            //double click detected
                            showToast(" 400毫秒内进行了" + voids.size() + "点次击");
                        }
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {

Android是不能多次监听同一个点击事件。但利用RxJava的操作符,例如publish, sharereplay可以实现。而RxBinding恰好支持对点击事件的多次监听。

(3)用户登录界面,勾选同意隐私协议,登录按钮就变高亮

//原始方式
        usernameEt.addTextChangedListener(....);
        
        pwdEt.addTextChangedListener(....;
        
        protocolCb.setOnCheckedChangeListener(....);

//现在
Observable<CharSequence> usernameOb = RxTextView.textChanges(usernameEt);
        Observable<CharSequence> pwdOb = RxTextView.textChanges(pwdEt);
        Observable<Boolean> protocolOb = RxCompoundButton.checkedChanges(protocolCb);
        Observable.combineLatest(usernameOb, pwdOb, protocolOb, new Func3<CharSequence, CharSequence, Boolean, Boolean>() {
            @Override
            public Boolean call(CharSequence username, CharSequence pwd, Boolean isChecked) {
                return !TextUtils.isEmpty(username) && !TextUtils.isEmpty(pwd) && isChecked;
            }
        }).subscribe(new Action1<Boolean>() {
            @Override
            public void call(Boolean aBoolean) {
                RxView.enabled(highLightkBtn).call(aBoolean);
            }
        });

操作符combineLatest作用就是当多个Observables中的任何一个发射了一个数据时,通过一定的方法去组合多个Observables最新数据,然后发射最终结果。

在本例中两个输入框只要内容发生变化,就会发送Observable然后我们在Fun2中利用我们的验证方法去判断输入框中最新的内容,最终返回是否可点击的结果。

(4)输入文本框监听

//原始方式
final EditText name = (EditText) v.findViewById(R.id.name);
name.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // do some work here with the updated text
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

//现在
final EditText name = (EditText) v.findViewById(R.id.name);
Subscription editTextSub =RxTextView.textChanges(name)
            .subscribe(new Action1<String>() {
                @Override
                public void call(String value) {
                    // do some work with the updated text
                }
            });
// Make sure to unsubscribe the subscription

(5)搜索 :debounce()在一定的时间内没有操作就会发送事件。

RxTextView.textChanges(searchEt)
                  .debounce(500, TimeUnit.MILLISECONDS)
                  .observeOn(AndroidSchedulers.mainThread())
                  .map(new Func1<CharSequence, String>() {
                      @Override
                      public String call(CharSequence charSequence) {
                          String searchKey = charSequence.toString();
                          if (TextUtils.isEmpty(searchKey)) {
                              showToast("搜索关键字不能为null");
                          }
                          else {
                              showToast("0.5秒内没有操作,关键字:" + searchKey);
                          }
                          return searchKey;
                      }
                  })
                  .observeOn(Schedulers.io())
                  .map(new Func1<String, List<String>>() {
                      @Override
                      public List<String> call(String searchKey) {
                          return search(searchKey);
                      }
                  })
                  .observeOn(AndroidSchedulers.mainThread())
                  .subscribe(new Action1<List<String>>() {
                      @Override
                      public void call(List<String> strings) {

                      }
                  });
3.各种异步操作

前面举的 RetrofitRxBinding 的例子,是两个可以提供现成的 Observable 的库。而如果你有某些异步操作无法用这些库来自动生成 Observable,也完全可以自己写。例如数据库的读写、大图片的载入、文件压缩/解压等各种需要放在后台工作的耗时操作,都可以用 RxJava 来实现。

4.RxBus

RxBus 名字看起来像一个库,但它并不是一个库,而是一种模式,它的思想是使用 RxJava 来实现了 EventBus ,而让你不再需要使用 Otto 或者 GreenRobot 的 EventBus

总结

此文作为RxJava中的入门介绍,在学习的总结的同时,对参考资料中的知识加以提炼,并结合自身项目中运用到的实例进行了总结。如有不足之处,欢迎指出~

参考资料:

1.Rxjava

2.RxKotlin

3.给 Android 开发者的 RxJava 详解(推荐)

4.RxBus

5.Android 响应式编程 RxJava2 完全解析

6.RxJava 与 Retrofit 结合的最佳实践

7.这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)

8.神兵利器–RxBinding,用的就是你

猜你喜欢

转载自blog.csdn.net/jun5753/article/details/88897723