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 方法执行网络请求。
