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
- Achieving results
- Implementation Method
-
Specific implementation
- Step 1: Introduce the Retrofit class library
- Step 2: Define the service interface
- Step 3: Customize the Return Result ResponseBody
- Step 4: Define the download interceptor
- Step 5: Build an Okhttp instance and set up an interceptor
- Step 6: Build a Retrofit instance and initiate a downloaded network request
- How to use
- summary
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.