Network Request Issue

285 views
Skip to first unread message

Christophe Vandenberghe

unread,
Sep 4, 2014, 4:29:25 PM9/4/14
to rxj...@googlegroups.com
Hello,

I'm trying to perform a network request in the background. However, whatever I try I always get the following error: 

FATAL EXCEPTION: main
rx.exceptions.OnErrorNotImplementedException: Observers must subscribe from the main UI thread, but was Thread[RxCachedThreadScheduler-1,5,main]

The test application is straight forward: I have one input field and a button. Whenever the button is clicked the query entered in the input field is used to make a server request using "Request.execute(query)". For now, the result is just logged. Executing this code using rxjava-core-0.20.3 and rxjava-android-0.20.3 always results in the above error. Anyone got any ideas?

public class Search extends Activity {
 
 
@Override
 
protected void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);
    setContentView
(R.layout.activity_search);

   
final EditText inputQuery = (EditText) findViewById(R.id.input_query);
   
final ImageButton sendQuery = (ImageButton) findViewById(R.id.send_query);

   
ViewObservable.clicks(sendQuery, false)
     
.map(new Func1<ImageButton, String>() {
       
@Override public String call(ImageButton _) {
         
return inputQuery.getText().toString();
       
}
     
})
     
.map(new Func1<String, String>() {
       
@Override public String call(String query) {
         
try {
           
return Request.execute(query);
         
} catch (Exception e) {
            e
.printStackTrace();
         
}
           
return "Fail";
         
}
     
})
     
.subscribeOn(Schedulers.newThread())
     
.observeOn(AndroidSchedulers.mainThread())
     
.subscribe(new Action1<String>() {
       
@Override public void call(String result) {
         
Log.e("Search", "Result: " + result);
       
}
     
});
 
}
}





Shixiong Zhu

unread,
Sep 4, 2014, 8:49:48 PM9/4/14
to Christophe Vandenberghe, rxj...@googlegroups.com
The error is because `ViewObservable.clicks(sendQuery, false)` should be subscribed in the UI thread, but `subscribeOn(Schedulers.newThread())` makes it be subscribed in a new thread. You can use `observeOn` to run your network request in a new thread, such as:


        ViewObservable.clicks(sendQuery, false)
                .map(new Func1<ImageButton, String>() {
                    @Override
                    public String call(ImageButton _) {
                        return inputQuery.getText().toString();
                    }
                })
                .observeOn(Schedulers.newThread())
                .map(new Func1<String, String>() {
                    @Override
                    public String call(String query) {
                        try {
                            return Request.execute(query);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return "Fail";
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {

                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                    }

                    @Override
                    public void onNext(String s) {
                        Log.e("Search", "Result: " + s);
                    }
                });


In addition, I prefer to add an `onError` callback so that I can receive the `onError` message and won't encounter `OnErrorNotImplementedException`.


Best Regards,

Shixiong Zhu

Christophe Vandenberghe

unread,
Sep 7, 2014, 4:33:48 AM9/7/14
to rxj...@googlegroups.com, chv...@gmail.com
Thank you for the clarification, didn't know you could observe on a new thread.

I do have another question. I am using Fragments to separate parts of my Android UI. I have one Fragment containing the input field and which then performs the network request similar to the example given above. However, I now have two additional Fragments which want to use the result of these network requests. One which shows the results using a list and one which shows the results on a map. I use fragments as it depends on the hardware whether both are shown simultaneously (tablets) or seperatly (smart phones).

My question now is, how would you properly expose the observable that returns the network request results so it can be used in both fragments.

Austyn Mahoney

unread,
Sep 8, 2014, 4:57:20 PM9/8/14
to rxj...@googlegroups.com, chv...@gmail.com
You would subscribe to the same observable from each Fragment. Each Fragment would get onNext, onError, and onComplete events from the Observable.

There are many ways to accomplish this. Here is a pretty complex pattern to handle the multiple subscription problem and caching all in one go: https://gist.github.com/austynmahoney/f440444477ad0ec3ca32

Christophe Vandenberghe

unread,
Sep 12, 2014, 2:58:45 PM9/12/14
to Austyn Mahoney, rxj...@googlegroups.com
Thanks for the example, that can certainly help me.
Reply all
Reply to author
Forward
0 new messages