简介
OkHttp3
1)一个处理网络请求的开源项目,它是基于Socket(java socket api)直接封装的用于网络请求的轻量级框架。
2)优点
① 允许连接到同一个主机地址的所有请求,提高请求效率
② 共享Socket,减少对服务器的请求次数
③ 通过连接池,减少了请求延迟
④ 缓存响应数据来减少重复的网络请求
⑤ 减少了对数据流量的消耗
⑥ 自动处理GZip压缩
3)功能
① get,post请求
② 文件的上传下载
③ 加载图片(内部会图片大小自动压缩)
④ 支持请求回调,直接返回对象、对象集合
⑤ 支持session的保持
4)Gradle dependencies
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
5)链接
官网:http://square.github.io/okhttp/
GitHub:https://github.com/square/okhttp
Gson
1)Gson是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Java转化为Json字符串。
2)Gradle dependencies
implementation 'com.google.code.gson:gson:2.8.2'
TypeBuilder
1)一个用于生成泛型的简易Builder
2)优点
当你无法通过子类去获取泛型时,你就可以使用TypeBuilder,不用写一长串的嵌套或去实现部分Type接口。
3)Gradle dependencies
implementation 'com.github.ikidou:TypeBuilder:1.0'
4)链接
GitHub:https://github.com/ikidou/TypeBuilder
使用和封装
1.创建OkHttpClient对象,并且配置支持https,添加拦截器(Interceptor)
/**
* 初始化:为OkHttpClient配置参数
*/
public static void initConfig(Map<String, String> headers) {
/****** 配置基本参数 ******/
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
//设置连接超时时间
okHttpClientBuilder.connectTimeout(TIME_OUT, TimeUnit.SECONDS);
//设置写操作超时时间
okHttpClientBuilder.writeTimeout(TIME_OUT, TimeUnit.SECONDS);
//设置读操作超时时间
okHttpClientBuilder.readTimeout(TIME_OUT, TimeUnit.SECONDS);
//设置重定向
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.构建请求
1)封装请求参数
请求参数分为三种:
① 字符串
② JSONObject
③ 文件
RequestParams中使用ConcurrentHashMap维护请求参数
public ConcurrentHashMap<String, String> urlParams = new ConcurrentHashMap<String, String>();
public ConcurrentHashMap<String, Object> fileParams = new ConcurrentHashMap<String, Object>();
public Object jsonParam = null;
以 键/值 对的形式设置请求参数:
/**
* 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)构建Get、Post、文件上传请求
BuildRequest.createGetRequest(...)
BuildRequest.createPostRequest(...)
BuildRequest.createMultiPostRequest(...)
① 创建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()) {
//将请求参数添加到请求体中
urlBuilder.append(entry.getKey())
.append("=")
.append(entry.getValue())
.append("&");
}
}
return new Request.Builder()
.url(urlBuilder.substring(0, urlBuilder.length() - 1))
.get()
.build();
}
② 创建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()) {
//将请求参数添加到请求体中
bodyBuilder.add(entry.getKey(), entry.getValue());
}
FormBody body = bodyBuilder.build();
request = new Request.Builder().url(url).post(body).build();
}
} else {
//如果请求参数为空时,当成get请求处理
request = new Request.Builder().url(url).get().build();
}
return request;
}
③ 创建文件上传请求
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.应用OkHttpClient发送http/https请求
/**
* 发送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)请求响应回调Callback,OkHttp源码注释说明:
也就是对HTTP请求响应的包装
2)当请求返回数据是Json格式时,自定义数据处理类,实现Callback接口,将Json数据转化成对象或者对象集合返回
public class JsonCallback implements Callback {
protected final String RESULT_CODE = "error_code"; // 有返回则对于http请求来说是成功的,但还有可能是业务逻辑上的错误
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; //网络错误
protected final int JSON_ERROR = -2; //JSON数据相关的错误
protected final int OTHER_ERROR = -3; //未知的错误
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) {
//此时还是在非UI线程,需要通过Handler转发
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);
}
}
}
});
}
/**
* 处理返回结果,解析JSON数据
* <p>此时是在异步线程(子线程)中</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转换成实体类对象或对象集合
obj = JsonHelper.parseJSON(result, mClass);
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
3)当请求返回数据是XML格式时,自定义数据处理类,实现Callback接口,解析XML数据转化成对象或者对象集合返回。
public class XmlCallback implements Callback {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//关于Xml数据解析此处就不讲了,很简单
}
}
4)文件上传请求响应,自定义数据处理类,实现Callback接口
public class FileUploadCallback implements Callback {
protected final int NETWORK_ERROR = -1; //网络错误
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)文件下载请求响应,自定义数据处理类,实现Callback接口
public class FileDownloadCallback implements Callback {
private final int NETWORK_ERROR = -1; //网络错误
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中提供一种强大机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能。在没有本地缓存的情况下,每个拦截器都必须至少调用chain.proceed(request)一次,这个简单的方法实现了Http请求的发起以及从服务端获取响应。
OkHttp内部维护了两个拦截器集合(List),一个interceptors(Application Interceptors)和networkInterceptors(Network Interceptors),通过遍历集合依次调用每一个拦截器。
拦截器可以进行链式处理,假如你同时有一个压缩数据和校验数据的拦截器,你可以决定将请求或者响应数据先进行压缩还是先校验大小。
关于两种拦截器Application Interceptors 和 Network Interceptors,可以查阅官方说明:https://github.com/square/okhttp/wiki/Interceptors
这里我用到的是Application Interceptors如下
1)添加统一固定请求头的拦截器
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();
// 设置"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) + ",");//进行了编码处理
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.应到项目中具体步骤
1)初始化OkHttp,在Application的onCreate方法中调用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)发起网络请求
向服务器发送请求获取用户信息:
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);
}