1

I have the following peace of code containing network operations. Where I get the well known error Error in http connectionandroid.os.NetworkOnMainThreadException which is pretty descriptive and explained for example here.

What I don't understand is, how can the error occur here when I clearly subscribe on Scheduler.io(). What do I do wrong?

ReactiveLocationProvider locationProvider = new ReactiveLocationProvider(mContext);
locationProvider.getLastKnownLocation().subscribeOn(Schedulers.io()).subscribe((location -> {
    // ... unrelated code
    try {
        this.postPing(pingResponse); // <<< this is an http post causing the error
        Log.d(LOG_TAG, "trying to post response");
    } catch (Exception e) {
        e.printStackTrace();
    }
    cursor.close();

Edit: This does not seem to be entirely trivial. That's why I provide additional context.

The code above used to look like this and didn't use ReactiveLocationProvider, but rather the FusedLocationApi

 Location location = LocationServices.FusedLocationApi.getLastLocation(MainActivity.mGoogleApiClient);
                    // ... unrelated code
                    try {
                        this.postPing(pingResponse);
                        Log.d(LOG_TAG, "trying to post response");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    cursor.close();

And was run from within a GcmListenerService:

@Override
    public void onMessageReceived(String from, Bundle data) {
        Log.d("GCMListener", "onMessageReceived");
        String action = data.getString("action");
        switch (action) {
            case "getPing":
                requests.getPingObservable()
                        .subscribeOn(Schedulers.io()).retryWhen(
                        errors ->
                                errors
                                        .zipWith(Observable.range(1, 3), (n, i) -> i)
                                        .flatMap(retryCount -> Observable.timer((long) Math.pow(5, retryCount), TimeUnit.SECONDS))
                        ).subscribe((pingsJsonObjectString) -> {
                }, (error) -> error.printStackTrace());
                break;
            default:
                break;
        }
    }

Unfortunately the GcmListenerService doesn't receive notifications when the app is closed. So I decided to cheat a bit and create an alarm to trigger the reciever with an alarm.

In The AndroidMainfest.xml I added the line <category android:name="com.smilingkoala.ping.periodic_wakeup"/>

<!-- The Google Cloud Messaging receiver and services -->
        <receiver
            android:name="com.google.android.gms.gcm.GcmReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="com.smilingkoala.ping" />
                <category android:name="com.smilingkoala.ping.periodic_wakeup"/>
            </intent-filter>
        </receiver>

and in MainActivity.java I called:

public void startAlarm(Context context) {
    Intent intent = new Intent("com.smilingkoala.ping.periodic_wakeup");
    PendingIntent sender = PendingIntent.getService(context, 0, intent, 0);
    AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
    am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
            AlarmManager.INTERVAL_FIFTEEN_MINUTES / 3, AlarmManager.INTERVAL_FIFTEEN_MINUTES, sender);
}

To restart the listener once the application is closed. This actually worked. I received a message. But now I didn't have access to MainActivity.mGoogleApiClient which I used in the old code. That's why I was forced to use ReactiveLocationProvider. However now i can't run the networking code.

I assume something I did leading up to this Error is forbidden.

Community
  • 1
  • 1
Lukasz
  • 2,257
  • 3
  • 26
  • 44
  • The subscribe happens back on the UI thread? – OneCricketeer Jun 17 '16 at 20:56
  • @cricket_007 I'm not quite sure if I understand correctly, but If you mean the script around the code snipped above then no. It's part of an observable called also on `Schedulers.io`. – Lukasz Jun 17 '16 at 21:09
  • Well, `this.postPing(pingResponse);`seems to be happening on the main thread, yes? Otherwise, why are you getting an error? – OneCricketeer Jun 17 '16 at 21:15
  • @cricket_007 So when I call a method from within subscribe it is not run on the same thread, as the rest of the stuff in subscribe? – Lukasz Jun 17 '16 at 21:21
  • Should all be on the same thread. That is just the first method that requires not to be on the main thread. I don't really have any examples of network calls on RxJava – OneCricketeer Jun 17 '16 at 21:33
  • @cricket_007 "Should all be on the same thread." So you understand how weird this is for me. I don't even know how to tackle debugging this. Any tips on this? – Lukasz Jun 17 '16 at 21:40
  • Try with a simpler example. Is all that location code really necessary to debug the issue? – OneCricketeer Jun 17 '16 at 21:45
  • @Lukasz is the try/catch code executed in an Observable operator such as map/doOnNext etc? Is it in the final subscribe() call? – Tassos Bassoukos Jun 18 '16 at 09:29
  • @TassosBassoukos the try catch is in the subscribe of `getLastKnownLocation()`, however this is itself called within the observable `getPingObservable()` – Lukasz Jun 18 '16 at 09:48

1 Answers1

2

Ok, I am not 100% sure, but let's try to explain this (:

If you read documentation on .subscribeOn operator, you'll see that it defines scheduler on which Observable itself (not the Subscriber!) will be executed. You need to explicitly define scheduler for subscriber with .observeOn operator, or it will be executed on thread where it was subscribed on.

For more detailed information and examples you should check these links: http://www.grahamlea.com/2014/07/rxjava-threading-examples/ http://www.introtorx.com/Content/v1.0.10621.0/15_SchedulingAndThreading.html#SubscribeOnObserveOn

Also you can debug and experiment with threading by calling Thread.currentThread() to check where is your code executing.

aleien
  • 786
  • 1
  • 9
  • 22