0
votes

I'm working with rxjava2. My problem is that sometimes server sends me nothing (response body is null, and so on List size = 0), so in that case I'd like to repeat request after 5 sec. I have an Retrofit2 request:

  @GET("/ics/api/{bidId}/calltracking/reports/widgets")
    Single<List<CallTrackingWidget>> getWidgets(@Path("bidId") int bidId);

Use it like so:

RetrofitFactory.getRetrofitService().getWidgets(mDataManager.getBidId())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(widgets -> getView().showLoading())
                .doOnSuccess(widgets -> getView().hideLoading())
                .repeatWhen(widgets -> widgets.flatMap(size -> {
                    if ((int) size == 0) {
                        return Flowable.just("asd").delay(5, TimeUnit.SECONDS);
                    } else {
                        return widgets;
                    }
                }))
                .subscribe(widgets -> {
                    mDataManager.getProduct().getCallTrackingData().setWidgets(widgets);
                    getView().initWidgets(mDataManager.getProduct().getCallTrackingData().getWidgetNames());
                }, throwable -> {
                    handleError(throwable, R.string.error_internet);
                })

But my code a have an exeption:

android.view.ViewRootImpl$CalledFromWrongThreadException: 
Only the original thread that created a view hierarchy can touch its views.

Help me please to repeat request after 5 sec, when List size is 0.

2
Aren't you updating or dealing with UI off the main thread? That getView() tells me you probably are. You can only work with the UI using the main thread, in Android. - dazito
The delay() operator will use Schedulers.computation() as its default scheduler. The repeatWhen() will then move the whole observer chain on to a computation thread and out of the main thread. - Bob Dalgleish
@BobDalgleish so how to solve it? - Masquitos
Move the repeatWhen() operator up the chain so it occurs before observeOn(). Anything downstream of observeOn() will continue to operate on the main thread, and upstream may change as needed. - Bob Dalgleish

2 Answers

1
votes

I think your problem lies in these two lines:

.doOnSubscribe(widgets -> getView().showLoading())
.doOnSuccess(widgets -> getView().hideLoading())

These two actions will execute off the Main thread. And as the exception implies, you can only touch Android views from the Main thread.

Try moving this view manipulation somewhere else. Like this:

getView().showLoading(); // Show loading view
RetrofitFactory.getRetrofitService().getWidgets(mDataManager.getBidId())
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .repeatWhen(widgets - > widgets.flatMap(size - > {
        if ((int) size == 0) {
            return Flowable.just("asd").delay(5, TimeUnit.SECONDS);
        } else {
            return widgets;
        }
    }))
    .subscribe(widgets - > {
        getView().hideLoading(); // Hide loading view
        mDataManager.getProduct().getCallTrackingData().setWidgets(widgets);
        getView().initWidgets(mDataManager.getProduct().getCallTrackingData().getWidgetNames());
    }, throwable - > {
        getView().hideLoading(); // Hide loading view
        handleError(throwable, R.string.error_internet);
    })
0
votes

Because you don't deal with the view on main thread, you can write like this:

RetrofitFactory.getRetrofitService().getWidgets(mDataManager.getBidId())
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .doOnSubscribe(widgets -> getView().showLoading())
    .subscribeOn(AndroidSchedulers.mainThread())
    .repeatWhen(widgets - > widgets.flatMap(size - > {
        if ((int) size == 0) {
            return Flowable.just("asd").delay(5, TimeUnit.SECONDS);
        } else {
            return widgets;
        }
    }))
    .subscribe(widgets - > {
        getView().hideLoading(); // Hide loading view
        mDataManager.getProduct().getCallTrackingData().setWidgets(widgets);
        getView().initWidgets(mDataManager.getProduct().getCallTrackingData().getWidgetNames());
    }, throwable - > {
        getView().hideLoading(); // Hide loading view
        handleError(throwable, R.string.error_internet);
    })