簡介
OkHttp3
1)一個(gè)處理網(wǎng)絡(luò)請求的開源項(xiàng)目,它是基于Socket(java socket api)直接封裝的用于網(wǎng)絡(luò)請求的輕量級框架。
2)優(yōu)點(diǎn)
① 允許連接到同一個(gè)主機(jī)地址的所有請求,提高請求效率
② 共享Socket,減少對服務(wù)器的請求次數(shù)
③ 通過連接池,減少了請求延遲
④ 緩存響應(yīng)數(shù)據(jù)來減少重復(fù)的網(wǎng)絡(luò)請求
⑤ 減少了對數(shù)據(jù)流量的消耗
⑥ 自動(dòng)處理GZip壓縮
3)功能
① get,post請求
② 文件的上傳下載
③ 加載圖片(內(nèi)部會圖片大小自動(dòng)壓縮)
④ 支持請求回調(diào),直接返回對象、對象集合
⑤ 支持session的保持
4)Gradle dependencies
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
5)鏈接
官網(wǎng):http://square.github.io/okhttp/
GitHub:https://github.com/square/okhttp
Gson
1)Gson是Google提供的用來在Java對象和JSON數(shù)據(jù)之間進(jìn)行映射的Java類庫。可以將一個(gè)Json字符轉(zhuǎn)成一個(gè)Java對象,或者將一個(gè)Java轉(zhuǎn)化為Json字符串。
2)Gradle dependencies
implementation 'com.google.code.gson:gson:2.8.2'
TypeBuilder
1)一個(gè)用于生成泛型的簡易Builder
2)優(yōu)點(diǎn)
當(dāng)你無法通過子類去獲取泛型時(shí),你就可以使用TypeBuilder,不用寫一長串的嵌套或去實(shí)現(xiàn)部分Type接口。
3)Gradle dependencies
implementation 'com.github.ikidou:TypeBuilder:1.0'
4)鏈接
GitHub:https://github.com/ikidou/TypeBuilder
使用和封裝
1.創(chuàng)建OkHttpClient對象,并且配置支持https,添加攔截器(Interceptor)
/**
* 初始化:為OkHttpClient配置參數(shù)
*/
public static void initConfig(Map<String, String> headers) {
/****** 配置基本參數(shù) ******/
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
//設(shè)置連接超時(shí)時(shí)間
okHttpClientBuilder.connectTimeout(TIME_OUT, TimeUnit.SECONDS);
//設(shè)置寫操作超時(shí)時(shí)間
okHttpClientBuilder.writeTimeout(TIME_OUT, TimeUnit.SECONDS);
//設(shè)置讀操作超時(shí)時(shí)間
okHttpClientBuilder.readTimeout(TIME_OUT, TimeUnit.SECONDS);
//設(shè)置重定向
okHttpClientBuilder.followRedirects(true);
/****** 添加請求頭攔截器 ******/
okHttpClientBuilder.addInterceptor(new HeaderInterceptor(headers));
/****** 添加日志顯示攔截器 ******/
if (BuildConfig.isDebug()) {
okHttpClientBuilder.addInterceptor(new LoggerInterceptor());
}
/****** 添加https支持 ******/
okHttpClientBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
//信任所有Https
okHttpClientBuilder.sslSocketFactory(HttpsUtils.initSSLSocketFactory(), HttpsUtils.initTrustManager());
mOkHttpClient = okHttpClientBuilder.build();
}
2.構(gòu)建請求
1)封裝請求參數(shù)
請求參數(shù)分為三種:
① 字符串
② JSONObject
③ 文件
RequestParams中使用ConcurrentHashMap維護(hù)請求參數(shù)
public ConcurrentHashMap<String, String> urlParams = new ConcurrentHashMap<String, String>();
public ConcurrentHashMap<String, Object> fileParams = new ConcurrentHashMap<String, Object>();
public Object jsonParam = null;
以 鍵/值 對的形式設(shè)置請求參數(shù):
/**
* Adds key/value string pair to the request.
*
* @param key the key name for the new param.
* @param value the value string for new param.
*/
public void put(String key, String value) {
if (key != null && value != null) {
urlParams.put(key, value);
}
}
/**
* Adds key/value string with Object pair to the request.
*
* @param key the key name for the new param.
* @param object the value object for new param.
* @throws FileNotFoundException when the file not find throw a exception.
*/
public void put(String key, Object object) throws FileNotFoundException{
if (key != null) {
fileParams.put(key, object);
}
}
/**
* Adds object pair to the request.
*
* @param object
*/
public void put(Object object) {
this.jsonParam = object;
}
2)構(gòu)建Get、Post、文件上傳請求
BuildRequest.createGetRequest(...)
BuildRequest.createPostRequest(...)
BuildRequest.createMultiPostRequest(...)
① 創(chuàng)建Get請求
public static Request createGetRequest(String url, RequestParams params) {
StringBuilder urlBuilder = new StringBuilder(url).append("?");
if (params != null) {
for (Map.Entry<String, String> entry : params.urlParams.entrySet()) {
//將請求參數(shù)添加到請求體中
urlBuilder.append(entry.getKey())
.append("=")
.append(entry.getValue())
.append("&");
}
}
return new Request.Builder()
.url(urlBuilder.substring(0, urlBuilder.length() - 1))
.get()
.build();
}
② 創(chuàng)建Post請求
public static Request createPostRequest(String url, RequestParams params) {
Request request = null;
if (params != null) {
if (params.isJsonParam()) {
if (params.jsonParam instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) params.jsonParam;
RequestBody body = RequestBody.create(JSON_TYPE, jsonObject.toString());
request = new Request.Builder().url(url).post(body).build();
} else {
throw new IllegalArgumentException("RequestParams.jsonParam must a JsonObject");
}
} else {
FormBody.Builder bodyBuilder = new FormBody.Builder();
for (Map.Entry<String, String> entry : params.urlParams.entrySet()) {
//將請求參數(shù)添加到請求體中
bodyBuilder.add(entry.getKey(), entry.getValue());
}
FormBody body = bodyBuilder.build();
request = new Request.Builder().url(url).post(body).build();
}
} else {
//如果請求參數(shù)為空時(shí),當(dāng)成get請求處理
request = new Request.Builder().url(url).get().build();
}
return request;
}
③ 創(chuàng)建文件上傳請求
public static Request createMultiPostRequest(String url, RequestParams params) {
MultipartBody.Builder requestBody = new MultipartBody.Builder();
requestBody.setType(MultipartBody.FORM);
if (params != null) {
for (Map.Entry<String, Object> entry : params.fileParams.entrySet()) {
if (entry.getValue() instanceof File) {
String key = entry.getKey();
File file = (File) entry.getValue();
requestBody.addFormDataPart(key, file.getName(), RequestBody.create(FILE_TYPE, file));
} else if (entry.getValue() instanceof String) {
requestBody.addFormDataPart(entry.getKey(), entry.getKey() , RequestBody.create(DEFAULT_TYPE, (String) entry.getValue()));
}
}
}
MultipartBody body = requestBody.build();
return new Request.Builder()
.url(url)
.post(body)
.build();
}
3.應(yīng)用OkHttpClient發(fā)送http/https請求
/**
* 發(fā)送http/https請求
*
* @param request
* @param callback
* @return
*/
public static Call sendRequest(Request request, Callback callback) {
Call call = mOkHttpClient.newCall(request);
call.enqueue(callback);
return call;
}
1)請求響應(yīng)回調(diào)Callback,OkHttp源碼注釋說明:

也就是對HTTP請求響應(yīng)的包裝
2)當(dāng)請求返回?cái)?shù)據(jù)是Json格式時(shí),自定義數(shù)據(jù)處理類,實(shí)現(xiàn)Callback接口,將Json數(shù)據(jù)轉(zhuǎn)化成對象或者對象集合返回
public class JsonCallback implements Callback {
protected final String RESULT_CODE = "error_code"; // 有返回則對于http請求來說是成功的,但還有可能是業(yè)務(wù)邏輯上的錯(cuò)誤
protected final int RESULT_CODE_VALUE = 0;
protected final String ERROR_MSG = "error_msg";
protected final String EMPTY_MSG = "";
protected final int NETWORK_ERROR = -1; //網(wǎng)絡(luò)錯(cuò)誤
protected final int JSON_ERROR = -2; //JSON數(shù)據(jù)相關(guān)的錯(cuò)誤
protected final int OTHER_ERROR = -3; //未知的錯(cuò)誤
private Handler mHandler;
private ProcessorListener mListener;
private Class<?> mClass;
public JsonCallback(DataProcessor dataProcessor) {
this.mListener = dataProcessor.listener;
this.mClass = dataProcessor.cls;
this.mHandler = new Handler(Looper.getMainLooper());
}
@Override
public void onFailure(final Call call, final IOException e) {
//此時(shí)還是在非UI線程,需要通過Handler轉(zhuǎn)發(fā)
mHandler.post(new Runnable() {
@Override
public void run() {
if (mListener != null) {
mListener.onFailure(new OkHttpException(NETWORK_ERROR, e));
}
}
});
}
@Override
public void onResponse(final Call call, final Response response) throws IOException {
final Object resultObj = handleResponse(response);
mHandler.post(new Runnable() {
@Override
public void run() {
if (resultObj == null) {
if (mListener != null) {
mListener.onFailure(new OkHttpException(NETWORK_ERROR, EMPTY_MSG));
}
} else {
if (mListener != null) {
mListener.onSuccess(resultObj);
}
}
}
});
}
/**
* 處理返回結(jié)果,解析JSON數(shù)據(jù)
* <p>此時(shí)是在異步線程(子線程)中</p>
*
* @param response
*/
private Object handleResponse(Response response) {
if (response == null) {
return null;
}
Object obj = null;
try {
final String result = response.body().string();
if (BuildConfig.isDebug()) {
android.util.Log.w("Debug mode" , "result = " + result);
}
if (mClass == null) {
obj = result;
} else {
//將json轉(zhuǎn)換成實(shí)體類對象或?qū)ο蠹? obj = JsonHelper.parseJSON(result, mClass);
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
3)當(dāng)請求返回?cái)?shù)據(jù)是XML格式時(shí),自定義數(shù)據(jù)處理類,實(shí)現(xiàn)Callback接口,解析XML數(shù)據(jù)轉(zhuǎn)化成對象或者對象集合返回。
public class XmlCallback implements Callback {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//關(guān)于Xml數(shù)據(jù)解析此處就不講了,很簡單
}
}
4)文件上傳請求響應(yīng),自定義數(shù)據(jù)處理類,實(shí)現(xiàn)Callback接口
public class FileUploadCallback implements Callback {
protected final int NETWORK_ERROR = -1; //網(wǎng)絡(luò)錯(cuò)誤
private Handler mHandler;
private ProcessorListener mListener;
public FileUploadCallback(DataProcessor dataProcessor) {
this.mListener = dataProcessor.listener;
mHandler = new Handler(Looper.getMainLooper());
}
@Override
public void onFailure(Call call, final IOException e) {
// 此處省略,具體代碼見GitHub
......
}
@Override
public void onResponse(Call call, Response response) throws IOException {
......
}
}
5)文件下載請求響應(yīng),自定義數(shù)據(jù)處理類,實(shí)現(xiàn)Callback接口
public class FileDownloadCallback implements Callback {
private final int NETWORK_ERROR = -1; //網(wǎng)絡(luò)錯(cuò)誤
private final String EMPTY_MSG = "";
private final int MESSAGE_DOWNLOAD_PROGRESS = 1;
private String mFilePath;
private int mProgress;
private Handler mHandler;
private DownloadListener mListener;
public FileDownloadCallback(DataProcessor dataProcessor) {
// 此處省略,具體代碼見GitHub
......
}
@Override
public void onFailure(Call call, final IOException e) {
......
}
@Override
public void onResponse(Call call, Response response) throws IOException {
......
}
}
4.添加攔截器
Interceptor 攔截器
攔截器是OkHttp中提供一種強(qiáng)大機(jī)制,它可以實(shí)現(xiàn)網(wǎng)絡(luò)監(jiān)聽、請求以及響應(yīng)重寫、請求失敗重試等功能。在沒有本地緩存的情況下,每個(gè)攔截器都必須至少調(diào)用chain.proceed(request)一次,這個(gè)簡單的方法實(shí)現(xiàn)了Http請求的發(fā)起以及從服務(wù)端獲取響應(yīng)。
OkHttp內(nèi)部維護(hù)了兩個(gè)攔截器集合(List),一個(gè)interceptors(Application Interceptors)和networkInterceptors(Network Interceptors),通過遍歷集合依次調(diào)用每一個(gè)攔截器。
攔截器可以進(jìn)行鏈?zhǔn)教幚?,假如你同時(shí)有一個(gè)壓縮數(shù)據(jù)和校驗(yàn)數(shù)據(jù)的攔截器,你可以決定將請求或者響應(yīng)數(shù)據(jù)先進(jìn)行壓縮還是先校驗(yàn)大小。
關(guān)于兩種攔截器Application Interceptors 和 Network Interceptors,可以查閱官方說明:https://github.com/square/okhttp/wiki/Interceptors
這里我用到的是Application Interceptors如下

1)添加統(tǒng)一固定請求頭的攔截器
public class HeaderInterceptor implements Interceptor{
private Map<String, String> headerMap;
public HeaderInterceptor() {
this(null);
}
public HeaderInterceptor(Map<String, String> headers) {
this.headerMap = headers;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
// 設(shè)置"User-Agent"
builder.addHeader("User-Agent", getUserAgent());
/**
* addHeader("client", "android")
* addHeader("version", "1.0.0")
*/
if (headerMap != null && !headerMap.isEmpty()) {
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
builder.addHeader(entry.getKey(), entry.getValue());
}
}
Request request = builder.build();
return chain.proceed(request);
}
......
}
2)日志攔截器
public class LoggerInterceptor implements Interceptor{
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String method = request.method();
String parameter = "null";
if ("POST".equals(method)) {
RequestBody requestBody = request.body();
StringBuilder sb = new StringBuilder();
if (requestBody instanceof FormBody) {
FormBody body = (FormBody) requestBody;
int size = body.size();
for (int i = 0; i < size; i++) {
//sb.append(body.encodedName(i) + "=" + body.encodedValue(i) + ",");//進(jìn)行了編碼處理
sb.append(body.name(i) + "=" + body.value(i) + ",");
}
sb.delete(sb.length() - 1, sb.length());
parameter = "RequestParams:{" + sb.toString() + "}";
}
}
String log = String.format("Sending request %s on %s%n%s%s", request.url(), chain.connection(),
request.headers(), parameter);
Log.w("LoggerInterceptor", "log = " + log);
return chain.proceed(request);
}
}
5.對外提供請求接口
1)Get請求
RequestManager.get(...)
public static void get(String url, RequestParams params, ProcessorListener dataListener, Class<?> clazz) {
checkInit();
XOkHttpClient.sendRequest(BuildRequest.createGetRequest(url, params),
new JsonCallback(new DataProcessor(dataListener, clazz)));
}
2)Post請求
RequestManager.post(...)
public static void post(String url, RequestParams params, ProcessorListener dataListener, Class<?> clazz) {
checkInit();
XOkHttpClient.sendRequest(BuildRequest.createPostRequest(url, params),
new JsonCallback(new DataProcessor(dataListener, clazz)));
}
3)文件上傳請求
RequestManager.uploadFile(...)
public static void uploadFile(String url, RequestParams params, ProcessorListener dataListener) {
checkInit();
XOkHttpClient.sendRequest(BuildRequest.createMultiPostRequest(url, params),
new FileUploadCallback(new DataProcessor(dataListener)));
}
4)文件下載請求
RequestManager.downloadFile(...)
public static void downloadFile(String url, RequestParams params, ProcessorListener dataListener, String savePath) {
checkInit();
//文件下載用GET請求
XOkHttpClient.sendRequest(BuildRequest.createGetRequest(url, params),
new FileDownloadCallback(new DataProcessor(dataListener, savePath)));
}
6.應(yīng)到項(xiàng)目中具體步驟
1)初始化OkHttp,在Application的onCreate方法中調(diào)用RequestManager.init(...)
Map<String, String> headers = new HashMap<String, String>();
headers.put("client", "android");
headers.put("version", "1.0.0");
RequestManager.init(headers, CommonUtils.isDebugMode());
2)發(fā)起網(wǎng)絡(luò)請求

向服務(wù)器發(fā)送請求獲取用戶信息:
public static void sendUserInfo(String userId, final ISenderCallback<User> callback) {
String url = RequestUrl.USER_INFO_URL;
RequestParams params = new RequestParams();
params.put("userId", userId);
RequestManager.post(url, params, new ProcessorListener() {
@Override
public void onSuccess(Object responseObj) {
Object obj = ((Response) responseObj).data;
if (callback != null) {
callback.onSuccess((User) obj);
}
}
@Override
public void onFailure(OkHttpException e) {
if (callback != null) {
callback.onFailure(String.valueOf(e.getEMsg()));
}
}
}, User.class);
}