2016-03-04 5 views
2

Я пытаюсь выполнить синхронный запрос на мой сервер в Android. (Код работает безупречно в простом Java-проекте)Ошибка Retrofit 2: NetworkOnMainThreadException

Я знаю, что запросы синхронизации не должны выполняться в основном потоке, поэтому я начинаю новый поток, где сетевое взаимодействие выполняется. Я также знаю, что можно делать асинхронные вызовы, но вызов синхронизации лучше подходит для меня.

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

Проблема в том, что я все еще получаю сообщение об ошибке «NetworkOnMainThreadException». Переоборудовать ли каким-то образом на основной поток, выполняя на боковой нитке?

Запуск нового потока:

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(); 
    } 

код дооснащения

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); 
    } 

ошибка:

 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) 

ответ

3

Вместо того чтобы начать новую нить себя, дооснащения может автоматически делать запросы на отдельном потоке , Вы можете сделать это, позвонив по телефону 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 
     } 
    }); 

Если вы хотите синхронные запросы использовать execute() в вашей отдельной теме:

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

Если вы по какой-то причине хотите отключить NetworkOnMainThreadException без использования потоков использовать:

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

Однако это действительно не рекомендуется, если вы не знаете, что делает StrictMode, НЕ используйте его.

+1

В моем примере я добавил «networkingIsDoneHere». Это показывает, почему я не хочу использовать это решение. Мне нужен результат моего сетевого вызова в моем «if case» и возвращает true для false. –

+0

Извините, я, должно быть, неправильно понял. Вы используете 'execute()' в своем потоке? Я обновил свой ответ. –

+0

Думаю, я нашел свою проблему. Я вызываю код где-то еще из основного потока с помощью Handler с Runnable, но это, похоже, не работает в отдельном потоке. Я собираюсь принять твое мнение, потому что это хорошее объяснение общей концепции :) –

0

Ваша проблема в том, что метод createRetrofitAdapter был вызван в основной поток. Здесь вы создаете новый экземпляр Retrofit, но это абсолютно невозможно сделать в потоке пользовательского интерфейса. Простейшим ярлыком является сделать это в AsyncTask.