本文使用的OkHttp版本是okhttp3,使用okhttp2的情況稍有不同擅腰,使用okhttp2的同學(xué)替換為okhttp3也不麻煩蟋恬,API都很接近;當(dāng)然趁冈,如果想要使用okhttp2使用stetho & 攔截器也是可以的歼争,可以參考官方文檔。
先貼出Gradle依賴渗勘,基本上我使用的都是最新版本的庫(kù)沐绒。
def okhttp3Version = '3.4.1'
compile('com.squareup.okhttp3:okhttp:' + okhttp3Version)
compile('com.squareup.okhttp3:logging-interceptor:' + okhttp3Version)
testCompile('com.squareup.okhttp3:mockwebserver:' + okhttp3Version)
compile 'com.facebook.stetho:stetho-okhttp3:1.3.1'
1. 定義一個(gè)全局的OkHttp請(qǐng)求單例類,全局統(tǒng)一使用單一OkHttpClient:
/**
* 全局統(tǒng)一使用的OkHttpClient工具旺坠,okhttp版本:okhttp3
*/
public class OkHttpUtils {
public static final long DEFAULT_READ_TIMEOUT_MILLIS = 15 * 1000;
public static final long DEFAULT_WRITE_TIMEOUT_MILLIS = 20 * 1000;
public static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 20 * 1000;
private static final long HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;
private static volatile OkHttpUtils sInstance;
private OkHttpClient mOkHttpClient;
private OkHttpUtils() {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
//包含header乔遮、body數(shù)據(jù)
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
//FaceBook 網(wǎng)絡(luò)調(diào)試器,可在Chrome調(diào)試網(wǎng)絡(luò)請(qǐng)求取刃,查看SharePreferences,數(shù)據(jù)庫(kù)等
.addNetworkInterceptor(new StethoInterceptor())
//http數(shù)據(jù)log蹋肮,日志中打印出HTTP請(qǐng)求&響應(yīng)數(shù)據(jù)
.addInterceptor(loggingInterceptor)
.build();
}
public static OkHttpUtils getInstance() {
if (sInstance == null) {
synchronized (OkHttpUtils.class) {
if (sInstance == null) {
sInstance = new OkHttpUtils();
}
}
}
return sInstance;
}
public OkHttpClient getOkHttpClient() {
return mOkHttpClient;
}
public void setCache(Context appContext) {
final File baseDir = appContext.getApplicationContext().getCacheDir();
if (baseDir != null) {
final File cacheDir = new File(baseDir, "HttpResponseCache");
mOkHttpClient.newBuilder().cache((new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE)));
}
}
}
在所有需要用到OkHttpClient的地方使用以下代碼,全局使用同一個(gè)OkHttpClient實(shí)例:
OkHttpClient okHttpClient = OkHttpUtils.getInstance().getOkHttpClient();
全局使用一個(gè)OkHttpClient的原因是所有請(qǐng)求沒(méi)必要?jiǎng)?chuàng)建多個(gè)請(qǐng)求客戶端實(shí)例璧疗,一個(gè)好處是節(jié)省內(nèi)存坯辩,另外一個(gè)是全局設(shè)定了一些監(jiān)控工具,如Facebook的Stetho 和 OkHttp自帶的HttpLoggingInterceptor病毡,你就可以監(jiān)管你的所有Http請(qǐng)求濒翻。
2. HttpLoggingInterceptor Log直接觀察HTTP請(qǐng)求&響應(yīng)數(shù)據(jù)
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
//包含header、body數(shù)據(jù)
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//在build OkHttpClient的時(shí)候加入Log攔截器
OkHttpClient.Builder().addInterceptor(loggingInterceptor)
HttpLoggingInterceptor
的效果如下圖,開發(fā)時(shí)如果需要看整個(gè)App的所有請(qǐng)求及相應(yīng)有送,可以使用okhttp關(guān)鍵字過(guò)濾淌喻。
在實(shí)例化你的HttpLoggingInterceptor的時(shí)候傳入一個(gè)Logger參數(shù),可以定制化OkHttp輸出的格式化http請(qǐng)求體&響應(yīng)體log雀摘。
3. Facebook強(qiáng)大的監(jiān)測(cè)工具:Stetho
3.1 在build OkHttpClient時(shí)需要添加網(wǎng)絡(luò)攔截器
OkHttpClient.Builder().addNetworkInterceptor(new StethoInterceptor())
3.2 在Application的OnCreate中初始化
//FaceBook調(diào)試器,可在Chrome調(diào)試網(wǎng)絡(luò)請(qǐng)求,查看SharePreferences,數(shù)據(jù)庫(kù)等
Stetho.initializeWithDefaults(this);
3.3 連接手機(jī)裸删,在Chrome中打開<a>chome://inspect/#devices</a>
看到如下界面,則代表監(jiān)測(cè)成功阵赠,如果沒(méi)有App顯示涯塔,那應(yīng)該就是忘了在Application中初始化Stetho
3.4 查看網(wǎng)絡(luò)請(qǐng)求
3.5查看數(shù)據(jù)庫(kù)
查看SharePreferences也同理,點(diǎn)擊Local Storage就是你的App的所有SharePreferences清蚀。
4. 自定義OkHttp攔截器
當(dāng)你與服務(wù)端對(duì)接的時(shí)候匕荸,當(dāng)服務(wù)端功能還沒(méi)開發(fā)完成,而你等待著服務(wù)器的接口測(cè)試時(shí)枷邪,自定義攔截器就可以幫你無(wú)需等待服務(wù)端完成功能先進(jìn)行開發(fā)榛搔。
步驟:
1)先與服務(wù)端協(xié)商接口返回?cái)?shù)據(jù)格式,拿到協(xié)商的數(shù)據(jù)东揣,寫一些假數(shù)據(jù)践惑,每個(gè)接口對(duì)應(yīng)建立一個(gè)JSON文本文件,里面放接口定義的數(shù)據(jù)嘶卧。
2)自定義攔截器
/** * 自定義okhttp攔截器,可定制接口偽造Http響應(yīng)數(shù)據(jù) */
public final class MockDataApiInterceptor implements Interceptor {
public static final String TAG = MockDataApiInterceptor.class.getSimpleName();
@Override
public Response intercept(Chain chain) throws IOException {
Response response = null;
String path = chain.request().url().uri().getPath();
LogUtil.d(TAG, "intercept: path=" + path);
response = interceptRequestWhenDebug(chain, path);
if (null == response) {
LogUtil.i(TAG, "intercept: null == response");
response = chain.proceed(chain.request());
} return response;
}
/**
* 測(cè)試環(huán)境下攔截需要的接口請(qǐng)求芥吟,偽造數(shù)據(jù)返回
*
* @param chain 攔截器鏈
* @param path 請(qǐng)求的路徑path
* @return 偽造的請(qǐng)求Response侦铜,有可能為null
*/
private Response interceptRequestWhenDebug(Chain chain, String path) {
Response response = null;
if (BuildConfig.DEBUG) {
Request request = chain.request();
if (path.equalsIgnoreCase("/api/event")) {
//活動(dòng)列表接口
response = getMockEventListResponse(request);
} else if (path.startsWith("/api/event/")) {
//活動(dòng)詳情接口
response = getMockEventDetailResponse(request);
}
}
return response;
}
/**
* 偽造活動(dòng)詳情接口響應(yīng)
*
* @param request 用戶的請(qǐng)求
* @return 偽造的活動(dòng)詳情HTTP響應(yīng)
*/
private Response getMockEventDetailResponse(Request request) {
Response response;
String data = MockDataGenerator.getMockDataFromJsonFile("mock/EventDetail.json");
response = getHttpSuccessResponse(request, data);
return response;
}
/**
* 偽造活動(dòng)列表接口響應(yīng)
*
* @param request 用戶的請(qǐng)求
* @return 偽造的活動(dòng)列表HTTP響應(yīng)
*/
private Response getMockEventListResponse(Request request) {
Response response;
String data = MockDataGenerator.getMockDataFromJsonFile("mock/EventList.json");
response = getHttpSuccessResponse(request, data);
return response;
}
/**
* 根據(jù)數(shù)據(jù)JSON字符串構(gòu)造HTTP響應(yīng),在JSON數(shù)據(jù)不為空的情況下返回200響應(yīng)运沦,否則返回500響應(yīng)
*
* @param request 用戶的請(qǐng)求
* @param dataJson 響應(yīng)數(shù)據(jù)泵额,JSON格式
* @return 構(gòu)造的HTTP響應(yīng)
*/
private Response getHttpSuccessResponse(Request request, String dataJson) {
Response response;
if (TextUtils.isEmpty(dataJson)) {
LogUtil.w(TAG, "getHttpSuccessResponse: dataJson is empty!");
response = new Response.Builder()
.code(500)
.protocol(Protocol.HTTP_1_0)
.request(request)
//必須設(shè)置protocol&request,否則會(huì)拋出異常
.build();
} else {
response = new Response.Builder()
.code(200)
.message(dataJson)
.request(request)
.protocol(Protocol.HTTP_1_0)
.addHeader("Content-Type", "application/json")
.body(ResponseBody.create(MediaType.parse("application/json"), dataJson)) .build();
}
return response;
}
private Response getHttpFailedResponse(Chain chain, int errorCode, String errorMsg) {
if (errorCode < 0) {
throw new IllegalArgumentException("httpCode must not be negative");
}
Response response;
response = new Response.Builder()
.code(errorCode)
.message(errorMsg)
.request(chain.request())
.protocol(Protocol.HTTP_1_0)
.build();
return response;
}
}
接口返回什么數(shù)據(jù)都由你自己定義携添,即使是Http的響應(yīng)碼等嫁盲。這里的響應(yīng)body我們使用服務(wù)端同學(xué)給我們提供的JSON文件
3)在build OkHttpClient時(shí)添加自定義攔截器
OkHttpClient.Builder().addInterceptor(new MockDataApiInterceptor())
4)進(jìn)行開發(fā),完善數(shù)據(jù)解析邏輯等烈掠,與View結(jié)合等羞秤。
5. OkHttp與Retrofit/RxJava組合請(qǐng)求
準(zhǔn)備寫一篇文章詳細(xì)講