Android 周期性接收可观测发射值

Android 周期性接收可观测发射值,android,rx-java,Android,Rx Java,我必须定期轮询一些RESTful端点以刷新android应用程序的数据。我还需要根据连接情况暂停和恢复(如果手机处于脱机状态,甚至不需要尝试)。我当前的解决方案正在运行,但它使用标准Java的ScheduledExecutorService执行定期任务,但我希望保持Rx范式 这是我当前的代码,为了简洁起见,跳过了部分代码 userProfileObservable = Observable.create(new Observable.OnSubscribe<UserProfile>(

我必须定期轮询一些RESTful端点以刷新android应用程序的数据。我还需要根据连接情况暂停和恢复(如果手机处于脱机状态,甚至不需要尝试)。我当前的解决方案正在运行,但它使用标准Java的
ScheduledExecutorService
执行定期任务,但我希望保持Rx范式

这是我当前的代码,为了简洁起见,跳过了部分代码

userProfileObservable = Observable.create(new Observable.OnSubscribe<UserProfile>() {
    @Override
    public void call(final Subscriber<? super UserProfile> subscriber) {
        final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        final Runnable runnable = new Runnable() {
            @Override
            public void run() {
                // making http request here
            }
        };
        final List<ScheduledFuture<?>> futures = new ArrayList<ScheduledFuture<?>>(1);
        networkStatusObservable.subscribe(new Action1<Boolean>() {
            @Override
            public void call(Boolean networkAvailable) {
                if (!networkAvailable) {
                    pause();
                } else {
                    pause();                        
                    futures.add(scheduledExecutorService.scheduleWithFixedDelay(runnable, 0, SECOND_IN_MILLIS * SECONDS_TO_EXPIRE, TimeUnit.MILLISECONDS));
                }
            }

            private void pause() {
                for (ScheduledFuture<?> future : futures) {
                    future.cancel(true);
                }
                futures.clear();
            }
        });

        final Subscription subscription = new Subscription() {
            private boolean isUnsubscribed = false;

            @Override
            public void unsubscribe() {
                scheduledExecutorService.shutdownNow();
                isUnsubscribed = true;
            }

            @Override
            public boolean isUnsubscribed() {
                return isUnsubscribed;
            }
        };
        subscriber.add(subscription);
    }
}).multicast(BehaviorSubject.create()).refCount();
userProfileObservable=Observable.create(new Observable.OnSubscribe()){
@凌驾

public void call(最终用户选项之一是使用Observable.interval并在发出间隔时检查用户状态:

     Observable<Long> interval = Observable.interval(1, TimeUnit.SECONDS);

    //pulling the user data
    Observable<Observable<String>> userObservable = interval.map(new Func1<Long, Observable<String>>() {
        Random random = new Random();
        @Override
        public Observable<String> call(Long tick) {
            //here you are pulling user data; you should do it asynchronously - rx way - because the interval is using Schedulers.computation which is not best suited for doing io operations
            switch(random.nextInt(10)){
                case 0://suppose this is for cases when network in  not available or exception happens
                    return Observable.<String>just(null);
                case 1:
                case 2:
                    return Observable.just("Alice");
                default:
                    return Observable.just("Bob");
            }
        }
    });

    Observable<String> flatUsers = userObservable.flatMap(new Func1<Observable<String>, Observable<? extends String>>() {
        @Override
        public Observable<? extends String> call(Observable<String> stringObservable) {
            return stringObservable;
        }
    });

    //filter valid data
    Observable<String> usersWithoutErrors = flatUsers.filter(new Func1<String, Boolean>() {
        @Override
        public Boolean call(String s) {
            return s != null;
        }
    });

    //publish only changes
    Observable<String> uniqueUsers = usersWithoutErrors.distinctUntilChanged();
最后,您可以创建observable,它使用scheduler定期发出用户状态-请参阅以了解最适合您需要的调度器:

public abstract class ScheduledOnSubscribe<T> implements Observable.OnSubscribe<T>{
    private final Scheduler scheduler;
    private final long initialDelay;
    private final long period;
    private final TimeUnit unit;

    public ScheduledOnSubscribe(Scheduler scheduler, long initialDelay, long period, TimeUnit unit) {
        this.scheduler = scheduler;
        this.initialDelay = initialDelay;
        this.period = period;
        this.unit = unit;
    }

    abstract T next() throws Exception;


    @Override
    public void call(final Subscriber<? super T> subscriber) {
        final Scheduler.Worker worker = scheduler.createWorker();
        subscriber.add(worker);
        worker.schedulePeriodically(new Action0() {
            @Override
            public void call() {
                try {
                    subscriber.onNext(next());
                } catch (Throwable e) {
                    try {
                        subscriber.onError(e);
                    } finally {
                        worker.unsubscribe();
                    }
                }
            }

        }, initialDelay, period, unit);
    }
}

//And here is the sample usage
 Observable<String> usersObservable = Observable.create(new ScheduledOnSubscribe(Schedulers.io(), 1, 1, TimeUnit.SECONDS ){
        Random random = new Random();
        @Override
        String next() throws Exception {
            //if you use Schedulers.io, you can call the remote service synchronously
            switch(random.nextInt(10)){
                case 0:
                    return null;
                case 1:
                case 2:
                    return "Alice";
                default:
                    return "Bob";
            }
        }
    });
公共抽象类ScheduledOnSubscribe实现了Observable.OnSubscribe{
专用最终调度器;
私人最终长时间延迟;
私人最终长期;
私人最终计时单位;
公共ScheduledOnSubscribe(调度器调度器、长初始延迟、长周期、时间单位){
this.scheduler=调度程序;
this.initialDelay=initialDelay;
这个周期=周期;
这个。单位=单位;
}
抽象T next()抛出异常;
@凌驾

public void call(final Subscriber好的,我会发布我自己的解决方案,也许有人会从中受益。我只发布与问题相关的部分,省略HTTP和缓存内容。我是这样做的:

private ConnectableObservable<Long> createNetworkBoundHeartbeatObservable(final Observable<Boolean> networkStatusObservable,
                                                                          final Observable<Boolean> pauseResumeObservable) {

    final Observable<Boolean> pausableHeartbeatObservable = Observable.combineLatest(networkStatusObservable, pauseResumeObservable,
            new Func2<Boolean, Boolean, Boolean>() {
                @Override
                public Boolean call(Boolean networkAvailable, Boolean mustPause) {
                    return mustPause && networkAvailable;
                }
            }
    ).distinctUntilChanged();

    final Observable<Boolean> hasToResumeObservable = pausableHeartbeatObservable.filter(new Func1<Boolean, Boolean>() {
        @Override
        public Boolean call(Boolean networkAvailable) {
            return networkAvailable;
        }
    });
    final Observable<Boolean> hasToStopObservable = pausableHeartbeatObservable.filter(new Func1<Boolean, Boolean>() {
        @Override
        public Boolean call(Boolean networkAvailable) {
            return !networkAvailable;
        }
    });


    return pausableHeartbeatObservable.concatMap(new Func1<Boolean, Observable<Long>>() {
        @Override
        public Observable<Long> call(Boolean shouldResumeRequests) {
            if (shouldResumeRequests) {
                long timeToUpdate;
                final Date oldestModifiedExpiresAt = cache.oldestModifiedExpiresAt();
                timeToUpdate = Math.max(0, oldestModifiedExpiresAt.getTime() - System.currentTimeMillis());
                Log.d(TAG, String.format("Have to restart updates, %d seconds till next update", timeToUpdate / SECOND_IN_MILLIS));
                return Observable
                        .timer(timeToUpdate, SECONDS_TO_EXPIRE * SECOND_IN_MILLIS, TimeUnit.MILLISECONDS)
                        .takeUntil(hasToStopObservable);
            } else {
                Log.d(TAG, "Have to pause updates");
                return Observable.<Long>never().takeUntil(hasToResumeObservable);
            }
        }
    }).multicast(PublishSubject.<Long>create());
}
私有可连接可观察createNetworkBoundHeartbeatObservable(最终可观察网络状态Observable,
最终可观测暂停(可观测){
最终可观测pausableHeartbeatObservable=可观测。组合测试(网络状态可观测,pauseResumeObservable,
新功能2(){
@凌驾
公共布尔调用(布尔网络可用,布尔mustPause){
返回mustPause&networkAvailable;
}
}
).distinctUntilChanged();
最终可观测hasToResumeObservable=pausableHeartbeatObservable.filter(新Func1(){
@凌驾
公共布尔调用(布尔网络可用){
返回可用网络;
}
});
最终可观测hasToStopObservable=pausableHeartbeatObservable.filter(新函数1(){
@凌驾
公共布尔调用(布尔网络可用){
return!网络可用;
}
});
返回pausableHeartbeatObservable.concatMap(新的Func1(){
@凌驾
公共可观察调用(布尔shouldResumeRequests){
如果(应恢复请求){
长时间更新;
最终日期oldestModifiedExpiresAt=cache.oldestModifiedExpiresAt();
timeToUpdate=Math.max(0,oldestModifiedExpiresAt.getTime()-System.currentTimeMillis());
Log.d(TAG,String.format(“必须重新启动更新,距离下一次更新还有%d秒”,timeToUpdate/SECOND_IN_MILLIS));
可观测回波
.timer(timeToUpdate,秒到秒过期*秒,单位为毫秒,时间单位为毫秒)
.takeUntil(hasToStopObservable);
}否则{
Log.d(标记“必须暂停更新”);
return Observable.never().takeUntil(hasToResumeObservable);
}
}
}).multicast(PublishSubject.create());
}
正如您所见,暂停或恢复更新的条件变得有点复杂,添加了一个新的Observable来支持在应用程序进入后台时暂停

然后,解决方案的核心是
concatMap
操作,该操作按顺序发出
可观测值(因此,concatMap,而不是flatMap,请参见此问题:)。它发出
间隔
从不
可观察到的
,这取决于更新是继续还是暂停。然后每个发出的
可观察到的
都被
接受,直到“相反的”
可观察到的
发出新值

返回
ConnectableObservable
,因为创建的
Observable
是热的,所有预定订户都必须在它开始发出某些信息之前订阅它,否则初始事件可能会丢失。我稍后会调用它的
connect


我会接受我的答案或其他基于投票的答案,如果有的话。

在这个GitHub问题上,有几种方法可能会对你有所帮助

这三种实现是:



Observable.create(新建onsubscribeefunc()){
@凌驾

publicsubscriptiononsubscribe(finalobserver使用interval()有一种更简单的方法。我已经测试了这段代码,它可以正常工作。 但首先,您应该将要定期执行的作业封装在Action1的子类中

class Act<T> implements Action1<T> {
     public Service service;
     public String data;
     public void call(T t){
         service.log(data); //the periodic job
     }
}
class Act实现Action1{
公共服务;
公共字符串数据;
公开作废通知(T){
service.log(数据);//定期作业
}
}
(为了简洁起见,我将字段公开,但这是不可取的)。现在,您可以按以下方式安排它:

Act<Long> act=new Act<>();
act.data="dummy data";
act.service=this;
Observable.interval(0l, period, TimeUnit.SECONDS).subscribeOn(Schedulers.from(Executors.newFixedThreadPool(10))).subscribe((Action1<Long>)act);
Act-Act=new-Act();
act.data=“虚拟数据”;
服务=这个;
可观察的.interval(0l,period,TimeUnit.SECONDS).subscribeOn(Schedulers.from(Executors.newFixedThreadPool(10)).subscribe((Action1)act);
与另一个答案中给出的方法不同,这种方法不会在任何地方阻塞线程。这种方法允许我们将变量作为一种可变存储传递到操作中,这在后续调用中可能很方便。此外,通过这种方法,您可以在自己的线程池中订阅调用。

Short answer.RxJava2:

Observable.interval(initialDelay, unitAmount, timeUnit)
            .subscribe(value -> {
                // code for periodic execution
            });
选择initialDelay、unitAmount和
Observable.create(new OnSubscribeFunc<String>() {
        @Override
        public Subscription onSubscribe(final Observer<? super String> o) {
            return Schedulers.newThread().schedule(0L, new Func2<Scheduler, Long, Subscription>() {
                @Override
                public Subscription call(Scheduler inner, Long t2) {
                    o.onNext("data-from-polling");
                    return inner.schedule(t2, this, 1000, TimeUnit.MILLISECONDS);
                }
            });
        }
    }).toBlockingObservable().forEach(new Action1<String>() {
        @Override
        public void call(String v) {
            System.out.println("output: " + v);
        }
    });
class Act<T> implements Action1<T> {
     public Service service;
     public String data;
     public void call(T t){
         service.log(data); //the periodic job
     }
}
Act<Long> act=new Act<>();
act.data="dummy data";
act.service=this;
Observable.interval(0l, period, TimeUnit.SECONDS).subscribeOn(Schedulers.from(Executors.newFixedThreadPool(10))).subscribe((Action1<Long>)act);
Observable.interval(initialDelay, unitAmount, timeUnit)
            .subscribe(value -> {
                // code for periodic execution
            });