Example of downloading files and implementing progress monitoring using Retrofit
- 2021-10-11 19:30:27
- OfStack
1. Preface
Recently, I want to do a function of downloading files with progress bar. After watching it on the Internet for 1 circle, I found that many of them are realized by adding interceptors based on OkHttpClient. Personally, I think it is slightly complicated, so I still use the simplest method to realize it: monitoring the progress based on file writing.
2. Implementation steps
2.1 Design the listening interface
Design the interface under 1 according to the requirements:
public interface DownloadListener {
void onStart();// Download Start
void onProgress(int progress);// Download progress
void onFinish(String path);// Download complete
void onFail(String errorInfo);// Download failed
}
If you still need download speed and so on, you can design your own interface and parameters.
2.2 Writing Network Interface Service
public interface DownloadService {
@Streaming
@GET
Call<ResponseBody> download(@Url String url);
}
It is basically 1 with the normal interface writing. It should be noted that @ Streaming annotation should be added.
By default, Retrofit reads all of the server-side Response into memory before processing the results. If a very large file is returned on the server side, oom is prone to occur. The main function of @ Streaming is to write the bytes downloaded in real time to disk immediately, instead of reading the whole file into memory.
2.3 Start a network request
public class DownloadUtil {
public static void download(String url, final String path, final DownloadListener downloadListener) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.xxx.com")
// Obtained through thread pool 1 Threads, specifying callback Run in child threads.
.callbackExecutor(Executors.newSingleThreadExecutor())
.build();
DownloadService service = retrofit.create(DownloadService.class);
Call<ResponseBody> call = service.download(url);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull final Response<ResponseBody> response) {
// Will Response Write to slave disk, see the following analysis for details
// Note that this method runs in a child thread
writeResponseToDisk(path, response, downloadListener);
}
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable throwable) {
downloadListener.onFail(" Network Error ~ ");
}
});
}
}
In Retrofit, Callback runs in the main thread by default. If we write Response directly to disk, this operation runs directly in the main thread, it will report NetworkOnMainThreadException exception. Therefore, it must be put into a child thread to run.
So far, it is still a normal network request. So, it's still very simple. Here comes the highlight.
2.4 Monitor Download Progress
private static void writeResponseToDisk(String path, Response<ResponseBody> response, DownloadListener downloadListener) {
// From response Get the input stream and the total size
writeFileFromIS(new File(path), response.body().byteStream(), response.body().contentLength(), downloadListener);
}
private static int sBufferSize = 8192;
// Write the input stream to a file
private static void writeFileFromIS(File file, InputStream is, long totalLength, DownloadListener downloadListener) {
// Start downloading
downloadListener.onStart();
// Create a file
if (!file.exists()) {
if (!file.getParentFile().exists())
file.getParentFile().mkdir();
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
downloadListener.onFail("createNewFile IOException");
}
}
OutputStream os = null;
long currentLength = 0;
try {
os = new BufferedOutputStream(new FileOutputStream(file));
byte data[] = new byte[sBufferSize];
int len;
while ((len = is.read(data, 0, sBufferSize)) != -1) {
os.write(data, 0, len);
currentLength += len;
// Calculate the current download progress
downloadListener.onProgress((int) (100 * currentLength / totalLength));
}
// Download is complete and return to the saved file path
downloadListener.onFinish(file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
downloadListener.onFail("IOException");
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Therefore, in fact, it is to monitor the progress by listening to the writing of files.
2.5 Use Examples
String url = "";
String path = "";
DownloadUtil.download(url, path, new DownloadListener() {
@Override
public void onStart() {
// Run on child threads
}
@Override
public void onProgress(int progress) {
// Run on child threads
}
@Override
public void onFinish(String path) {
// Run on child threads
}
@Override
public void onFail(String errorInfo) {
// Run on child threads
}
});
Note that the above callbacks are all run in child threads. If you need to update UI and other operations, you can use Handler and other updates.