Search code examples
androidrxjsretrofit2

How to create a long polling in Android by using Retrofit2 and RxJs?


I was trying to follow this stackoverflow answer for implementing long polling to an API.

The issue is that by trying to use Observable.interval() or Observable.timer() the call is made only once and the first call is casted only after the delay, while, i would that the first call is made immediatly and then other are dont after some seconds.

Here is my method where i subscribe to the Observable

private void getTables(int roomNumber) {
    Observable.interval(2000, TimeUnit.MILLISECONDS, Schedulers.io())
                    .flatMap((Function<Long, ObservableSource<List<Tables>>>) aLong -> RetrofitClient.getInstance().getApi()
                            .getTables(Utils.getTablesURL(ip), String.valueOf(roomNumber))
                            .doOnError(err -> Log.e("Polling", "Error retriving tables: " + err))
                            .onErrorResumeNext(throwable -> Observable.empty()))
            .take(1)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<List<Tables>>() {
                @Override
                public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {

                }

                @Override
                public void onNext(@io.reactivex.rxjava3.annotations.NonNull List<Tables> tablesList) {
                    if (tablesList != null && !tablesList.isEmpty()) {
                        adapterTables = new AdapterTables(layoutManager, tablesList);
                        adapterTables.setListener(position -> getTableInfo(position, tablesList));
                        recyclerView.setAdapter(adapterTables);

                        recyclerView.setVisibility(View.VISIBLE);
                        containerNoData.setVisibility(View.GONE);
                    }
                }

                @Override
                public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
                    if (adapterTables.getItemCount() == 0) {
                        containerNoData.setVisibility(View.VISIBLE);
                        recyclerView.setVisibility(View.GONE);
                    }
                }

                @Override
                public void onComplete() {

                }
            });
}

That method is called after another API call is succeded.

But what's happens in the app is that i have no data until 2000ms are passed, then it seems like the observable is just destroyed and it's called only once.

I've tried to use even .timer instead of interval.


Solution

  • I had to use interval by setting both initialDelay and period values, then i had to remove .take(1).

    So i changed my code from:

    Observable.interval(2000, TimeUnit.MILLISECONDS, Schedulers.io())
                    .flatMap((Function<Long, ObservableSource<List<Tables>>>) aLong -> RetrofitClient.getInstance().getApi()
                            .getTables(Utils.getTablesURL(ip), String.valueOf(roomNumber))
                            .doOnError(err -> Log.e("Polling", "Error retriving tables: " + err))
                            .onErrorResumeNext(throwable -> Observable.empty()))
            .take(1)
            ...
    

    To

    Observable.interval(0, 2000, TimeUnit.MILLISECONDS, Schedulers.io())
                    .flatMap((Function<Long, ObservableSource<List<Tables>>>) aLong -> RetrofitClient.getInstance().getApi()
                            .getTables(Utils.getTablesURL(ip), String.valueOf(roomNumber))
                            .doOnError(err -> Log.e("Polling", "Error retriving tables: " + err))
                            .onErrorResumeNext(throwable -> Observable.empty()))
            ...