Android:在新线程中使用改造时会发生NetworkOnMainThreadException吗?

时间:2016-07-21 14:51:40

标签: android multithreading retrofit

我尝试使用改造库从分离的线程中的url下载文件(apk:2mB)。

这是我的代码(请参阅教程:https://futurestud.io/blog/retrofit-2-how-to-download-files-from-server):

public class MainActivity extends AppCompatActivity {
    String fileUrl = ".....";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        new Thread(new Runnable() {
            @Override
            public void run() {
                FileDownloadService downloadService = ServiceGenerator.createService(FileDownloadService.class);
                Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl2);

                call.enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        if (response.isSuccessful()) {
                           writeResponseBodyToDisk(response.body());
                        } else {
                            Log.d("fail", "server contact failed");
                        }
                    }

                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {
                        Log.e("fail", "error");
                    }
                });
            }
        }).start();
    }

    private boolean writeResponseBodyToDisk(ResponseBody body) {
        try {
            File apkFile = new File(getExternalFilesDir(null) +
                    File.separator + "test.apk");
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                byte[] fileReader = new byte[4096];

                inputStream = body.byteStream();
                outputStream = new FileOutputStream(apkFile);

                while (true) {
                    int read = inputStream.read(fileReader);

                    if (read == -1) {
                        break;
                    }

                    outputStream.write(fileReader, 0, read);
                }

                outputStream.flush();

                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }

                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

    }
}

我的服务代码:

public class ServiceGenerator {

    public static final String API_BASE_URL = "https://your.api.url/";

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

    private static Retrofit.Builder builder =
        new Retrofit.Builder()
                .baseUrl(API_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create());

    public static <S> S createService(Class<S> serviceClass) {
        Retrofit retrofit = builder.client(httpClient.build()).build();
        return retrofit.create(serviceClass);
    }
}

public interface FileDownloadService {
    @Streaming
    @GET
    Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);
}

我在一个新线程中开始下载任务,但我得到了这个:

    android.os.NetworkOnMainThreadException

如果我选择下载3kB的图像,程序运行正常。如果我添加以下代码:

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

下载将持续一段时间,并停止信息:

    StrictMode policy violation; ~duration=1342 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2

这是堆栈跟踪:

Process: com.testapp.yiyuanguo.apkinstall, PID: 25788
android.os.NetworkOnMainThreadException   
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:249)
at libcore.io.IoBridge.recvfrom(IoBridge.java:553)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:485)
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:37)
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:237)
at okio.Okio$2.read(Okio.java:139)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:211)
at okio.RealBufferedSource.read(RealBufferedSource.java:50)
at okhttp3.internal.http.Http1xStream$FixedLengthSource.read(Http1xStream.java:381)
at okhttp3.internal.Util.skipAll(Util.java:178)
at okhttp3.internal.Util.discard(Util.java:160)
at okhttp3.internal.http.Http1xStream$FixedLengthSource.close(Http1xStream.java:397)
at okio.RealBufferedSource.close(RealBufferedSource.java:396)
at okio.ForwardingSource.close(ForwardingSource.java:43)
at okio.RealBufferedSource.close(RealBufferedSource.java:396)
at okio.RealBufferedSource$1.close(RealBufferedSource.java:384)
at com.testapp.apkinstall.MainActivity.writeResponseBodyToDisk(MainActivity.java:87)
at com.testapp.apkinstall.MainActivity.access$000(MainActivity.java:24)
at com.testapp.apkinstall.MainActivity$1$1.onResponse(MainActivity.java:42)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:68)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)

1 个答案:

答案 0 :(得分:5)

onResponseUI Thread上运行,byteStream()访问底层网络连接,并且如异常所述,您无法执行此操作。正如@EpicPandaForce指出的那样,由于您使用的是自己的主题,因此您不需要致电enqueue,但可以直接致电execute(),这是一个阻止来电Response<T>

Response<ResponseBody> response = call.execute();
if (response.isSuccessful()) {
  writeResponseBodyToDisk(response.body());
}

在这种情况下,所有内容都在您的线程的上下文中运行,而不是在UI线程上运行。在您的帖子中,您可以访问byteStream(),但您无法体验NetworkOnMainThreadException