Seconds Understanding File Download Implementation of Retrofit2 Zone Progress Bar

Posted by ali_mac1 on Thu, 09 May 2019 08:09:09 +0200

Copyright Statement) Notes for non-commercial purposes are free to be reproduced
Blog address: https://blog.csdn.net/ShuSheng0007/article/details/82428733
From: shusheng007

Summary

Retrofit, an encapsulated Library of Okhttp, has become the label of network requests in Android development. It is very convenient to use it to handle Http requests, but when it comes to download functions, it is more difficult, especially when you need to get the download progress in real time. When you download, you just need to get the download progress. So this part of the knowledge is summarized.

Achieving results

Previous implementation diagram

Implementation Method

We know Retrofit is the encapsulation Library of Okhttp, OkHttp is the real originator of network requests, and OkHttp has a strong interceptor mechanism.If you don't know what interceptor mechanism is, simply look at the following example, otherwise skip it:

For example, Wang Er-gou and Li Fat-zi both like the emerald flower. Wang Er-gou and Li Fat-zi are separated by Li Fat-zi. The king Er-gou wrote a love note to the emerald flower and was blocked by Li Fat-zi in the middle when throwing it in. The Li Fat-zi can modify the contents of the note, even throw away the original piece of paper and change it to his own strip, or simply leave the paper blank.Bar to emerald, etc.In this case, Fat Li is like an interceptor.

In fact, the real-time report of download progress is achieved through the interceptor of OkHttp. During the download process of OkHttp, the interceptor is used to intercept the downloaded content, calculate the progress, and then expose it to users.

Specific implementation

Step 1: Introduce the Retrofit class library

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.2.0'
}

Step 2: Define the service interface

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

The downloaded interface is a Get request, but you need to mark it with the @Streaming annotation. If you do not use this annotation, Okhttp will load the downloaded content into memory at once before returning, which will cause OOM to occur when you download a slightly larger file.It is worth noting that we marked our parameters with @Url to indicate that the dynamic Url part can be passed in.

Step 3: Customize the Return Result ResponseBody

Since we use an interceptor to intercept downloads, which is a ResponseBody type of data, this is where we get the download progress.

public class DownloadResponseBody extends ResponseBody {

    private ResponseBody responseBody;
    private DownloadListener downloadListener;
    private BufferedSource bufferedSource;
    private Executor executor;

    public DownloadResponseBody(ResponseBody responseBody, Executor executor, DownloadListener downloadListener) {
        this.responseBody = responseBody;
        this.downloadListener = downloadListener;
        this.executor = executor;
    }

    @Override
    public MediaType contentType() {
        return responseBody.contentType();
    }

    @Override
    public long contentLength() {
        return responseBody.contentLength();
    }

    @Override
    public BufferedSource source() {
        if (bufferedSource == null) {
            bufferedSource = Okio.buffer(source(responseBody.source()));
        }
        return bufferedSource;
    }

    private Source source(Source source) {
        return new ForwardingSource(source) {
            long totalBytesRead = 0L;

            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                final long bytesRead = super.read(sink, byteCount);
                // read() returns the number of bytes read, or -1 if this source is exhausted.
                if (null != downloadListener) {
                    totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                    Logger.t("DownloadUtil").d("Downloaded:" + totalBytesRead + "Share:" + responseBody.contentLength());
                    final int progress = (int) (totalBytesRead * 100 / responseBody.contentLength());
                    if (executor != null) {
                        executor.execute(() -> downloadListener.onProgress(progress));
                    } else {
                        downloadListener.onProgress(progress);
                    }
                }
                return bytesRead;
            }
        };
    }
}

Look mainly at the private Source source method, which provides Okio with a data source (okhttp uses Okio internally to process IO). We can see that in the source method, there is a function read(), which reads the byte stream, in which we calculate the download progress.It is worth noting here that we used an Executor, which is used to switch the method of exposing progress to the UI thread.

Step 4: Define the download interceptor

Interceptors are simpler. An executor for switching threads and a listener for progress monitoring are passed in from the constructor. By setting this interceptor, the download process is intercepted in our custom DowloadResponseBody.

public class DownloadInterceptor implements Interceptor {

    private DownloadListener listener;
    private Executor executor;

    public DownloadInterceptor(Executor executor, DownloadListener listener) {
        this.listener = listener;
        this.executor = executor;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());

        return originalResponse.newBuilder()
                .body(new DownloadResponseBody(originalResponse.body(), executor, listener))
                .build();
    }
}

Step 5: Build an Okhttp instance and set up an interceptor

OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(interceptor)
        .retryOnConnectionFailure(true)
        .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
        .build();

Step 6: Build a Retrofit instance and initiate a downloaded network request

final DownloadService api = new Retrofit.Builder()
        .baseUrl(baseUrl)
        .client(client)
        .build()
        .create(DownloadService.class);
new Thread(() -> {
    try {
        Response<ResponseBody> result = api.downloadWithDynamicUrl(rUrl).execute();
        File file = writeFile(filePath, result.body().byteStream());
        if (listener != null){
            executor.execute(()->{
                listener.onFinish(file);
            });
        }

    } catch (IOException e) {
        if (listener != null){
            executor.execute(()->{
                listener.onFailed(e.getMessage());
            });
        }
        e.printStackTrace();
    }
}).start();

How to use

It is easy to use, call the download function after getting an instance of the download tool class, enter and exit the network address of the file to be downloaded and the locally saved address, pass in progress monitoring, and complete your business logic in the listening callback.

 DownloadUtil.getInstance()
                .downloadFile(baseUrl, url, desFilePath, new DownloadListener() {
                    @Override
                    public void onFinish(final File file) {
                        tvFileLocation.setText("The downloaded file address is:" + file.getAbsolutePath());
                        installAPK(file, MainActivity.this);
                    }

                    @Override
                    public void onProgress(int progress) {
                        tvProgress.setText(String.format("Download progress is:%s", progress));
                    }

                    @Override
                    public void onFailed(String errMsg) {

                    }
                });

summary

Obviously Retrofit is more cumbersome to download than other OkHttp encapsulated class libraries, but it gives developers more flexibility.OkHttp's interceptor mechanism is worth learning for each of us.

Source Download Address

Topics: OkHttp Retrofit network Android