RxJava2 实战知识梳理(2) - 计算一段时间内数据的平均值

0.899字数 924阅读 4487

RxJava2 实战系列文章

RxJava2 实战知识梳理(1) - 后台执行耗时操作,实时通知 UI 更新
RxJava2 实战知识梳理(2) - 计算一段时间内数据的平均值
RxJava2 实战知识梳理(3) - 优化搜索联想功能
RxJava2 实战知识梳理(4) - 结合 Retrofit 请求新闻资讯
RxJava2 实战知识梳理(5) - 简单及进阶的轮询操作
RxJava2 实战知识梳理(6) - 基于错误类型的重试请求
RxJava2 实战知识梳理(7) - 基于 combineLatest 实现的输入表单验证
RxJava2 实战知识梳理(8) - 使用 publish + merge 优化先加载缓存,再读取网络数据的请求过程
RxJava2 实战知识梳理(9) - 使用 timer/interval/delay 实现任务调度
RxJava2 实战知识梳理(10) - 屏幕旋转导致 Activity 重建时恢复任务
RxJava2 实战知识梳理(11) - 检测网络状态并自动重试请求
RxJava2 实战知识梳理(12) - 实战讲解 publish & replay & share & refCount & autoConnect
RxJava2 实战知识梳理(13) - 如何使得错误发生时不自动停止订阅关系
RxJava2 实战知识梳理(14) - 在 token 过期时,刷新过期 token 并重新发起请求
RxJava2 实战知识梳理(15) - 实现一个简单的 MVP + RxJava + Retrofit 应用


一、前言

今天,我们继续跟着 RxJava-Android-Samples 的脚步,一起看一下RxJava2在实战当中的应用,在这个项目中,第二个的例子的描述如下:


简单地翻译过来:如果在2s内连续点击了一个按钮五次,那么我们只会收到一个“你点击了该按钮五次”的时间,而不是五个"你点击了该按钮"的事件。这个示例的目的是让我们学会如何应用buffer操作符。

二、示例

2.1 应用场景

仔细思考了一下,在平时的项目中,我们似乎不会遇到需要统计一段时间内用户点击了多少次按钮这种需求。

但是,我们有时候会需要计算一段时间内的平均数据,例如统计一段时间内的平均温度,或者统计一段时间内的平均位置。在接触RxJava之前,我们一般会将这段时间内统计到的数据都暂时存起来,等到需要更新的时间点到了之后,再把这些数据结合起来,计算这些数据的平均值。

现在,我们就来看一下,用RxJava2如何去实现这个需求。

2.2 示例代码

这里,我们通过一个Handler循环地发送消息,实现间隔一定时间进行温度的测量,但是在测量之后,我们并不实时地更新界面的温度显示,而是每隔3s统计一次过去这段时间内的平均温度。

public class BufferActivity extends AppCompatActivity {

    private PublishSubject<Double> mPublishSubject;
    private CompositeDisposable mCompositeDisposable;
    private TextView mTv;
    private SourceHandler mSourceHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_buffer);
        mTv = (TextView) findViewById(R.id.tv_buffer);
        mPublishSubject = PublishSubject.create();
        DisposableObserver<List<Double>> disposableObserver = new DisposableObserver<List<Double>>() {
            @Override
            public void onNext(List<Double> o) {
                double result = 0;
                if (o.size() > 0) {
                    for (Double d : o) {
                        result += d;
                    }
                    result = result / o.size();
                }
                Log.d("BufferActivity", "更新平均温度:" + result);
                mTv.setText("过去3秒收到了" + o.size() + "个数据, 平均温度为:" + result);
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onComplete() {

            }
        };
        mPublishSubject.buffer(3000, TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
        mCompositeDisposable = new CompositeDisposable();
        mCompositeDisposable.add(disposableObserver);
        //开始测量温度。
        mSourceHandler = new SourceHandler();
        mSourceHandler.sendEmptyMessage(0);
    }

    public void updateTemperature(double temperature) {
        Log.d("BufferActivity", "温度测量结果:" + temperature);
        mPublishSubject.onNext(temperature);
    }

    private class SourceHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            double temperature = Math.random() * 25 + 5;
            updateTemperature(temperature);
            //循环地发送。
            sendEmptyMessageDelayed(0, 250 + (long) (250 * Math.random()));
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSourceHandler.removeCallbacksAndMessages(null);
        mCompositeDisposable.clear();

    }
}

实际的运行结果为:



控制台输出的信息为:


三、示例解析

3.1 原理

在上面的例子中,我们使用了buffer(int time, Unit timeUnit),其原理图如下所示:


函数中的两个形参分别对应是时间的值和单位,这样,当我们通过下面这句发送事件:

mPublishSubject.onNext(temperature);

事件并不会直接传递到ObserveronNext方法中,而是放在缓冲区中,直到事件到之后,再将所有在这段缓冲事件内放入缓冲区中的值,放在一个List中一起发送到下游。

3.2 Buffer 的其它用法

关于Buffer的其它用法,这篇文章写得很全,我这里就不详细赘述了,大家可以参考:RxJava 的学习之变换操作符 - Buffer

四、参考文献

RxJava 的学习之变换操作符 - Buffer


更多文章,欢迎访问我的 Android 知识梳理系列:

推荐阅读更多精彩内容