Retrofit 2错误:NetworkOnMainThreadException

4

我正在尝试在Android中对我的服务器进行同步请求。(代码在普通Java项目中可以完美运行)

我知道同步请求不应该在主线程上执行,所以我启动了一个新的线程来处理网络请求。我也知道可以使用异步调用,但是同步调用更适合我的用例。

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

问题在于我仍然收到“NetworkOnMainThreadException”错误。Retrofit是否在主线程上运行,同时在侧线程上执行?
启动新的线程:
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 代码
    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)
2个回答

5

不需要自己启动新线程,Retrofit可以自动在单独的线程上执行请求。您可以通过调用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。 - G-J
抱歉,我可能误读了。你的线程中是否使用了 execute()?我已经更新了我的答案。 - Niels Masdorp
我想我找到了问题所在。我正在使用Handler和Runnable从主线程的其他位置调用代码,但这似乎并没有在单独的线程上运行。我将接受您的答案,因为它是对一般概念的很好解释 :) - G-J
啊,好的,很抱歉我不能再帮你了! - Niels Masdorp
1
@NielsMasdorp 是的,你是)) 感谢你编辑帖子,祝你有美好的一天! - Volodymyr R.tmnko
显示剩余2条评论

0
你的问题在于createRetrofitAdapter方法在主线程上被调用。这是你正在构建新的Retrofit实例的地方,但绝对不能在UI线程上完成。最简单的解决方法是在AsyncTask中完成它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接