XDRush

使用Retrofit下载文件

说明

这里不会对Retrofit进行介绍,欲了解Retrofit的请自行google之。这里只关注如何用Retrofit从服务器下载文件。

Retrofit下载普通文件

这里普通文件是指比较小的文件,比如图片、文本等。如果你厌倦了HttpRequest下载文件的方式,相信你看了下文一定会喜欢上Retrofit的。
这里我以Android中下载网络图片来设置ImageView为例:

定义Retrofit接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final static String mBaseUrl = "http://opsen-static.dolphin-browser.com";
private final static String mFileUrl = "/resources/icon/provision_services/1443166704.png";
interface DownloadServiceApi {
@GET(mFileUrl)
Call<ResponseBody> getSmallSizeFile();
}
private DownloadServiceApi getDownloadServiceApi() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(mBaseUrl)
.build();
return retrofit.create(DownloadServiceApi.class);
}

异步下载图片并设置ImageView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 网络操作另开线程来做.
new Thread(new Runnable() {
@Override
public void run() {
downloadFile();
}
}).start();
private void downloadSmallFile() {
DownloadServiceApi downloadServiceApi = getDownloadServiceApi();
final Call<ResponseBody> call = downloadServiceApi.getSmallSizeFile();
try {
// 同步下载文件
Response<ResponseBody> response = call.execute();
if (response.isSuccessful()) {
ResponseBody responseBody = response.body();
byte date[] = responseBody.bytes();
final Bitmap bitmap = BitmpaFactory.decodeByteArray(data, 0, date.length);
// 设置ImageView,注意要在UI线程中做.
runOnUiThread(new Runnable() {
@Override
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
new Thread(new Runnable() {
@Override
public void run() {
downloadSmallFile();
}
}).start();

有一点需要注意,那就是必须以ResponseBody作为返回类型,Retrofit会试图解析并转换它,所以不能使用任何其他返回类型,否则下载的文件毫无意义。

文件下载下来了,要保存在本地也就是很简单的事了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
private boolean writeResponseBodyToDisk(ResponseBody responseBody) {
try {
File saveFile = new File(getExternalFilesDir(null) + File.separator + "MyFile.apk");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[2000];
inputStream = responseBody.byteStream();
outputStream = new FileOutputStream(saveFile);
while (true) {
int read = inputStream.read(fileReader);
if (-1 == read) {
break;
}
outputStream.write(fileReader, 0, read);
}
outputStream.flush();
// 提示下载成功.
runOnUiThread(new Runnable() {
@Override
public void run() {
showTips("Save to local storage finished!");
}
});
return true;
} catch (Exception e) {
return false;
} finally {
if (null != inputStream) {
inputStream.close();
}
if (null != outputStream) {
outputStream.close();
}
}
} catch (Exception e) {
Logger.e(e.toString());
}
return false;
}
private void showTips(String tips) {
Toast.makeText(MainActivity.this, tips, Toast.LENGTH_SHOW).show();
}

Retrofit下载大文件

默认情况下,Retrofit在处理结果之前会将这个ResponseBody读进内存,这在普通文件的Response上表现还好,但如果是一个视频等大文件,就极有可能导致OutOfMemory。

好在Retrofit为了避免这个问题,引入了Streaming注解!它意味着立即传递字节码而不是一下子传递整个文件然后缓存在内存中。来看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
interface DownloadServiceApi {
@GET(mFileUrl)
Call<ResponseBody> getSmallSizeFile();
@Streaming
@GET
Call<ResponseBody> getBigSizeFile(@Url String fileUrl);
}
private void downloadBigFile() {
String fileUrl = "........";
DownloadServiceApi downloadServiceApi = getDownloadServiceApi();
final Call<ResponseBody> call = downloadFileApi.getBigSizeFile(fileUrl);
// 异步下载文件.
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, final Response<ResponseBody> response) {
if (response.isSuccessful()) {
showTips("download Successful and start writing file to local storage.");
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
boolean writeResult = writeResponseBodyToDisk(response.body());
return null;
}
}.execute();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
showTips("download failed!");
}
});
}
new Thread(new Runnable() {
@Override
public void run() {
downloadBigFile();
}
}).start();

这里也有一点需要特别注意:onResponse()、onFailure()方法都是在主线程中调用的,因此最好使用AsyncTask()来处理保存文件到本地的操作,不然会阻塞UI线程。