OKHttpUtils 分析
前言
本文是对张鸿洋的 OKHttp 辅助类okhttputils简要分析,以便学习如何封装常见工具的思想,建议配合源码食用。
主要涉及类:
- OkHttpUtils
- OkHttpRequestBuilder
- OkHttpRequest
- RequestCall
- Callback
基础
OkHttp是可以用于 Android 和 Java 的 Http 框架,经典的使用分为 3 步:
//1. 创建一个 OkHttpClient 客户端,在这里配置网络超时等全局配置
OkHttpClient okHttpClient = new OkHttpClient();
//2. 创建一个网络请求,每个 Http 访问对应一个 Request,详细配置了访问的 URL,类型,参数等信息
Request request = new Request
.Builder()
.url("https://www.baidu.com")
.build();
//3. 使用 OkHttpClient 客户端创建 Call 并执行该网络请求,分为阻塞和异步两种方式,异步会有对应回调
okHttpClient.newCall(request)
.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
虽然整体的逻辑已经很简单了,但是在实际使用的时候,不可能对每个网络请求都写一次上述代码,所以就需要对齐进行必要的封装以简化网络请求流程。
okhttputils 就做到了这一点,并且将上述第二步常见网络请求的过程也加入链式调用中,使用起来更加连贯:
//1. 全局配置唯一的 OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10000L, TimeUnit.MILLISECONDS)
.readTimeout(10000L, TimeUnit.MILLISECONDS)
.build();
OkHttpUtils.initClient(okHttpClient);
//2.在需要网络请求的时候,执行对应代码
OkHttpUtils.get()
.url("http://www.baidu.com")
.build()
.execute(new com.zhy.http.okhttp.callback.Callback() {
//回调方法
});
其中9~10
行相当于 OKHttp 步骤 2 创建网络请求,11~14
则就是步骤 3 执行网络请求的过程。
每次使用网络请求时只需要选择get
、post
等方法获取并配置相应builder
,然后选择execute
执行即可。
实现分析
那么 okhttputils 是如何实现这一点的呢?
首先看看OkHttpUtils
的结构:
可以看到大体上可以将其分为 3 个部分:
- OkHttpClient 相关
- 网络请求相关信息
- 与具体执行网络请求有关的方法
OkHttpClient 相关
我们先来看第一部分,OkHttpUtils 本质上只是对 OkHttpClient 的方法进行了一次封装,所以其肯定要持有 OkHttpClient 对象,一般来说一个 APP 只需要一个 OkHttpClient 对象即可,所以可以看到 OkHttpUtils 做了双重锁定的单例处理:
public static OkHttpUtils initClient(OkHttpClient okHttpClient)
{
if (mInstance == null)
{
synchronized (OkHttpUtils.class)
{
if (mInstance == null)
{
mInstance = new OkHttpUtils(okHttpClient);
}
}
}
return mInstance;
}
这样我们在第一次使用OkHttpUtils
的时候初始化的OkHttpClient
便会被保存到这里,之后的使用中就不需要再去反复创建了。
此外在OkHttpUtils
的结构中可以注意到有一个mPlatform
的变量,他会根据当前是 Android 还是其他平台的不同被初始化为 Android 主线程或者普通线程池,这个我们在后面回调网络请求状态的时候会用到。
private Platform mPlatform = findPlatform();
private static Platform findPlatform()
{
try
{
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0)
{
return new Android();
}
} catch (ClassNotFoundException ignored)
{
}
return new Platform();
}
网络请求相关信息
有了OkHttpClient
对象之后,下一步便是创建一个适当的网络请求。
在OkHttpUtils
中使用的是OkHttpRequestBuilder <T extends OkHttpRequestBuilder>
的子类来收集、配置相关的一些属性。
在该类中,定义了一系列网络请求基本的参数:
protected String url;
protected Object tag;
protected Map<String, String> headers;
protected Map<String, String> params;
protected int id;
此外还有一个抽象方法,用来创建执行网络请求的RequestCall
。
public abstract RequestCall build();
这个方法在其子类中的实现一般是调用OkHttpRequest
子类的build
方法,可以看到OkHttpRequestBuilder
只是将网络请求的相关参数传递到OkHttpRequest
中。
//com.zhy.http.okhttp.builder.GetBuilder
@Override
public RequestCall build()
{
if (params != null)
{
url = appendParams(url, params);
}
return new GetRequest(url, tag, params, headers,id).build();
}
在OkHttpRequest
中,利用上述的参数可以并通过generateRequest(Callback callback)
方法创建Request
。
//com.zhy.http.okhttp.request.OkHttpRequest
protected OkHttpRequest(String url, Object tag,
Map<String, String> params, Map<String, String> headers,int id)
{
this.url = url;
this.tag = tag;
this.params = params;
this.headers = headers;
this.id = id ;
if (url == null)
{
Exceptions.illegalArgument("url can not be null.");
}
initBuilder();//初始化 okhttp3.Request.Builder 用于生成 Request
}
public Request generateRequest(Callback callback)
{
RequestBody requestBody = buildRequestBody();
RequestBody wrappedRequestBody = wrapRequestBody(requestBody, callback);//用于更新下载进度等,为 okhttp3.Callback 增加更多功能
Request request = buildRequest(wrappedRequestBody);//在子类中使用 okhttp3.Request.Builder 对象生成对应的 Request
return request;
}
这里的抽象方法wrapRequestBody()
,buildRequest()
的实现,也是OkHttpRequest
各个子类主要的不同点。
Callback
是在okhttp3.Callback
的基础上增加了 before,progress 和对请求结果的处理等的回调。
OkHttpRequest
类的build
方法则只是将其自身传递给okhttp3.Call
的封装类RequestCall
,创建并返回该类的对象:
//com.zhy.http.okhttp.request.OkHttpRequest
public RequestCall build()
{
return new RequestCall(this);
}
执行网络请求
RequestCall
类则是对okhttp3.Call
类的进一步封装,对外提供更多的接口:开始、取消网络请求cancel()
,readTimeOut()
…等接口。
当执行RequestCall
的execute
方法时:
//com.zhy.http.okhttp.request.RequestCall
public void execute(Callback callback)
{
buildCall(callback);//创建 okhttp3.Call 对象,其所用的 Request 对象来自于 okHttpRequest.generateRequest(callback)
if (callback != null)
{
callback.onBefore(request, getOkHttpRequest().getId());
}
OkHttpUtils.getInstance().execute(this, callback);
}
可以看其最后只是将RequestCall
和callback
传递给了OkHttpUtils
类的execute
方法,也就是说,最终还是调用了okhttp3.Call
的enqueue()
方法,在这里执行了真正的网络请求:
//com.zhy.http.okhttp.OkHttpUtils
public void execute(final RequestCall requestCall, Callback callback)
{
if (callback == null)
callback = Callback.CALLBACK_DEFAULT;
final Callback finalCallback = callback;
final int id = requestCall.getOkHttpRequest().getId();
requestCall.getCall().enqueue(new okhttp3.Callback()
{
@Override
public void onFailure(Call call, final IOException e)
{
sendFailResultCallback(call, e, finalCallback, id);
}
@Override
public void onResponse(final Call call, final Response response)
{
try
{
if (call.isCanceled())
{
sendFailResultCallback(call, new IOException("Canceled!"), finalCallback, id);
return;
}
if (!finalCallback.validateReponse(response, id))
{
sendFailResultCallback(call, new IOException("request failed , reponse's code is : " + response.code()), finalCallback, id);
return;
}
Object o = finalCallback.parseNetworkResponse(response, id);
sendSuccessResultCallback(o, finalCallback, id);
} catch (Exception e)
{
sendFailResultCallback(call, e, finalCallback, id);
} finally
{
if (response.body() != null)
response.body().close();
}
}
});
}
而网络请求的回调,则是在本文最开始的mPlatform
提供的线程中进行。这样保证了在 Android 中,onBefore
、onAfter
、inProgress
等回调能够在 UI 线程进行。
public void sendSuccessResultCallback(final Object object, final Callback callback, final int id)
{
if (callback == null) return;
mPlatform.execute(new Runnable()
{
@Override
public void run()
{
callback.onResponse(object, id);
callback.onAfter(id);
}
});
}
总结
在本文中,okhttputils
将初始化OkHttpClient
的动作提取出来,这样同一个应用只需要在最开始的时候配置一下诸如网络超时、cookie 等既可。
在具体的实现中,通过OkHttpRequestBuilder
收集网络请求的属性并传递给OkHttpRequest
,在其子类中按照不同的需要实现生成Request
的方法。
OkHttpRequestBuilder
的build()
方法会生成RequestCall
对象,RequestCall
对象的execute()
方法会调用OkHttpRequestBuilder
对象的generateRequest()
方法产生Request
,并据此产生Call
对象,最后通过该Call
对象的 enqueue 方法执行网络请求。