Android 网络框架 Retrofit2.0介绍、使用和封装

前言
时至今日,Android的网络框架不再像之前那么到处都是,随着Google把 HttpClient直接删掉,似乎意味着Android越来越成熟。网络框架中的佼佼者Volley也不再那么光鲜,取而代之的是 Retrofit 和 okHttp。
感觉很像 OnePiece 中白胡子的离去象征着时代的变革,新时代的开始,多弗的垮台象征着七武海制度的取缔一样,不会使用Retrofit + okHttp + RxJava等一系列技术,就迈不进新时代的门槛,也不足以称为一个合格的开发者。

哈哈闲话不多说了,只是在 Android这个平台上 开发这么长时间的一点感触。各位看看笑笑也就算了。还是来介绍 Retrofit 吧。(直接略过1.9)

本文的所有代码在 retrofitLearn;

1. Retrofit介绍
A type-safe HTTP client for Android and Java
一个用于Android和Java平台的类型安全的网络框架

Retrofit is a type-safe REST client for Android built by Square. The library provides a powerful framework for authenticating and interacting with APIs and sending network requests with OkHttp.
Retrofit 是一个Square开发的类型安全的REST安卓客户端请求库。这个库为网络认证、API请求以及用OkHttp发送网络请求提供了强大的框架 。

Retrofit 把REST API返回的数据转化为Java对象,就像ORM框架那样,把数据库内的存储的数据转化为相应的Java bean对象。

那么我们知道Retrofit是一个类型安全的网络框架,而且它是使用REST API的,接下来我们看看什么是REST吧。

2. REST 介绍:
Resources Representational State Transfer
资源表现层状态转化

每一个URI代表一种资源
客户端和服务器之间,传递这种资源的某种 表现层(“资源”具体呈现出来的形式,比如.txt,.png,.jpg)
客户端通过四个HTTP动词(GET用来获取资源,POST用来新建或更新资源,PUT用来更新资源,DELETE用来删除资源)对服务器端资源进行操作,实现”表现层状态转化”
关于REST,这里只仅仅列出了结论,文末有超级好的链接,请查看。如果你所使用的API是REST的,那么恭喜你,这样的API看起来真的很舒服,庆幸的是我司就使用的是REST API。

知道了REST是什么,那接下啦就开始介绍Retrofit的用法啦。

3. Retrofit基本用法(未封装)
1.在build.gradle中添加依赖

//okHttp
compile ‘com.squareup.okhttp3:okhttp:3.2.0’

//retrofit
compile ‘com.squareup.retrofit2:retrofit:2.0.2’
compile ‘com.squareup.retrofit2:converter-gson:2.0.2’
compile ‘com.squareup.okhttp3:logging-interceptor:3.2.0’

2.创建接口,声明API

//Retrofit turns your HTTP API into a Java interface.
//创建接口,声明GitHub的API
public interface GitHubAPI {

/*
请求该接口:https://api.github.com/users/baiiu
*/
@GET(“users/{user}”)
Call<User> userInfo(@Path(“user”) String user);
}

3.在MainActivity.onCreate()方法中调用

/*
1.初始化OkHttpClient
*/
OkHttpClient client = new OkHttpClient();

/*
2.创建Retrofit
*/
retrofit = new Retrofit.Builder()
//设置OKHttpClient
.client(client)
//设置baseUrl,注意,baseUrl必须后缀”/”
.baseUrl(“https://api.github.com/”)

//添加Gson转换器
.addConverterFactory(GsonConverterFactory.create())
.build();

/*
2.获取GitHub的API
*/
GitHubAPI gitHubAPI = retrofit.create(GitHubAPI.class);

/*
3.异步调用
*/
Call<User> userCall = gitHubAPI.userInfo(“baiiu”);
userCall.enqueue(new Callback<User>() {
@Override public void onResponse(Call<User> call, Response<User> response) {
User body = response.body();
LogUtil.d(body == null ? “body == null” : body.toString());
}

@Override public void onFailure(Call<User> call, Throwable t) {
/*
判断是否被cancel掉了
*/
if (call.isCanceled()) {
LogUtil.d(“the call is canceled , ” + toString());
} else {
LogUtil.e(t.toString());
}
}
});

/*
取消调用
*/
//userCall.cancel();

/*
同步调用,举个例子,写法如下(当然不能在主线程调用):
*/

//Each instance can only be used once, but calling clone() will create a new instance that can be used.
Call<User> clone = userCall.clone();

Response<User> response = clone.execute();
User body = response.body();
LogUtil.d(body == null ? “body == null” : body.toString());

注意:
1. 设置BaseUrl时必须后缀”/”,如 https://api.github.com/,这篇文章说的超清晰:Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android

2. 因为Retrofit在创建时候传入了BaseUrl,所以基本上所有请求都基于该BaseUrl了。但是总有些API不是以该BaseUrl开头的,特别是有些公司可能不是restful API的。那么该怎么办呢。Retrofit提供了一个注解@Url解决这个问题,可以在运行时直接使用该Url直接访问。代码如下:

//使用@Url注解,传入该Url地址就OK啦,跨过BaseUrl,直接访问该Url地址
@GET Call<Daily> getNewsList(@Url String url);

恩,接下来,知道了Retrofit的基本使用,接下来就是介绍Retrofit的注解了。

4. Retrofit注解
Retrofit使用注解来声明API的请求方式、请求参数等。这里只介绍一下它的 @Header 注解,其他的请阅读 官网文档 和 鸿洋的 Retrofit2 完全解析 探索与okhttp之间的关系。和Retrofit 2.0 注解篇
(这块写了也是翻译官方文档的和参考鸿样的,会拉长篇幅)

请求头的设置可以通过 @Header 注解添加,又有两种添加方式:

设置静态的请求头。
@Headers(“Cache-Control: max-age=640000”)
@GET(“widget/list”)
Call<List<Widget>> widgetList();

@Headers({
“Accept: application/vnd.github.v3.full+json”,
“User-Agent: Retrofit-Sample-App”
})
@GET(“users/{username}”)
Call<User> getUser(@Path(“username”) String username);

动态的设置请求头。
@GET(“user”)
Call<User> getUser(@Header(“Authorization”) String authorization)

Note that headers do not overwrite each other. All headers with the same name will be included in the request.
同一个请求的同一个请求头在不同地方的设置不会被覆盖,而是会被全部添加进请求头中。

Headers that need to be added to every request can be specified using an OkHttp interceptor.
如果要给每个请求都添加同样的Header时,可以使用okHttp的 Interceptor 。

那么,接下来就介绍Interceptor。

5.Interceptors使用
Retrofit 2.0 底层强制依赖okHttp,所以可以使用okHttp的拦截器Interceptors 来对所有请求进行再处理。

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.

使用时,实现 Interceptor 接口,做我们自己的处理。
目前使用中,一般用来设置UA、设置缓存策略 、打印Log 等。这里介绍一个设置UA的Interceptor:

1.创建设置UA的Interceptor

public final class UserAgentInterceptor implements Interceptor {
private static final String USER_AGENT_HEADER_NAME = “User-Agent”;
private final String userAgentHeaderValue;

public UserAgentInterceptor(String userAgentHeaderValue) {
this.userAgentHeaderValue = userAgentHeaderValue;
}

@Override public Response intercept(Chain chain) throws IOException {
final Request originalRequest = chain.request();

final Request requestWithUserAgent = originalRequest.newBuilder()

//移除先前默认的UA
.removeHeader(USER_AGENT_HEADER_NAME)

//设置UA
.addHeader(USER_AGENT_HEADER_NAME, userAgentHeaderValue)

.build();
return chain.proceed(requestWithUserAgent);
}
}

2.创建okHttpClient时 设置该Interceptor

okHttpClient = new OkHttpClient.Builder()
//添加UA
.addInterceptor(new UserAgentInterceptor(HttpHelper.getUserAgent()))

//失败重连
.retryOnConnectionFailure(true)

//time out
.readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
.connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)

.build();

Interceptors 和 Header 的配合使用让我们很方便的使用Http本身的缓存。之前用Volley的时候这块就比较难处理。解下来就说说Retrofit的缓存机制。

6. 配置网络缓存
使用Retrofit 感觉最方便的就是它提供的缓存方式(虽然这是Http本身的),我们可以随便通过Header和Interceptor配置自己的缓存策略。

6.1 缓存设置原理:
缓存的设置是通过设置 Http请求头和响应头中的 Cache-Control 的 max-age 属性达到的。在复写 Interceptor 时,通过设置响应头的 Cache-Control 来达到目的。关于Http的本身的缓存命中请查看文末链接 (毕竟Http也是个大家伙) 。

6.2 缓存设置步骤
设置缓存目录
retrofit本身默认无缓存,连缓存目录都没有提供默认的,所以 要想实现缓存,必须要在创建OkHttpClient时设置缓存目录。 这块超级坑,自己试了半天,最终看了下Cache那边的源码,竟然没有默认的缓存目录!!!

设置缓存的方式

通过添加 @Headers(“Cache-Control: max-age=120”) 进行设置。添加了Cache-Control 的请求,retrofit 会默认缓存该请求的返回数据。
通过Interceptors实现缓存。
6.3 实现Interceptor进行缓存
提供了两种缓存策略(参考),设置缓存时Application Interceptor 和 Net Interceptor 都要设置:

AllCachedInterceptor
有网没网都先走缓存,可以设置间歇时间。
同时可以针对某一个request设置单独的缓存时间,使用 @Headers(“Cache-Control: max-age=120”) 就行。

OnOffLineCachedInterceptor
有网可以不走缓存,可设定时间,设置为0意味着有网不走缓存,全部刷新数据;无网强制读取缓存数据。

到现在为止,关于Retrofit的基本用法和缓存设置已经了解了,写点例子就已经会用了,接下来肯定就是进行封装了,让它更便于调用。

7. Retrofit 封装
封装Retrofit是为了提供更方便的调用,更好的配置和使用。那么看到开头的Retrofit调用方式,大概有这么几个步骤:
1. 创建OkHttpClient
2. 创建Retrofit实例
3. 获取我们写的API interface
4. 在代码中异步调用

那么封装时候也肯定从这几步入手:

7.1 初始化OkHttpClient
创建OkHttpFactory类,初始化OkHttpClient并对外提供该实例的单例。并提供一个 HttpHelper 类用于提供所需要的参数,比如UA。
这样做的好处是,程序中会使用OkHttp做一些其他请求操作,比如下载、上传等网络操作,就可用共用一个OkHttpClient。

这里使用枚举生成单例。

enum OKHttpFactory {

INSTANCE;

private final OkHttpClient okHttpClient;

private static final int TIMEOUT_READ = 25;
private static final int TIMEOUT_CONNECTION = 25;

OKHttpFactory() {
//打印请求Log
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

//缓存目录
Cache cache = new Cache(MyApplication.mContext.getCacheDir(), 10 * 1024 * 1024);

okHttpClient = new OkHttpClient.Builder()
//打印请求log
.addInterceptor(interceptor)

//stetho,可以在chrome中查看请求
.addNetworkInterceptor(new StethoInterceptor())

//添加UA
.addInterceptor(new UserAgentInterceptor(HttpHelper.getUserAgent()))

//必须是设置Cache目录
.cache(cache)

//走缓存,两个都要设置
.addInterceptor(new OnOffLineCachedInterceptor())
.addNetworkInterceptor(new OnOffLineCachedInterceptor())

//失败重连
.retryOnConnectionFailure(true)

//time out
.readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
.connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)

.build();
}

public OkHttpClient getOkHttpClient() {
return okHttpClient;
}
}

7.2 初始化Retrofit
依然使用枚举创建单例:

public enum RetrofitClient implements ApiContants {
INSTANCE;

private final Retrofit retrofit;

RetrofitClient() {
retrofit = new Retrofit.Builder()
//设置OKHttpClient
.client(OKHttpFactory.INSTANCE.getOkHttpClient())

//baseUrl
.baseUrl(GITHUB_BASEURL)

//gson转化器
.addConverterFactory(GsonConverterFactory.create())

.build();
}

public Retrofit getRetrofit() {
return retrofit;
}
}

7.3 创建ApiFactory类封装所有API
如题,创建ApiFactory类管理所有的API interface,对外提供方法获取他们,这样调用时会方便很多,而且也便于修改。

public enum ApiFactory {
INSTANCE;

private final GitHubAPI gitHubAPI;
private final AnotherAPI anotherAPI;

ApiFactory() {
gitHubAPI = RetrofitClient.INSTANCE.getRetrofit().create(GitHubAPI.class);
anotherAPI = RetrofitClient.INSTANCE.getRetrofit().create(AnotherAPI.class);
}

public GitHubAPI gitHubAPI() {
return gitHubAPI;
}

public AnotherAPI getAnotherAPI() {
return anotherAPI;
}
}
在外部调用时会方便很多,比如:

Call<User> userCall = ApiFactory.gitHubAPI().userInfo(“baiiu”);

封装的代码在GitHub上:retrofitLearn;

这样封装自己觉得还不错,就可以开心的写代码啦。接下来说说一个神奇的工具。

8. Stetho 一个神奇的工具
首先:使用该工具需要翻墙。 不能翻墙的还是使用Charles吧。

gradle中添加:
compile ‘com.facebook.stetho:stetho:1.3.1’
compile ‘com.facebook.stetho:stetho-okhttp3:1.3.1’

打开chrome,输入chrome://inspect
点击你的程序进行inspect,就可以直接使用Chrome进行抓包、调试你的应用啦。但是不能翻墙的话,你会看到一片空白。。。
结语:
毕竟Retrofit出来已经很久了,没有的赶紧用用吧。在使用Retrofit时,深深赶紧到其对Http本身机制的应用之深,真的感觉到非常棒的应用体验。用Retrofit作为网络框架,简直就能感觉到它设计之美,更何况还能和RxJava结合起来,美到不行。

深入解析OkHttp3

OkHttp是一个精巧的网络请求库,有如下特性: 
1)支持http2,对一台机器的所有请求共享同一个socket 
2)内置连接池,支持连接复用,减少延迟 
3)支持透明的gzip压缩响应体 
4)通过缓存避免重复的请求 
5)请求失败时自动重试主机的其他ip,自动重定向 
6)好用的API
其本身就是一个很强大的库,再加上Retrofit2、Picasso的这一套组合拳,使其愈发的受到开发者的关注。本篇博客,我将对Okhttp3进行分析(源码基于Okhttp3.4)。
如何引入Okhttp3?
配置Okhttp3非常简单,只需要在Android Studio 的gradle进行如下的配置:
compile ‘com.squareup.okhttp3:okhttp:3.4.1′
* 1
添加网络权限:
<uses-permission android:name=”android.permission.INTERNET”/>
* 1
Okhttp3的基本使用
okHttp的get请求 
okHttp的一般使用如下,okHttp默认使用的就是get请求
String url = “http://write.blog.csdn.net/postlist/0/0/enabled/1”;
mHttpClient = new OkHttpClient();

Request request = new Request.Builder().url(url).build();
okhttp3.Response response = null;
try {

response = mHttpClient.newCall(request).execute();
String json = response.body().string();
Log.d(“okHttp”,json);

} catch (IOException e) {
e.printStackTrace();
}

}

我们试着将数据在logcat进行打印,发现会报错,原因就是不能在主线程中进行耗时的操作 
 
说明mHttpClient.newCall(request).execute()是同步的,那有没有异步的方法呢,答案是肯定的,就是mHttpClient.newCall(request).enqueue()方法,里面需要new一个callback我们对代码进行修改,如下
public void requestBlog() {
String url = “http://write.blog.csdn.net/postlist/0/0/enabled/1”;

mHttpClient = new OkHttpClient();

Request request = new Request.Builder().url(url).build();
/* okhttp3.Response response = null;*/

/*response = mHttpClient.newCall(request).execute();*/
mHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
String json = response.body().string();
Log.d(“okHttp”, json);
}
});

}


Okhttp的POST请求
POST提交Json数据
private void postJson() throws IOException {
String url = “http://write.blog.csdn.net/postlist/0/0/enabled/1”;
String json = “haha”;

OkHttpClient client = new OkHttpClient();

RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {

Log.d(TAG, response.body().string());
}
});

}

POST提交键值对 
很多时候我们会需要通过POST方式把键值对数据传送到服务器。 OkHttp提供了很方便的方式来做这件事情。
private void post(String url, String json) throws IOException {
OkHttpClient client = new OkHttpClient();
RequestBody formBody = new FormBody.Builder()
.add(“name”, “liming”)
.add(“school”, “beida”)
.build();

Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();

Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
String str = response.body().string();
Log.i(TAG, str);

}

});
}

异步上传文件 
上传文件本身也是一个POST请求 
定义上传文件类型
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse(“text/x-markdown; charset=utf-8”);
* 1
* 2
将文件上传到服务器上:
private void postFile() {
OkHttpClient mOkHttpClient = new OkHttpClient();
File file = new File(“/sdcard/demo.txt”);
Request request = new Request.Builder()
.url(“https://api.github.com/markdown/raw”)
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();

mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG, response.body().string());
}
});
}
添加如下权限:
<uses-permission android:name=”android.permission.READ_EXTERNAL_STORAGE”/>
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>

提取响应头 
典型的HTTP头 像是一个 Map
private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
Request request = new Request.Builder()
.url(“https://api.github.com/repos/square/okhttp/issues”)
.header(“User-Agent”, “OkHttp Headers.java”)
.addHeader(“Accept”, “application/json; q=0.5”)
.addHeader(“Accept”, “application/vnd.github.v3+json”)
.build();

Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

System.out.println(“Server: ” + response.header(“Server”));
System.out.println(“Date: ” + response.header(“Date”));
System.out.println(“Vary: ” + response.headers(“Vary”));
}

Post方式提交String 
使用HTTP POST提交请求到服务。这个例子提交了一个markdown文档到web服务,以HTML方式渲染markdown。因为整个请求体都在内存中,因此避免使用此api提交大文档(大于1MB)。
private void postString() throws IOException {

OkHttpClient client = new OkHttpClient();

String postBody = “”
+ “Releases\n”
+ “——–\n”
+ “\n”
+ ” * zhangfei\n”
+ ” * guanyu\n”
+ ” * liubei\n”;

Request request = new Request.Builder()
.url(“https://api.github.com/markdown/raw”)
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
.build();

Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());

}

});

}
Post方式提交流
以流的方式POST提交请求体。请求体的内容由流写入产生。这个例子是流直接写入Okio的BufferedSink。你的程序可能会使用OutputStream,你可以使用BufferedSink.outputStream()来获取。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse(“text/x-markdown; charset=utf-8”);

private void postStream() throws IOException {
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}

@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8(“Numbers\n”);
sink.writeUtf8(“——-\n”);
for (int i = 2; i <= 997; i++) {
sink.writeUtf8(String.format(” * %s = %s\n”, i, factor(i)));
}
}

private String factor(int n) {
for (int i = 2; i < n; i++) {
int x = n / i;
if (x * i == n) return factor(x) + ” × ” + i;
}
return Integer.toString(n);
}
};

Request request = new Request.Builder()
.url(“https://api.github.com/markdown/raw”)
.post(requestBody)
.build();

Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());

}

});
}
Post方式提交表单
private void postForm() {
OkHttpClient client = new OkHttpClient();

RequestBody formBody = new FormBody.Builder()
.add(“search”, “Jurassic Park”)
.build();

Request request = new Request.Builder()
.url(“https://en.wikipedia.org/w/index.php”)
.post(formBody)
.build();

Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());

}

});

}

Post方式提交分块请求 
MultipartBody 可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。
private static final String IMGUR_CLIENT_ID = “…”;
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse(“image/png”);

private void postMultipartBody() {
OkHttpClient client = new OkHttpClient();

// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
MultipartBody body = new MultipartBody.Builder(“AaB03x”)
.setType(MultipartBody.FORM)
.addPart(
Headers.of(“Content-Disposition”, “form-data; name=\”title\””),
RequestBody.create(null, “Square Logo”))
.addPart(
Headers.of(“Content-Disposition”, “form-data; name=\”image\””),
RequestBody.create(MEDIA_TYPE_PNG, new File(“website/static/logo-square.png”)))
.build();

Request request = new Request.Builder()
.header(“Authorization”, “Client-ID ” + IMGUR_CLIENT_ID)
.url(“https://api.imgur.com/3/image”)
.post(body)
.build();

Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());

}

});
}

响应缓存 
为了缓存响应,你需要一个你可以读写的缓存目录,和缓存大小的限制。这个缓存目录应该是私有的,不信任的程序应不能读取缓存内容。 
一个缓存目录同时拥有多个缓存访问是错误的。大多数程序只需要调用一次new OkHttpClient(),在第一次调用时配置好缓存,然后其他地方只需要调用这个实例就可以了。否则两个缓存示例互相干扰,破坏响应缓存,而且有可能会导致程序崩溃。 
响应缓存使用HTTP头作为配置。你可以在请求头中添加Cache-Control: max-stale=3600 ,OkHttp缓存会支持。你的服务通过响应头确定响应缓存多长时间,例如使用Cache-Control: max-age=9600。
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(cacheDirectory, cacheSize);

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.cache(cache);
OkHttpClient client = builder.build();

Request request = new Request.Builder()
.url(“http://publicobject.com/helloworld.txt”)
.build();

Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
String response1Body = response.body().string();
System.out.println(“Response 1 response:          ” + response);
System.out.println(“Response 1 cache response:    ” + response.cacheResponse());
System.out.println(“Response 1 network response:  ” + response.networkResponse());
}

});

超时 
没有响应时使用超时结束call。没有响应的原因可能是客户点链接问题、服务器可用性问题或者这之间的其他东西。OkHttp支持连接,读取和写入超时。
private void ConfigureTimeouts() {

OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client = builder.build();

client.newBuilder().connectTimeout(10, TimeUnit.SECONDS);
client.newBuilder().readTimeout(10,TimeUnit.SECONDS);
client.newBuilder().writeTimeout(10,TimeUnit.SECONDS);

Request request = new Request.Builder()
.url(“http://httpbin.org/delay/2”) // This URL is served with a 2 second delay.
.build();

Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(“Response completed: ” + response);
}

});

}
简单封装okHttp框架
新建一个工具类OkHttpUtils 
OkHttpClient必须是单例的,所以这里我们需要使用到单例设计模式,私有化构造函数,提供一个方法给外界获取OkHttpUtils实例对象
public class OkHttpUtils {

private  static  OkHttpUtils mInstance;
private OkHttpClient mHttpClient;

private OkHttpUtils() {

};

public static  OkHttpUtils getInstance(){
return  mInstance;
}

}

一般网络请求分为get和post请求两种,但无论哪种请求都是需要用到request的,所以我们首先封装一个request,创建一个doRequest方法,在其内先编写mHttpClient.newCall(request).enqueue(new Callback())相关逻辑
public  void doRequest(final Request request){

mHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {

}
});
}

我们需要自定义一个callback,BaseCallback,并将其传入request方法中
public class BaseCallback  {

}

在OkHttpUtils中编写get和post方法
public void get(String url){

}

public void post(String url,Map<String,Object> param){

}

post方法中构建request对象,这里我们需要创建一个buildRequest方法,用于生成request对象
private  Request buildRequest(String url,HttpMethodType methodType,Map<String,Object> params){
return null;
}

这里需要定一个枚举对象HttpMethodType,用于区分是get还是post
enum  HttpMethodType{

GET,
POST,

}
buildRequest方法根据HttpMethodType不同有相应的逻辑处理
private  Request buildRequest(String url,HttpMethodType methodType,Map<String,Object> params){

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

if (methodType == HttpMethodType.POST){

builder.post(body);
}
else if(methodType == HttpMethodType.GET){

builder.get();
}

return builder.build();

}

builder.post()方法中需要一个body,所以我们需要创建一个方法builderFormData()方法用于返回RequestBody,这里内部逻辑后面再进行完善
private RequestBody builderFormData(Map<String,Object> params){
return null;
}

于是buildRequest方法变成了这样
private  Request buildRequest(String url,HttpMethodType methodType,Map<String,Object> params){

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

if (methodType == HttpMethodType.POST){

RequestBody body = builderFormData(params);

builder.post(body);
}
else if(methodType == HttpMethodType.GET){

builder.get();
}

return builder.build();

}
get方法进行修改:
public void get(String url,BaseCallback callback){

Request request = buildRequest(url,HttpMethodType.GET,null);

doRequest(request,callback);

}

post方法进行修改:
public void post(String url,Map<String,Object> params,BaseCallback callback){

Request request = buildRequest(url,HttpMethodType.POST,params);

doRequest(request,callback);
}
完善builderFormData()方法
private RequestBody builderFormData(Map<String,String> params){
FormBody.Builder builder =  new FormBody.Builder();

if(params!=null){
for(Map.Entry<String,String> entry:params.entrySet()){
builder.add(entry.getKey(),entry.getValue());
}
}
return builder.build();
}

BaseCallback中定义一个抽象方法onBeforeRequest,这样做的理由是我们在加载网络数据成功前,一般都有进度条等显示,这个方法就是用来做这些处理的
public abstract class BaseCallback  {

public  abstract void onBeforeRequest(Request request);

}

OkHttpUtils的doRequest方法增加如下语句:
baseCallback.onBeforeRequest(request);
* 1
BaseCallback中多定义2个抽象方法
public abstract  void onFailure(Request request, Exception e) ;

/**
*请求成功时调用此方法
* @param response
*/
public abstract  void onResponse(Response response);

由于Response的状态有多种,比如成功和失败,所以需要onResponse分解为3个抽象方法
/**
*
* 状态码大于200,小于300 时调用此方法
* @param response
* @param t
* @throws
*/
public abstract void onSuccess(Response response,T t) ;

/**
* 状态码400,404,403,500等时调用此方法
* @param response
* @param code
* @param e
*/
public abstract void onError(Response response, int code,Exception e) ;

/**
* Token 验证失败。状态码401,402,403 等时调用此方法
* @param response
* @param code

*/
public abstract void onTokenError(Response response, int code);

response.body.string()方法返回的都是String类型,而我们需要显示的数据其实是对象,所以我们就想抽取出方法,直接返回对象,由于我们不知道对象的类型是什么,所以我们在BaseCallback中使用范型
public abstract class BaseCallback<T>
* 1
BaseCallback中需要将泛型转换为Type,所以要声明Type类型
public   Type mType;
* 1
BaseCallback中需要如下一段代码,将泛型T转换为Type类型
static Type getSuperclassTypeParameter(Class<?> subclass)
{
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class)
{
throw new RuntimeException(“Missing type parameter.”);
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}

在BaseCallback的构造函数中进行mType进行赋值
public BaseCallback()
{
mType = getSuperclassTypeParameter(getClass());
}
OkHttpUtils中doRequest方法的onFailure与onResponse方法会相应的去调用baseCallback的方法
mHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
baseCallback.onFailure(request,e);
}

@Override
public void onResponse(Call call, Response response) throws IOException {

if(response.isSuccessful()) {

baseCallback.onSuccess(response,null);

}else {
baseCallback.onError(response,response.code(),null);
}
/*mGson.fromJson(response.body().string(),baseCallback.mType);*/
}

});
onResponse方法中成功的情况又有区分,根据mType的类型不同有相应的处理逻辑,同时还要考虑Gson解析错误的情况
@Override
public void onResponse(Call call, Response response) throws IOException {

if(response.isSuccessful()) {

String resultStr = response.body().string();

if (baseCallback.mType == String.class){

baseCallback.onSuccess(response,resultStr);
}
else {
try {

Object obj = mGson.fromJson(resultStr, baseCallback.mType);
baseCallback.onSuccess(response,obj);
}
catch (com.google.gson.JsonParseException e){ // Json解析的错误
baseCallback.onError(response,response.code(),e);
}
}

}else {
baseCallback.onError(response,response.code(),null);
}

}

构造函数中进行一些全局变量的初始化的操作,还有一些超时的设计
private OkHttpUtils() {

mHttpClient = new OkHttpClient();
OkHttpClient.Builder builder = mHttpClient.newBuilder();
builder.connectTimeout(10, TimeUnit.SECONDS);
builder.readTimeout(10,TimeUnit.SECONDS);
builder.writeTimeout(30,TimeUnit.SECONDS);

mGson = new Gson();

};

静态代码块初始化OkHttpUtils对象
static {
mInstance = new OkHttpUtils();
}
在okHttpUtils内,需要创建handler进行UI界面的更新操作,创建callbackSuccess方法
private void callbackSuccess(final  BaseCallback callback , final Response response, final Object obj ){

mHandler.post(new Runnable() {
@Override
public void run() {
callback.onSuccess(response, obj);
}
});
}

doRequest方法的onResponse方法也进行相应的改写
if (baseCallback.mType == String.class){

/*baseCallback.onSuccess(response,resultStr);*/
callbackSuccess(baseCallback,response,resultStr);
}

创建callbackError方法
private void callbackError(final BaseCallback callback, final Response response, final Exception e) {

mHandler.post(new Runnable() {
@Override
public void run() {
callback.onError(response, response.code(), e);
}
});
}

将doRequest方法的onResponse方法中的baseCallback.onError(response,response.code(),e);替换为callbackError(baseCallback,response,e);方法
@Override
public void onResponse(Call call, Response response) throws IOException {

if(response.isSuccessful()) {

String resultStr = response.body().string();

if (baseCallback.mType == String.class){

/*baseCallback.onSuccess(response,resultStr);*/
callbackSuccess(baseCallback,response,resultStr);
}
else {
try {

Object obj = mGson.fromJson(resultStr, baseCallback.mType);
/*baseCallback.onSuccess(response,obj);*/
callbackSuccess(baseCallback,response,obj);
}
catch (com.google.gson.JsonParseException e){ // Json解析的错误
/*baseCallback.onError(response,response.code(),e);*/
callbackError(baseCallback,response,e);
}
}

}else {

callbackError(baseCallback,response,null);
/*baseCallback.onError(response,response.code(),null);*/
}

}

至此,我们的封装基本完成。
OkHttp3源码分析
请求处理分析 
当我们要请求网络的时候我们需要用OkHttpClient.newCall(request)进行execute或者enqueue操作,当我们调用newCall时:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}

实际返回的是一个RealCall类,我们调用enqueue异步请求网络实际上是调用了RealCall的enqueue方法:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException(“Already Executed”);
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
最终的请求是dispatcher来完成的。
Dispatcher任务调度
Dispatcher的本质是异步请求的管理器,控制最大请求并发数和单个主机的最大并发数,并持有一个线程池负责执行异步请求。对同步的请求只是用作统计。他是如何做到控制并发呢,其实原理就在上面的2个execute代码里面,真正网络请求执行前后会调用executed和finished方法,而对于AsyncCall的finished方法后,会根据当前并发数目选择是否执行队列中等待的AsyncCall。并且如果修改Dispatcher的maxRequests或者maxRequestsPerHost也会触发这个过程。 
Dispatcher主要用于控制并发的请求,它主要维护了以下变量:
/** 最大并发请求数*/
private int maxRequests = 64;
/** 每个主机最大请求数*/
private int maxRequestsPerHost = 5;
/** 消费者线程池 */
private ExecutorService executorService;
/** 将要运行的异步请求队列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/**正在运行的异步请求队列 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** 正在运行的同步请求队列 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

构造函数
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}

public Dispatcher() {
}

public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory(“OkHttp Dispatcher”, false));
}
return executorService;
}

Dispatcher有两个构造函数,可以使用自己设定线程池,如果没有设定线程池则会在请求网络前自己创建线程池,这个线程池类似于CachedThreadPool比较适合执行大量的耗时比较少的任务。
异步请求
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}

当正在运行的异步请求队列中的数量小于64并且正在运行的请求主机数小于5时则把请求加载到runningAsyncCalls中并在线程池中执行,否则就再入到readyAsyncCalls中进行缓存等待。
AsyncCall 
线程池中传进来的参数就是AsyncCall它是RealCall的内部类,内部也实现了execute方法:
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException(“Canceled”));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, “Callback failure for ” + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}

首先我们来看看最后一行, 无论这个请求的结果如何都会执行client.dispatcher().finished(this);
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}

/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError(“Call wasn’t in-flight!”);
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}

if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
finished方法将此次请求从runningAsyncCalls移除后还执行了promoteCalls方法:
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();

if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}

if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}

可以看到最关键的点就是会从readyAsyncCalls取出下一个请求,并加入runningAsyncCalls中并交由线程池处理。好了让我们再回到上面的AsyncCall的execute方法,我们会发getResponseWithInterceptorChain方法返回了Response,很明显这是在请求网络。
Interceptor拦截器 
在回到RealCall中,我们看到无论是execute还是enqueue,真正的Response是通过这个函数getResponseWithInterceptorChain获取的,其他的代码都是用作控制与回调。而这里就是真正请求的入口,也是到了OkHttp的一个很精彩的设计:Interceptor与Chain 
看一下RealCall中的getResponseWithInterceptorChain方法
private Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));

Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}

这也是与旧版本不一致的地方,在3.4.x以前,没有这些内部的这些拦截器,只有用户的拦截器与网络拦截器。而Request和Response是通过HttpEngine来完成的。在RealCall实现了用户拦截器与RetryAndFollowUp的过程,而在HttpEngine内部处理了请求转换、Cookie、Cache、网络拦截器、连接网络的过程。值得一提的是,在旧版是获取到Response后调用网络拦截器的拦截。 
而在这里,RealInterceptorChain会递归的创建并以此调用拦截器,去掉诸多异常,简化版代码如下:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
Connection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();

calls++;

// If we already have a stream, confirm that the incoming request will use it.
if (this.httpStream != null && !sameConnection(request.url())) {
throw new IllegalStateException(“network interceptor ” + interceptors.get(index – 1)
+ ” must retain the same host and port”);
}

// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpStream != null && calls > 1) {
throw new IllegalStateException(“network interceptor ” + interceptors.get(index – 1)
+ ” must call proceed() exactly once”);
}

// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpStream, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

// Confirm that the next interceptor made its required call to chain.proceed().
if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException(“network interceptor ” + interceptor
+ ” must call proceed() exactly once”);
}

// Confirm that the intercepted response isn’t null.
if (response == null) {
throw new NullPointerException(“interceptor ” + interceptor + ” returned null”);
}

return response;
}
Chain与Interceptor会互相递归调用,直到链的尽头。 
我们看到,通过职责链模式,清楚地切开了不同的逻辑,每个拦截器完成自己的职责,从而完成用户的网络请求。 
大概流程是: 
1)先经过用户拦截器 
2)RetryAndFollowUpInterceptor负责自动重试和进行必要的重定向 
3)BridgeIntercetor负责将用户Request转换成一个实际的网络请求的Request,再调用下层的拦截器获取Response,最后再将网络Response转换成用户的Reponse 
4)CacheInterceptor负责控制缓存 
5)ConnectInterceptor负责进行连接主机 
6)网络拦截器进行拦截 
7)CallServerInterceptor是真正和服务器通信,完成http请求
连接与通信 
在RetryAndFollowUpInterceptor中,会创建StreamAllocation,然后交给下游的ConnectInterceptor
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();

// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals(“GET”);
HttpStream httpStream = streamAllocation.newStream(client, doExtensiveHealthChecks);
RealConnection connection = streamAllocation.connection();

return realChain.proceed(request, streamAllocation, httpStream, connection);
}

这里会创建一个HttpStream,并且取到一个RealConnection,继续交给下游的CallServerInterceptor。 
我们跟踪进去看看,StreamAllocation里面做了什么
public HttpStream newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
int connectTimeout = client.connectTimeoutMillis();
int readTimeout = client.readTimeoutMillis();
int writeTimeout = client.writeTimeoutMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();

try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);

HttpStream resultStream;
if (resultConnection.framedConnection != null) {
resultStream = new Http2xStream(client, this, resultConnection.framedConnection);
} else {
resultConnection.socket().setSoTimeout(readTimeout);
resultConnection.source.timeout().timeout(readTimeout, MILLISECONDS);
resultConnection.sink.timeout().timeout(writeTimeout, MILLISECONDS);
resultStream = new Http1xStream(
client, this, resultConnection.source, resultConnection.sink);
}

synchronized (connectionPool) {
stream = resultStream;
return resultStream;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
这里的代码逻辑是这样的,找一个健康的连接,设置超时时间,然后根据协议创建一个HttpStream并返回。 
继续跟进去看findHealthyConnection:
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);

// If this is a brand new connection, we can skip the extensive health checks.
synchronized (connectionPool) {
if (candidate.successCount == 0) {
return candidate;
}
}

// Do a (potentially slow) check to confirm that the pooled connection is still good. If it
// isn’t, take it out of the pool and start again.
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}

return candidate;
}
}

上面的逻辑也很简单,在findConnection中找一个连接,然后做健康检查,如果不健康就回收,并再次循环,那么真正寻找连接的代码就在findConnection里面了:
/**
* Returns a connection to host a new stream. This prefers the existing connection if it exists,
* then the pool, finally building a new connection.
*/
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled) throws IOException {
Route selectedRoute;
synchronized (connectionPool) {
if (released) throw new IllegalStateException(“released”);
if (stream != null) throw new IllegalStateException(“stream != null”);
if (canceled) throw new IOException(“Canceled”);

RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}

// Attempt to get a connection from the pool.
RealConnection pooledConnection = Internal.instance.get(connectionPool, address, this);
if (pooledConnection != null) {
this.connection = pooledConnection;
return pooledConnection;
}

selectedRoute = route;
}

if (selectedRoute == null) {
selectedRoute = routeSelector.next();
synchronized (connectionPool) {
route = selectedRoute;
refusedStreamCount = 0;
}
}
RealConnection newConnection = new RealConnection(selectedRoute);
acquire(newConnection);

synchronized (connectionPool) {
Internal.instance.put(connectionPool, newConnection);
this.connection = newConnection;
if (canceled) throw new IOException(“Canceled”);
}

newConnection.connect(connectTimeout, readTimeout, writeTimeout, address.connectionSpecs(),
connectionRetryEnabled);
routeDatabase().connected(newConnection.route());

return newConnection;
}

这里大概分成分成3大步: 
1)如果当前有连接并且符合要求的话,就直接返回 
2)如果线程池能取到一个符合要求的连接的话,就直接返回 
3)如果Route为空,从RouteSelector取一个Route,然后新建一个RealConnection,并放入ConnectionPool,随后调用connect,再返回
也就是说不管当前走的是步骤1还是2,一开始一定是从3开始的,也就是在RealConnection的connect中真正完成了socket连接。 
connect里面代码比较长,真正要做的就是一件事,如果是https请求并且是http代理,则建立隧道连接,隧道连接请参考RFC2817,否则建立普通连接。 
这两者都调用了2个函数:connectSocket(connectTimeout, readTimeout); establishProtocol(readTimeout, writeTimeout, connectionSpecSelector); 
但是隧道连接则多了一个代理认证的过程,可能会反复的connectSocket和构造请求。 
看一下connectSocket:
private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();

rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);

rawSocket.setSoTimeout(readTimeout);
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
throw new ConnectException(“Failed to connect to ” + route.socketAddress());
}
source = Okio.buffer(Okio.source(rawSocket));
sink = Okio.buffer(Okio.sink(rawSocket));
}

就是根据Route来创建socket,在connect,随后将rawSocket的InputStream与OutputStream包装成Source与Sink。这里提一下,OkHttp是依赖Okio的,Okio封装了Java的IO API,如这里的Source与Sink,非常简洁实用。
而establishProtocol里,如果是https则走TLS协议,生成一个SSLSocket,并进行握手和验证,同时如果是HTTP2或者SPDY3的话,则生成一个FrameConnection。这里不再多提,HTTP2和HTTP1.X大相径庭,我们这里主要是分析HTTP1.X的连接,后面有机会我们会单独开篇讲HTTP2。同时TLS相关的话题这里也一并略过,想了解的朋友可以看一看相应的Java API和HTTPS连接的资料。
再回到StreamAllcation.newStream的代码resultStream = new Http1xStream( client, this, resultConnection.source, resultConnection.sink);实质上HttpStream其实就是Request和Response读写Socket的抽象,我们看到Http1xStream取到了Socket输入输出流,随后在CallServerInterceptor可以拿来做读写。
我们看CallServerInterceptor做了什么:
@Override public Response intercept(Chain chain) throws IOException {
HttpStream httpStream = ((RealInterceptorChain) chain).httpStream();
StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation();
Request request = chain.request();

long sentRequestMillis = System.currentTimeMillis();
httpStream.writeRequestHeaders(request);

if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
}

httpStream.finishRequest();

Response response = httpStream.readResponseHeaders()
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();

if (!forWebSocket || response.code() != 101) {
response = response.newBuilder()
.body(httpStream.openResponseBody(response))
.build();
}

if (“close”.equalsIgnoreCase(response.request().header(“Connection”))
|| “close”.equalsIgnoreCase(response.header(“Connection”))) {
streamAllocation.noNewStreams();
}

int code = response.code();
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
throw new ProtocolException(
“HTTP ” + code + ” had non-zero Content-Length: ” + response.body().contentLength());
}

return response;
}

CallServerInterceptor顾名思义,就是真正和Server进行通信的地方。这里也是按照HTTP协议,依次写入请求头,还有根据情况决定是否写入请求体。随后读响应头闭构造一个Response。 
里面具体是如何实现呢,我们看Http1xStream: 
首先是写头:
@Override public void writeRequestHeaders(Request request) throws IOException {
String requestLine = RequestLine.get(
request, streamAllocation.connection().route().proxy().type());
writeRequest(request.headers(), requestLine);
}

构造好请求行,进入writeRequest:
/** Returns bytes of a request header for sending on an HTTP transport. */
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException(“state: ” + state);
sink.writeUtf8(requestLine).writeUtf8(“\r\n”);
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(“: “)
.writeUtf8(headers.value(i))
.writeUtf8(“\r\n”);
}
sink.writeUtf8(“\r\n”);
state = STATE_OPEN_REQUEST_BODY;
}

这里就一目了然了,就是一行行的写请求行和请求头到sink中 
再看readResponse:
/** Parses bytes of a response header from an HTTP transport. */
public Response.Builder readResponse() throws IOException {
if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
throw new IllegalStateException(“state: ” + state);
}

try {
while (true) {
StatusLine statusLine = StatusLine.parse(source.readUtf8LineStrict());

Response.Builder responseBuilder = new Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
.headers(readHeaders());

if (statusLine.code != HTTP_CONTINUE) {
state = STATE_OPEN_RESPONSE_BODY;
return responseBuilder;
}
}
} catch (EOFException e) {
// Provide more context if the server ends the stream before sending a response.
IOException exception = new IOException(“unexpected end of stream on ” + streamAllocation);
exception.initCause(e);
throw exception;
}
}

也是一样的,从source中读请求行和请求头 
最后看openResponseBody:
@Override public ResponseBody openResponseBody(Response response) throws IOException {
Source source = getTransferStream(response);
return new RealResponseBody(response.headers(), Okio.buffer(source));
}

这里说一下就是根据请求的响应把包裹InputStream的source再次封装,里面做一些控制逻辑,然后再封装成ResponseBody。 
例如FiexdLengthSource,就是期望获取到byte的长度是固定的值:
/** An HTTP body with a fixed length specified in advance. */
private class FixedLengthSource extends AbstractSource {
private long bytesRemaining;

public FixedLengthSource(long length) throws IOException {
bytesRemaining = length;
if (bytesRemaining == 0) {
endOfInput(true);
}
}

@Override public long read(Buffer sink, long byteCount) throws IOException {
if (byteCount < 0) throw new IllegalArgumentException(“byteCount < 0: ” + byteCount);
if (closed) throw new IllegalStateException(“closed”);
if (bytesRemaining == 0) return -1;

long read = source.read(sink, Math.min(bytesRemaining, byteCount));
if (read == -1) {
endOfInput(false); // The server didn’t supply the promised content length.
throw new ProtocolException(“unexpected end of stream”);
}

bytesRemaining -= read;
if (bytesRemaining == 0) {
endOfInput(true);
}
return read;
}

@Override public void close() throws IOException {
if (closed) return;

if (bytesRemaining != 0 && !Util.discard(this, DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
endOfInput(false);
}

closed = true;
}
}

当读完期望的长度时就把这个RealConnection回收,如果少于期望的长度则抛异常。
ConnectionPool 
到了OkHttp3时代,ConnectionPool就是每个Client独享的了,我们刚才提到了ConnectionPool,那么他到底是如何运作呢。 
ConnectionPool持有一个静态的线程池。 
StreamAllocation不管通过什么方式,在获取到RealConnection后,RealConnection会添加一个对StreamAllocation的引用。 
在每个RealConnection加入ConnectionPool后,如果当前没有在清理,就会把cleanUpRunnable加入线程池。 
cleanUpRunnable里面是一个while(true),一个循环包括: 
调用一次cleanUp方法进行清理并返回一个long, 如果是-1则退出,否则调用wait方法等待这个long值的时间 
cleanUp代码如下:
ong cleanup(long now) {
int inUseConnectionCount = 0;
int idleConnectionCount = 0;
RealConnection longestIdleConnection = null;
long longestIdleDurationNs = Long.MIN_VALUE;

// Find either a connection to evict, or the time that the next eviction is due.
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();

// If the connection is in use, keep searching.
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++;
continue;
}

idleConnectionCount++;

// If the connection is ready to be evicted, we’re done.
long idleDurationNs = now – connection.idleAtNanos;
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
}

if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {
// We’ve found a connection to evict. Remove it from the list, then close it below (outside
// of the synchronized block).
connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) {
// A connection will be ready to evict soon.
return keepAliveDurationNs – longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {
// All connections are in use. It’ll be at least the keep alive duration ’til we run again.
return keepAliveDurationNs;
} else {
// No connections, idle or in use.
cleanupRunning = false;
return -1;
}
}

closeQuietly(longestIdleConnection.socket());

// Cleanup again immediately.
return 0;
}

遍历每一个RealConnection,通过引用数目确定哪些是空闲的,哪些是在使用中,同时找到空闲时间最长的RealConnection。 
如果空闲数目超过最大空闲数或者空闲时间超过最大空闲时间,则清理掉这个RealConnection,并返回0,表示需要立刻再次清理 
否则如果空闲的数目大于0个,则等待最大空闲时间-已有的最长空闲时间 
否则如果使用中的数目大于0,则等待最大空闲时间 
否则 返回 -1,并标识退出清除状态 
同时如果某个RealConnection空闲后,会进入ConnectionPool.connectionBecameIdle方法,如果不可被复用,则被移除,否则立刻唤醒上面cleanUp的wait,再次清理,因为可能超过了最大空闲数目 
这样通过一个静态的线程池,ConnectionPool做到了每个实例定期清理,保证不会超过最大空闲时间和最大空闲数目的策略。
OkHttp3分析就到此结束了。