Oliver Hausler
Oliver Hausler

Reputation: 4977

UI blocking occurs in Android despite RxJava

I am using RxJava to move network access to a separate thread in Android, but my UI still blocks.

I am not using the wrong observable as shown here: Android RxJava, Non Blocking?

The codepoints [A], [B] and [C] in below code are passed in the order [A] -> [C] -> [B] so the current thread is processed fine and RxJava calls [C] once it had a result. This is fine.

Also, blocking is much better compared to doing the network call on the UI thread, but I still have minor blocking. The UI stays fluent after the call is made, but if the server does not respond in a matter of milliseconds, it blocks.

    private search; // search is an instance variable in the same class

    // [A]

    Observable.just(search.find("something")) // search.find calls the REST endpoint
            .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Action1<Search>() {

                @Override public void call(Search search) {
                    // further processing // [B]
                }

            }, new Action1<Throwable>() {

                @Override public void call(Throwable throwable) {
                    // error handler
                }

            });

    // [C]

Could it be a problem that search is an instance variable in the same class where the Observable uses it, but the endpoint call is performed from a separate library? It shouldn't matter, right?

Am I doing anything bad that I shouldn't be doing?

--

Find looks like this (removed exception handling for brevity):

public Search find(String searchtext) {
    setSearchtext(searchtext);
    SearchEndpoint.find(Session.getUser().getId(), searchtext);
    return this;
}

SearchEndpoint like this:

public static Search find(final Long userId, final String searchtext) throws IOException {
    return ApiService.api().searches().find(userId).setFind(searchtext).execute();
}

and makes a call to the generated Google cloud endpoint library.

Upvotes: 2

Views: 2374

Answers (2)

ExactaBox
ExactaBox

Reputation: 3385

I know this is a few months old-- but instead of createing an entirely new Observable (which is relatively error-prone), you can use the map operator to run the search:

String search_input = "something"; // this is where you can specify other search terms
Observable.just(search_input) 
      .map(s -> search.find(s))  // search.find calls the REST endpoint
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe( // your subscriber goes here

If not using lambdas, that map function should look like:

.map(new Func1<String, Search>() {
      @Override          
      public Search call(String s) {
          return search.find(s)
      }
 })

Upvotes: 1

david.mihola
david.mihola

Reputation: 12982

Try this:

Observable.create(new Observable.OnSubscribe<Search>() {

    @Override
    // method signature is from memory - I hope I am correct...
    public void call(Subscriber<? super Search> subscriber) {
        try {
            Search search = search.find("something");
            subscriber.onNext(search);
            subscriber.onCompleted();
        } catch (SomeException e) {
            subscriber.onError(e);
        }
    }
})
// and then continue with your .subscribeOn(...)

To clarify, maybe this makes the problem with your code more obvious:

Observable.just(search.find("something"))

is clearly equivalent to

Search search = search.find("something");
Observable.just(search)

And this makes it obvious that search.find is executed before we ever hand the control over to rxjava and it is executed on whatever thread you are currently on - then the construction of an Observable from the pre-computed value and the delivery of the value happen on another thread but that does not help you much...

Upvotes: 4

Related Questions