用RxJava实现倒计时与踩坑

作者: 6db2cd5e23ce | 来源:发表于2016-02-13 01:20 被阅读15241次

    废话不多说直接上代码:

    public class RxCountDown {
    
        public static Observable<Integer> countdown(int time) {
            if (time < 0) time = 0;
    
            final int countTime = time;
            return Observable.interval(0, 1, TimeUnit.SECONDS)
                    .subscribeOn(AndroidSchedulers.mainThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .map(new Func1<Long, Integer>() {
                        @Override
                        public Integer call(Long increaseTime) {
                            return countTime - increaseTime.intValue();
                        }
                    })
                    .take(countTime + 1);
    
        }
    }
    

    代码比较简单,利用interval()定时发送Observable,通过map()0、1、2、3...的计数变为...3、2、1、0倒计时。通过take()>=0Observable

    使用时:

    RxCountDown.countdown(5)
               .doOnSubscribe(new Action0() {
                        @Override
                        public void call() {
                            appendLog("开始计时");
                        }
                    })
               .subscribe(new Subscriber<Integer>() {
                        @Override
                        public void onCompleted() {
                            appendLog("计时完成");
                        }
    
                        @Override
                        public void onError(Throwable e) {
    
                        }
    
                        @Override
                        public void onNext(Integer integer) {
                            appendLog("当前计时:" + integer);
                        }
                    });
    

    这样就实现了一个5秒的倒计时。

    运行结果:

    D/HIDETAG: <--  当前时间:07分58秒628 --- 开始计时  -->
    D/HIDETAG: <--  当前时间:07分59秒646 --- 当前计时:5  -->
    D/HIDETAG: <--  当前时间:07分59秒647 --- 当前计时:4  -->
    D/HIDETAG: <--  当前时间:08分00秒646 --- 当前计时:3  -->
    D/HIDETAG: <--  当前时间:08分01秒646 --- 当前计时:2  -->
    D/HIDETAG: <--  当前时间:08分02秒645 --- 当前计时:1  -->
    D/HIDETAG: <--  当前时间:08分03秒646 --- 当前计时:0  -->
    D/HIDETAG: <--  当前时间:08分03秒650 --- 计时完成  -->
    

    WTF……54怎么同时执行了!反倒是doOnSubscribe()计时5之间有1秒的间隔,很明显有BUG。

    这么几行代码找了1个小时没找到问题在哪里……后来尝试着把.subscribeOn(AndroidSchedulers.mainThread())删除,然后又运行了一下:

    D/HIDETAG: <--  当前时间:14分58秒142 --- 开始计时  -->
    D/HIDETAG: <--  当前时间:14分58秒162 --- 当前计时:5  -->
    D/HIDETAG: <--  当前时间:14分59秒163 --- 当前计时:4  -->
    D/HIDETAG: <--  当前时间:15分00秒150 --- 当前计时:3  -->
    D/HIDETAG: <--  当前时间:15分01秒150 --- 当前计时:2  -->
    D/HIDETAG: <--  当前时间:15分02秒149 --- 当前计时:1  -->
    D/HIDETAG: <--  当前时间:15分03秒150 --- 当前计时:0  -->
    D/HIDETAG: <--  当前时间:15分03秒151 --- 计时完成  -->
    

    居然正确了,倒计时正常工作了……
    不知道是Rx的BUG还是我漏掉了什么知识,为什么指定subscribe的线程为主线程会导致第一次计时不准确?
    希望有知道的不吝赐教。


    2016.2.16更新:找到原因了,不是Rx的锅,是我自己编译环境的问题……

    相关文章

      网友评论

      • Sky简简:var time = 6 //倒计时6s
        Observable.interval(0, 1, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io())
        .take((time + 1).toLong())
        .map { along ->
        return@map time - along
        }
        .observeOn(AndroidSchedulers.mainThread())
        .doOnSubscribe {
        //开始验证码倒计时

        }
        .subscribe({ num ->
        //倒计时过程中

        }, {}, {
        //倒计时完毕

        })
      • 012f8d530b41:为什么要那么复杂?直接这样写不行吗? mDisposable = io.reactivex.Observable.interval(TestCount,1, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Consumer<Long>() {
        @Override
        public void accept(Long aLong) throws Exception {
        TestCount--;
        if(TestCount<=0){
        if(mDisposable.isDisposed()){
        mDisposable.dispose();
        }

        }else{
        Logger.e("-----倒计时"+TestCount);
        }

        }
        });
      • VZzzzzz:既不是编译环境的问题 也不是你的bug,你的订阅和消费都是异步的,上游发送和下游消费不是同步的。
      • DevWang:首先感谢楼主的无私分享,但是我在实际开发中遇到了以下问题:
        public void autoJumpDelay() {
        final int countTime = 10;
        Observable
        .interval(0, 1, TimeUnit.SECONDS)
        .observeOn(AndroidSchedulers.mainThread())
        .map(new Func1<Long, Integer>() {
        @Override
        public Integer call(Long increaseTime) {
        return countTime - increaseTime.intValue();
        }
        })
        .take(countTime)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Subscriber<Integer>() {
        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable e) {
        }

        @Override
        public void onNext(Integer remainTime) {
        System.out.println(remainTime);
        mView.startCountdown(remainTime);
        }
        });
        }

        @Override
        public void startCountdown(final int remainTime) {
        tvSkip.setText(
        TextUtils.concat(
        String.valueOf(remainTime),
        " 跳过"
        )
        );
        }

        这段代码在Android4.2、5.0上都运行正常,但是在4.3.3上不能正常更新ui
        有人能给解决下么?
        DevWang:@Jerome_n 实验了一下 和这个没有关系 这个只是倒计时到0结束 我的写法是到1结束。可能你没这个测试机重现不了这个问题吧
        0fbd3d13a248:@不时不食 .take(countTime)=====>.take(countTime+1)
        DevWang:@不时不食 问题补充下:Android4.3下有问题,倒计时会显示"10 跳过" 然后过两秒直接显示"7 跳过"一直到"1 跳过",中间有2秒没了
      • c545b28ee9fd:我也遇到了 特别是单位是毫秒时直接发onError
      • 84f8179d5b88:请教下楼主,正在倒计时,此时关闭界面,然后就会崩,如何在关闭界面时候清理掉?
        84f8179d5b88:谢谢楼主 后面取消了 才看到消息
        6db2cd5e23ce:@Flow_sunquan 订阅的时候会返回 subscription,在onDestroy()里面取消订阅就行了
      • __Berial___:没遇到楼主发现的问题,我的代码(kotlin): Observable.interval(0, 1, TimeUnit.SECONDS)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribeOn(AndroidSchedulers.mainThread())
        .limit(5)
        .map { 5 - it }
        .doOnSubscribe { println("开始倒计时") }
        .doOnCompleted { println("结束倒计时") }
        .subscribe { println("$it") };
        运行结果:http://ww2.sinaimg.cn/large/ded3864ejw1f110zlf3jbj216206wad8.jpg
        __Berial___:@Hideeee 作为更新强迫症患者,用的都是最新版。你用java这么写试试...
        6db2cd5e23ce:@__Berial___ 奇了怪了。我在模拟器和真机上都是这样,难道是编译环境的问题……
      • Euterpe:subscribe的线程指定为new thread
        流火枫林:@Hideeee 看起来像bug。能把代码发出来看看吗?
        6db2cd5e23ce:@Euterpe 嗯,主要是想模仿AsyncTask,还是不要指望subscribe线程灵活切换了。 刚刚又发现一个坑,不管是subscribe还是observe,只要其中有两次切到主线程,计数就不正确……

      本文标题:用RxJava实现倒计时与踩坑

      本文链接:https://www.haomeiwen.com/subject/wkmukttx.html