我尝试使用改造库从分离的线程中的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)
答案 0 :(得分:5)
onResponse
在UI Thread
上运行,byteStream()
访问底层网络连接,并且如异常所述,您无法执行此操作。正如@EpicPandaForce
指出的那样,由于您使用的是自己的主题,因此您不需要致电enqueue
,但可以直接致电execute()
,这是一个阻止来电Response<T>
:
Response<ResponseBody> response = call.execute();
if (response.isSuccessful()) {
writeResponseBodyToDisk(response.body());
}
在这种情况下,所有内容都在您的线程的上下文中运行,而不是在UI线程上运行。在您的帖子中,您可以访问byteStream()
,但您无法体验NetworkOnMainThreadException