G-J
G-J

Reputation: 400

Retrofit 2 error: NetworkOnMainThreadException

I'm trying to do a synchronous request to my server in Android. (Code works flawlessly in a plain Java project)

I know that sync requests shouldn't be done on the main thread, so I start a new thread where the networking is done. I also know that it is possible to do Async-calls but a sync call fits my usecase better.

bool networkingIsDoneHere(){
  dostuff();
  int number = doNetworkCall();
  if(number < 500){
    return true;
  }
  return false
}

The problem is that I still get the "NetworkOnMainThreadException" error. Does retrofit somehow run on the main thread, while being executed on a side thread?

Starting new thread:

Thread timerThread = new Thread() {
        @Override
        public void run() {
            while (true) {
                try {
                    networkingIsDoneHere();
                    sleep(getExecutionInterval());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    };

    @Override
    public void startRule() {
        timerThread.start();
    }

Retrofit code

    private Retrofit createRetrofitAdapter() throws Exception {
        return retrofit = new Retrofit.Builder()
                .baseUrl("https://maps.googleapis.com/maps/api/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

    }

    public interface ApiGoogleMapsService {
        @GET("url...")
        Call<MapApiCall> getDurationJson(@Query("origins") String origin, @Query("destinations") String destination);
    }

error:

        Caused by: android.os.NetworkOnMainThreadException
be.kul.gj.annotationfw W/System.err:     at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1273)
be.kul.gj.annotationfw W/System.err:     at com.android.org.conscrypt.OpenSSLSocketImpl.shutdownAndFreeSslNative(OpenSSLSocketImpl.java:1131)
be.kul.gj.annotationfw W/System.err:     at com.android.org.conscrypt.OpenSSLSocketImpl.close(OpenSSLSocketImpl.java:1126)
be.kul.gj.annotationfw W/System.err:     at okhttp3.internal.Util.closeQuietly(Util.java:105)
be.kul.gj.annotationfw W/System.err:     at okhttp3.internal.http.StreamAllocation.deallocate(StreamAllocation.java:260)
be.kul.gj.annotationfw W/System.err:     at okhttp3.internal.http.StreamAllocation.connectionFailed(StreamAllocation.java:289)
be.kul.gj.annotationfw W/System.err:     at okhttp3.internal.http.HttpEngine.close(HttpEngine.java:429)
be.kul.gj.annotationfw W/System.err:     at okhttp3.RealCall.getResponse(RealCall.java:270)
be.kul.gj.annotationfw W/System.err:     at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:198)
be.kul.gj.annotationfw W/System.err:     at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:160)
be.kul.gj.annotationfw W/System.err:     at okhttp3.RealCall.execute(RealCall.java:57)
be.kul.gj.annotationfw W/System.err:     at retrofit2.OkHttpCall.execute(OkHttpCall.java:177)
be.kul.gj.annotationfw W/System.err:     at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:87)
be.kul.gj.annotationfw W/System.err:     at model.GoogleMapsAccess.getTravelDuration(GoogleMapsAccess.java:52)
be.kul.gj.annotationfw W/System.err:     at rule.RoutePickupRule.condition(RoutePickupRule.java:40)

Upvotes: 4

Views: 9415

Answers (2)

Niels Masdorp
Niels Masdorp

Reputation: 2428

Instead of starting a new thread yourself, Retrofit can automatically do requests on a separate thread. You can do this by calling enqueue();.

Call<MapApiCall> call = api.getDurationJson(foo);

call.enqueue(new Callback<MapApiCall>() {
       @Override
       public void onResponse(Call<MapApiCall> call, Response<MapApiCall> response) {
           //Do something with response
       }

       @Override
       public void onFailure(Call<MapApiCall> call, Throwable t) {
           //Do something with failure
       }
   });

If you want synchronous requests use execute() within your seperate thread:

Call<MapApiCall> call = api.getDurationJson();  
MapApiCall apiCall = call.execute().body();  

If you, for some reason, want to disable the NetworkOnMainThreadException without using threads use:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

However, this is really not recommended, if you don't know what StrictMode does, do NOT use it.

Upvotes: 5

IgorGanapolsky
IgorGanapolsky

Reputation: 26831

Your problem is that createRetrofitAdapter method was called on the main thread. That's where you were building a new Retrofit instance, but it absolutely cannot be done on the UI thread. The simplest shortcut is to do it in an AsyncTask.

Upvotes: 0

Related Questions