OkHttpUtils 源码解析

接上篇的 OkHttp 源码解析,目前项目中更多的用到的是 OkHttpUtilsOkHttp 所以有必要了解它的原理,以便遇到网络相关的问题时,可以及时的定位并解决问题,关于 OkHttp 源码相关的内容请看上篇, 请在阅读过上篇的基础上来看这篇会更好的理解,下面就开始吧。

OkHttpUtils 项目

本文的目录大致是这样:

  • OkHttpUtils 简单使用
  • OkHttpUtils 源码解析(V2.6.2)
  • OkHttp 和OkHttp 的对比

OkHttpUtils 简单使用

首先要在 gradle 中加上

1
compile 'com.zhy:okhttputils:2.6.2'

将直接使用okhttp默认的配置生成OkhttpClient,如果你有任何配置,记得在Application中调用initClient方法进行设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyApplication extends Application
{
@Override
public void onCreate()
{
super.onCreate();

OkHttpClient okHttpClient = new OkHttpClient.Builder()
// .addInterceptor(new LoggerInterceptor("TAG"))
.connectTimeout(10000L, TimeUnit.MILLISECONDS)
.readTimeout(10000L, TimeUnit.MILLISECONDS)
//其他配置
.build();

OkHttpUtils.initClient(okHttpClient);

}
}

在具体的使用过程中

GET 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
String url = "http://www.csdn.net/";
OkHttpUtils
.get()
.url(url)
.addParams("username", "hyman")
.addParams("password", "123")
.build()
.execute(new StringCallback()
{
@Override
public void onError(Request request, Exception e)
{

}

@Override
public void onResponse(String response)
{

}
});

Post JSON

1
2
3
4
5
6
7
OkHttpUtils
.postString()
.url(url)
.content(new Gson().toJson(new User("zhy", "123")))
.mediaType(MediaType.parse("application/json; charset=utf-8"))
.build()
.execute(new MyStringCallback());

能看出常用的GET ,POST 请求写起来非常简单流畅, 并且网络回调直接到主线程中了,可以直接处理数据。

OkHttpUtils 源码解析

我们通过这个get 请求,进去到源码中看下是如何封装的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OkHttpUtils.get().url("http://www.baidu.com").build().execute(new com.zhy.http.okhttp.callback.Callback() {
@Override
public Object parseNetworkResponse(Response response, int id) throws Exception {
return null;
}

@Override
public void onError(Call call, Exception e, int id) {

}

@Override
public void onResponse(Object response, int id) {

}
});
}

OkHttpUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

public static final long DEFAULT_MILLISECONDS = 10_000L;
private volatile static OkHttpUtils mInstance;
private OkHttpClient mOkHttpClient;
private Platform mPlatform;

public OkHttpUtils(OkHttpClient okHttpClient)
{
if (okHttpClient == null)
{
mOkHttpClient = new OkHttpClient();
} else
{
mOkHttpClient = okHttpClient;
}

mPlatform = Platform.get();
}


public static OkHttpUtils initClient(OkHttpClient okHttpClient)
{
if (mInstance == null)
{
synchronized (OkHttpUtils.class)
{
if (mInstance == null)
{
mInstance = new OkHttpUtils(okHttpClient);
}
}
}
return mInstance;
}

public static OkHttpUtils getInstance()
{
return initClient(null);
}


public Executor getDelivery()
{
return mPlatform.defaultCallbackExecutor();
}

public OkHttpClient getOkHttpClient()
{
return mOkHttpClient;
}

public static GetBuilder get()
{
return new GetBuilder();
}

public static PostStringBuilder postString()
{
return new PostStringBuilder();
}

public static PostFileBuilder postFile()
{
return new PostFileBuilder();
}

public static PostFormBuilder post()
{
return new PostFormBuilder();
}

public static OtherRequestBuilder put()
{
return new OtherRequestBuilder(METHOD.PUT);
}

public static HeadBuilder head()
{
return new HeadBuilder();
}

public static OtherRequestBuilder delete()
{
return new OtherRequestBuilder(METHOD.DELETE);
}

public static OtherRequestBuilder patch()
{
return new OtherRequestBuilder(METHOD.PATCH);
}

...

根据代码 能看出 OkHttpUtils 做了双重锁定的单例处理,因为一个App有一个 OkHttpClient 对象就行了。new 出了一个 OkHttpClient 。

我们在Application中初始化后,就生成这样一个对象, 以后每次用的时候就取得这个,无需重复创建。

实例化 OkHttpUtils 时 会创建 mPlatform = Platform.get() 。我们看下这是什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private static final Platform PLATFORM = findPlatform();

public static Platform get()
{
L.e(PLATFORM.getClass().toString());
return PLATFORM;
}

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();
}


static class Android extends Platform
{
@Override
public Executor defaultCallbackExecutor()
{
return new MainThreadExecutor();
}

static class MainThreadExecutor implements Executor
{
private final Handler handler = new Handler(Looper.getMainLooper());

@Override
public void execute(Runnable r)
{
handler.post(r);
}
}
}

此外在OkHttpUtils的构造方法中可以注意到有一个mPlatform的变量,他会根据当前是Android还是其他平台的不同被初始化为Android主线程或者普通线程池。

当是 Android 系统,new 了一个Android ,里面一个内部类,实现了 Executor, 创建了一个 handler 传入 Looper.getMainLooper() 主线程的 Looper,线程池执行时在这把可执行的 runnable 发送到主线程, 然后执行,就实现了线程切换。

它的功能就是实现线程之间的切换的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//GetRequest
@Override
public RequestCall build()
{
if (params != null)
{
url = appendParams(url, params);
}

return new GetRequest(url, tag, params, headers,id).build();
}

//postString

new PostStringBuilder();

public class PostStringBuilder extends OkHttpRequestBuilder<PostStringBuilder>
{
private String content;
private MediaType mediaType;


public PostStringBuilder content(String content)
{
this.content = content;
return this;
}

public PostStringBuilder mediaType(MediaType mediaType)
{
this.mediaType = mediaType;
return this;
}

@Override
public RequestCall build()
{
return new PostStringRequest(url, tag, params, headers, content, mediaType,id).build();
}


}

当我们调用build 方法时做了什么操作呢
public PostStringRequest(String url, Object tag, Map<String, String> params, Map<String, String> headers, String content, MediaType mediaType,int id)
{
super(url, tag, params, headers,id);
this.content = content;
this.mediaType = mediaType;

if (this.content == null)
{
Exceptions.illegalArgument("the content can not be null !");
}
if (this.mediaType == null)
{
this.mediaType = MEDIA_TYPE_PLAIN;
}

}

把我们设置的参数传递过来, 比如我们经常用的 mediaType 和 content 组装起来,
在执行网络请求的时候会把这些参数按照需要配置好,传入。



//OkHttpRequest

protected Request.Builder builder = new Request.Builder();

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();
}

使用 get请求 new GetBuilder() 创建了一个继承 OkHttpRequest 的 GetRequest,配置需要的参数。

执行网络请求 execute

然后 看下 execute 的具体内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    public void execute(Callback callback)
{
buildCall(callback);

if (callback != null)
{
callback.onBefore(request, getOkHttpRequest().getId());
}

OkHttpUtils.getInstance().execute(this, callback);
}


接着看 execute 的内容
可以看其最后只是将RequestCall和callback传递给了OkHttpUtils类的execute方法,
也就是说,最终还是调用了okhttp3.Call的enqueue()方法,在这里执行了真正的网络请求:


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进行网络请求,你肯定能猜出来 requestCall.getCall()的内容,
//看下面吧, 我贴出来了,就是 返回了一个OkHttpClient创建的call。
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;
}

// 可以看到方法中 code码是 【200,300)的算是成功,其他的是失败
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();
}

}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

public Call buildCall(Callback callback)
{
request = generateRequest(callback);

if (readTimeOut > 0 || writeTimeOut > 0 || connTimeOut > 0)
{
readTimeOut = readTimeOut > 0 ? readTimeOut : OkHttpUtils.DEFAULT_MILLISECONDS;
writeTimeOut = writeTimeOut > 0 ? writeTimeOut : OkHttpUtils.DEFAULT_MILLISECONDS;
connTimeOut = connTimeOut > 0 ? connTimeOut : OkHttpUtils.DEFAULT_MILLISECONDS;

clone = OkHttpUtils.getInstance().getOkHttpClient().newBuilder()
.readTimeout(readTimeOut, TimeUnit.MILLISECONDS)
.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS)
.connectTimeout(connTimeOut, TimeUnit.MILLISECONDS)
.build();

call = clone.newCall(request);
} else
{
call = OkHttpUtils.getInstance().getOkHttpClient().newCall(request);
}
return call;
}

来看看处理返回成功的回调方法

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
});
}

是不是有点眼熟 mPlatform 其实就是上面的那个 Android , 把这个 runnable 发送到主线程。

在本文中,okhttputils将初始化OkHttpClient的动作提取出来,这样同一个应用只需要在最开始的时候配置一下诸如网络超时、cookie等既可。

在具体的实现中,通过OkHttpRequestBuilder收集网络请求的属性并传递给OkHttpRequest,在其子类中按照不同的需要实现生成Request的方法。

OkHttpRequestBuilder的build()方法会生成RequestCall对象,RequestCall对象的execute()方法会调用OkHttpRequestBuilder对象的generateRequest()方法产生Request,并据此产生Call对象,最后通过该Call对象的enqueue方法执行网络请求。

至此 OkHttpUtils 的源码分析完成了,其实代码相对简单,就是对 OkHttp 的一个封装, 少写了一些代码, 把常用的 get , post ,postString,postFile,head,put, delete 都进行了一个封装,调用起来非常方便。

并且网络请求的返回内容也回调到主线程,方便进行 UI 操作,

这也解决了上一个篇文章中说的 OkHttp 的两个缺点。

请联系我!