1. Preface
Recently, I want to create a function to download files with progress bars. I looked around online and found that many of them were implemented based on OkHttpClient with the addition of interceptors. I personally think it is a bit complicated, so I still use the simplest method to implement it: monitor progress based on file writing.
2. Implementation steps
2.1 Designing a monitoring interface
Design the interface according to the requirements:
public interface DownloadListener { void onStart();//Download starts void onProgress(int progress);//Download progress void onFinish(String path);//Download completed void onFail(String errorInfo);//Download failed}
If you still need download speed, etc., you can design the interface and parameters yourself.
2.2 Writing a network interface service
public interface DownloadService { @Streaming @GET Call<ResponseBody> download(@Url String url); }
It is basically the same as the normal interface writing method. It should be noted that the @Streaming annotation should be added.
By default, Retrofit will read all the server-side Response into memory before processing the results. If the server returns a very large file, it is easy to occur. The main function of using @Streaming is to write the bytes downloaded in real time to disk immediately without reading the entire file into memory.
2.3 Start network request
public class DownloadUtil { public static void download(String url, final String path, final DownloadListener downloadListener) { Retrofit retrofit = new () .baseUrl("") //Get a thread through the thread pool and specify that callback runs in the child thread. .callbackExecutor(()) .build(); DownloadService service = (); Call<ResponseBody> call = (url); (new Callback<ResponseBody>() { @Override public void onResponse(@NonNull Call<ResponseBody> call, @NonNull final Response<ResponseBody> response) { //Write Response to the disk, see the following analysis for details //Note that this method is run in the child thread writeResponseToDisk(path, response, downloadListener); } @Override public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable throwable) { ("Network Error~"); } }); } }
In Retrofit, Callback runs in the main thread by default. If we write Response directly to disk, this operation will be run directly in the main thread, a NetworkOnMainThreadException will be reported. So it must be placed in the child thread to run.
So far, it is still a very normal network request. So, it's still very simple. The highlight of the following is here.
2.4 Monitoring download progress
private static void writeResponseToDisk(String path, Response<ResponseBody> response, DownloadListener downloadListener) { //Get input stream and total size from response writeFileFromIS(new File(path), ().byteStream(), ().contentLength(), downloadListener); } private static int sBufferSize = 8192; //Write the input stream to the file private static void writeFileFromIS(File file, InputStream is, long totalLength, DownloadListener downloadListener) { //Start download (); //Create a file if (!()) { if (!().exists()) ().mkdir(); try { (); } catch (IOException e) { (); ("createNewFile IOException"); } } OutputStream os = null; long currentLength = 0; try { os = new BufferedOutputStream(new FileOutputStream(file)); byte data[] = new byte[sBufferSize]; int len; while ((len = (data, 0, sBufferSize)) != -1) { (data, 0, len); currentLength += len; //Calculate the current download progress ((int) (100 * currentLength / totalLength)); } //Download is completed and return to the saved file path (()); } catch (IOException e) { (); ("IOException"); } finally { try { (); } catch (IOException e) { (); } try { if (os != null) { (); } } catch (IOException e) { (); } } }
Therefore, it is actually to realize the monitoring of progress by listening to the file writing.
2.5 Use Examples
String url = ""; String path = ""; (url, path, new DownloadListener() { @Override public void onStart() { //Run in child thread } @Override public void onProgress(int progress) { //Run in child thread } @Override public void onFinish(String path) { //Run in child thread } @Override public void onFail(String errorInfo) { //Run in child thread } });
Note that the above callbacks are all run in child threads. If you need to update the UI and other operations, you can use Handler and so on to update it.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.