Okhttp、Retrofit進(jìn)度獲取的方法(一行代碼搞定)
起因
對(duì)于廣大Android開(kāi)發(fā)者來(lái)說(shuō),最近用的最多的網(wǎng)絡(luò)庫(kù),莫過(guò)于Okhttp啦(Retrofit依賴Okhttp)。
Okhttp不像SDK內(nèi)置的HttpUrlConnection一樣,可以明確的獲取數(shù)據(jù)讀寫的過(guò)程,我們需要執(zhí)行一些操作。
介紹
Retrofit依賴Okhttp、Okhttp依賴于Okio。那么Okio又是什么鬼?別急,看官方介紹:
Okio is a library that complements java.io and java.nio to make it much easier to access, store, and process your data.
翻譯過(guò)來(lái)就是,Okio是一個(gè)實(shí)現(xiàn)了java.io和java.nio的一個(gè)類庫(kù),它讓連接,存儲(chǔ),處理你的數(shù)據(jù)更加輕松~(Okio既是讀寫相關(guān)類庫(kù),獲取進(jìn)度要從Okio入手)。
好吧,對(duì)于廣大開(kāi)發(fā)者來(lái)說(shuō),內(nèi)心是這樣的:TM又要看你文檔和用例,按你規(guī)則走,輕松個(gè)毛啊!
其實(shí),讀下API,看下Example熟悉后,人家設(shè)計(jì)的還是很棒噠。
廢話不多說(shuō),先看效果。
效果

實(shí)際代碼:
//添加下載攔截器(this參數(shù)是實(shí)現(xiàn)下載進(jìn)度接口的對(duì)象)
mDownClient = new OkHttpClient.Builder()
//只需要一行代碼就行了
.addNetworkInterceptor(new DownloadInterceptor(this))
.build();
//添加上傳攔截器(this參數(shù)是實(shí)現(xiàn)上傳回調(diào)接口的對(duì)象)
mUploadClient = new OkHttpClient.Builder()
//只需要一行代碼就行了
.addNetworkInterceptor(new UploadInterceptor(this))
.build();
你只需要一行代碼是不行的!我為什么行?因?yàn)檫@是我寫的封裝類庫(kù)啊~(最后放地址)
思路
Okhttp依賴Okio進(jìn)行了數(shù)據(jù)的讀寫動(dòng)作,我們需要找到Okio進(jìn)行處理。那么,如何加上呢?
Okhttp可以添加Interceptor(攔截器),我們可以通過(guò)攔截器的接口方法,獲取對(duì)應(yīng)的responseBody、requestBody對(duì)象進(jìn)行操作。然后我們就獲取了讀寫相關(guān)的實(shí)現(xiàn)方法。具體實(shí)現(xiàn)是通過(guò)Source、Sink對(duì)象。
Source官方解釋:Supplies a stream of bytes. Use this interface to read data from wherever it's located。
Sink官方解釋:Receives a stream of bytes. Use this interface to write data wherever it's needed。
一句話概括:Source對(duì)象是對(duì)輸入流的包裝(下載讀數(shù)據(jù)),Sink是對(duì)輸出流的包裝(寫數(shù)據(jù)上傳)。
實(shí)現(xiàn)
根據(jù)需要添加下載、上傳Interceptor
//添加下載攔截器(this參數(shù)是實(shí)現(xiàn)下載進(jìn)度接口的對(duì)象)
mDownClient = new OkHttpClient.Builder()
.addNetworkInterceptor(new DownloadInterceptor(this))
.build();
//添加上傳攔截器(this參數(shù)是實(shí)現(xiàn)上傳回調(diào)接口的對(duì)象)
mUploadClient = new OkHttpClient.Builder()
.addNetworkInterceptor(new UploadInterceptor(this))
.build();
攔截器具體實(shí)現(xiàn)
//下載攔截器
public class DownloadInterceptor implements Interceptor {
private OnDownloadListener mListener;
public DownloadInterceptor( OnDownloadListener listener) {
mListener = listener;
}
@Override
public Response intercept(Chain chain) throws IOException {
//封裝ressponse對(duì)象
Response response = wrapResponse(chain.proceed(chain.request()));
return response;
}
private Response wrapResponse(Response response) {
if (response == null || response.body() == null) {
return response;
}
//獲取處理后的response對(duì)象
Response wrapResponse = getWrapResponse(response);
return wrapResponse;
}
private Response getWrapResponse(Response response) {
ProgressInfo info = new ProgressInfo();
info.setTime(System.currentTimeMillis()+"");
info.setUrl(response.request().url().toString());
Response.Builder builder = response.newBuilder();
//封裝responseBody,傳入相關(guān)參數(shù),獲取進(jìn)度數(shù)據(jù)回調(diào)
return builder.body(new WrapResponseBody(response.body(),info,mListener)).build();
}
}
--------------------------------------分割---------------------------------------
//上傳攔截器
public class UploadInterceptor implements Interceptor {
private OnUploadListener mListener;
public UploadInterceptor(OnUploadListener listener) {
mListener = listener;
}
@Override
public Response intercept(Chain chain) throws IOException {
//封裝request對(duì)象
Request request = wrapRequest(chain.request());
Response response = chain.proceed(request);
return response;
}
private Request wrapRequest(Request request) {
if (request == null || request.body() == null) {
return request;
}
Request.Builder builder = request.newBuilder();
ProgressInfo info = new ProgressInfo();
HttpUrl url = request.url();
info.setUrl(url.toString());
info.setTime(System.currentTimeMillis()+"");
//封裝requestBody,傳入?yún)?shù),獲取數(shù)據(jù)進(jìn)度回調(diào)
builder.method(request.method(),new WrapRequestBody(request.body(),info,mListener));
return builder.build();
}
}
responseBody、requestBody相關(guān)實(shí)現(xiàn)
//繼承ResponseBody實(shí)現(xiàn)具體方法
public class WrapResponseBody extends ResponseBody {
private Handler mHandler = new Handler(Looper.getMainLooper());
private ResponseBody mResponseBody;
private OnDownloadListener mListener;
private ProgressInfo mInfo;
private BufferedSource mBufferedSource;
private boolean mDoProgress;
//傳入進(jìn)度,以及監(jiān)聽(tīng)對(duì)象
public WrapResponseBody(ResponseBody responseBody, ProgressInfo info, OnDownloadListener listener) {
mResponseBody = responseBody;
mInfo = info;
mListener = listener;
}
@Nullable
@Override
public MediaType contentType() {
//接口方法,返回類型
return mResponseBody.contentType();
}
@Override
public long contentLength() {
long contentLength = mResponseBody.contentLength();
//gzip壓縮格式會(huì)返回-1,目前處理是在請(qǐng)求頭信息指定("Accept-Encoding","identity")表示不壓縮
if (contentLength == -1) {
mDoProgress = false;
mHandler.post(new Runnable() {
@Override
public void run() {
//切換線程,進(jìn)行失敗回調(diào)
mListener.onDownLoadGetContentLengthFail(mInfo);
}
});
} else {
mDoProgress = true;
}
return contentLength;
}
@Override
public BufferedSource source() {
//WrapSource(繼承ForwardingSource,F(xiàn)orwardingSource實(shí)現(xiàn)了Source接口)
if (mBufferedSource == null) {
mInfo.setContentLength(contentLength());
//傳入?yún)?shù),讀取具體進(jìn)度信息,并回調(diào)
WrapSource wrapSource = new WrapSource(mResponseBody.source(), mInfo, mListener,mDoProgress);
mBufferedSource = Okio.buffer(wrapSource);
}
return mBufferedSource;
}
}
--------------------------------------分割---------------------------------------
//繼承ResquestBody實(shí)現(xiàn)具體方法
public class WrapRequestBody extends RequestBody {
private RequestBody mRequestBody;
private OnUploadListener mListener;
private ProgressInfo mInfo;
private boolean mDoProgress;
private Handler mHandler = new Handler(Looper.getMainLooper());
//傳入進(jìn)度,以及監(jiān)聽(tīng)對(duì)象
public WrapRequestBody(RequestBody requestBody, ProgressInfo info, OnUploadListener listener) {
mRequestBody = requestBody;
mListener = listener;
mInfo = info;
}
@Override
public MediaType contentType() {
//接口方法,返回類型
return mRequestBody.contentType();
}
@Override
public long contentLength() throws IOException {
try {
//上傳內(nèi)容長(zhǎng)度,有異常走failWrok處理
long l = mRequestBody.contentLength();
mDoProgress = true;
return l;
} catch (IOException e) {
e.printStackTrace();
failWork();
return -1;
}
}
//進(jìn)行失敗處理
private void failWork() {
mDoProgress = false;
mHandler.post(new Runnable() {
@Override
public void run() {
//切換線程,回調(diào)失敗信息
mListener.onUploadGetContentLengthFail(mInfo);
}
});
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
mInfo.setContentLength(contentLength());
// WrapSink (繼承ForwardingSink,F(xiàn)orwardingSink實(shí)現(xiàn)了Sink接口)
///傳入?yún)?shù),讀取具體進(jìn)度信息,并回調(diào)
WrapSink wrapSink = new WrapSink(sink, mInfo, mListener, mDoProgress);
BufferedSink buffer = Okio.buffer(wrapSink);
mRequestBody.writeTo(buffer);
buffer.flush();
}
}
WrapSource、WrapSink相關(guān)實(shí)現(xiàn)
//繼承ForwardingSource 實(shí)現(xiàn)具體方法
public class WrapSource extends ForwardingSource {
private Handler mHandler = new Handler(Looper.getMainLooper());
private Source mSource;
private ProgressInfo mInfo;
private OnDownloadListener mListener;
private boolean mDoProgress;
public WrapSource(Source source, ProgressInfo info, OnDownloadListener listener, boolean doProgress) {
//傳入源Source、進(jìn)度信息、監(jiān)聽(tīng)進(jìn)度等信息。
super(source);
mSource = source;
mInfo = info;
mListener = listener;
//傳入是否繼續(xù)執(zhí)行回調(diào)boolean參數(shù),如果之前執(zhí)行有異常,則不再繼續(xù)執(zhí)行回調(diào)
mDoProgress = doProgress;
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
//獲取具體進(jìn)度信息,來(lái)到了熟悉的具體IO
long read = super.read(sink, byteCount);
if (read != -1) {
long l = mInfo.getCurrentLength() + read;
mInfo.setCurrentLength(l);
mHandler.post(new Runnable() {
@Override
public void run() {
if (mDoProgress) {
//切換到主線程,回調(diào)數(shù)據(jù)
mListener.onDownLoadProgress(mInfo);
}
}
});
}
return read;
}
}
--------------------------------------分割---------------------------------------
//繼承ForwardingSink 實(shí)現(xiàn)具體方法
public class WrapSink extends ForwardingSink {
private Handler mHandler = new Handler(Looper.getMainLooper());
public OnUploadListener mListener;
public ProgressInfo mInfo;
public boolean mDoProgress;
public WrapSink(Sink delegate, ProgressInfo info, OnUploadListener listener, boolean doProgress) {
//傳入源Source、進(jìn)度信息、監(jiān)聽(tīng)進(jìn)度等信息。
super(delegate);
mInfo = info;
mListener = listener;
//傳入是否繼續(xù)執(zhí)行回調(diào)boolean參數(shù),如果之前執(zhí)行有異常,則不再繼續(xù)執(zhí)行回調(diào)
mDoProgress = doProgress;
}
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
//獲取具體進(jìn)度信息,來(lái)到了熟悉的具體IO
long l = mInfo.getCurrentLength() + byteCount;
mInfo.setCurrentLength(l);
mHandler.post(new Runnable() {
@Override
public void run() {
if (mDoProgress) {
//切換到主線程,回調(diào)數(shù)據(jù)
mListener.onUpLoadProgress(mInfo);
}
}
});
}
}
總結(jié)
以上就是具體的流程了,按照步驟其實(shí)很簡(jiǎn)單。大家了解下挺好的,我這邊也封裝好了具體的類庫(kù)和Demo,大家可以直接依賴(查看README.md,使用簡(jiǎn)單)。
地址:https://github.com/HoldMyOwn/TNetProgress
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Retrofit之OKHttpCall源碼分析
- Android 封裝Okhttp+Retrofit+RxJava,外加攔截器實(shí)例
- okhttp3.4.1+retrofit2.1.0實(shí)現(xiàn)離線緩存的示例
- OKHttp3(支持Retrofit)的網(wǎng)絡(luò)數(shù)據(jù)緩存Interceptor攔截器的實(shí)現(xiàn)
- RxJava+Retrofit+OkHttp實(shí)現(xiàn)多文件下載之?dāng)帱c(diǎn)續(xù)傳
- RxJava+Retrofit+OkHttp實(shí)現(xiàn)文件上傳
- 深入淺出RxJava+Retrofit+OkHttp網(wǎng)絡(luò)請(qǐng)求
- 淺談RxJava+Retrofit+OkHttp 封裝使用
- Android中Retrofit+OkHttp進(jìn)行HTTP網(wǎng)絡(luò)編程的使用指南
- Retrofit和OkHttp如何實(shí)現(xiàn)Android網(wǎng)絡(luò)緩存
相關(guān)文章
Android系統(tǒng)進(jìn)程間通信Binder機(jī)制在應(yīng)用程序框架層的Java接口源代碼分析
本文主要介紹 Android系統(tǒng)進(jìn)程間通信Binder機(jī)制Java 接口源碼分析,這里詳細(xì)介紹了如何實(shí)現(xiàn)Binder 機(jī)制和Java接口直接的通信,有興趣的小伙伴可以參考下2016-08-08
Android學(xué)習(xí)筆記——Menu介紹(三)
今天繼續(xù)昨天沒(méi)有講完的Menu的學(xué)習(xí),主要是Popup Menu的學(xué)習(xí),需要的朋友可以參考下2014-10-10
Android自定義DigitalClock控件實(shí)現(xiàn)商品倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了Android DigitalClock實(shí)現(xiàn)商品倒計(jì)時(shí),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Android Messenger實(shí)現(xiàn)進(jìn)程間雙向通信
這篇文章主要為大家詳細(xì)介紹了Messenger實(shí)現(xiàn)進(jìn)程間雙向通信,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
Android編程自定義搜索框?qū)崿F(xiàn)方法【附demo源碼下載】
這篇文章主要介紹了Android編程自定義搜索框?qū)崿F(xiàn)方法,涉及Android界面布局、數(shù)據(jù)加載、事件響應(yīng)等相關(guān)操作技巧,并附帶完整demo源碼供讀者下載參考,需要的朋友可以參考下2017-12-12
Android實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07

