Retrofit使用OkHttp,Volley支持替换底层http栈为OkHttp,甚至Google的最新源码里,都用起了OkHttp,替换了原来用的HttpClient。
笔者虽然一直听说OkHttp怎么怎么好,但始终云里雾里究竟它如何优越,所以抽空瞄了瞄源码,小小分析一下。
okhttp官网
OkHttp在网络有问题的时候表现很好:
- 它会静默从常见的连接问题中恢复。
- 如果你的服务有多个IP地址对应,OkHttp会在首次连接失败的时候尝试其他地址。
- OkHttp使用现代的TLS features (SNI, ALPN) 来初始化连接, 并在握手失败的时候倒回到TLS 1.0。
OkHttp 2.0的API设计为流式builders和immutability,同时支持同步blocking call和异步带callback的call。
你可以不用重写网络层代码来试试OkHttp。okhttp-urlconnection模块实现了大家熟悉的java.net.HttpURLConnection API,而okhttp-apache模块实现了Apache HttpClient API.
OkHttp支持Android 2.3和以上,对Java要求至少1.7。
对了,OkHttp还用了Okio来做快速I/O和可调整大小的buffer。
分别来看看Get和Post请求吧
Get请求并获得response body:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
Post请求,看上去和上面的差别只在于Request的Builder多了.post(body):
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
这么多module,里面的类也不算少,怎么看起呢,不如就以使用里面的代码作为切入点吧。
作为入口,OkHttpClient里面包含了各种东西,OkHttpClient
:
// 之前提到的协议
private static final List DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
// TLS那些事
private static final List DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT);
// 这里实现了各种抽象方法,初始化了Intelnal单例,提供给各处使用
static {
Internal.instance = new Internal() {
}
/** 懒加载(HTTPS那些事) */
private static SSLSocketFactory defaultSslSocketFactory;
// 也是之前提过的,多个IP时候的选择策略
private final RouteDatabase routeDatabase;
// 异步请求执行时候的策略,用到了ExecutorService作线程池,默认创建的是core 0,max Integer.MAX_VALUE,keep alive 60秒的线程池
private Dispatcher dispatcher;
// 代理嘛,大家都懂得,分为DIRECT(直连无代理)、HTTP和SOCKS
private Proxy proxy;
private List protocols;
private List connectionSpecs;
// 拦截器 - 在Response正式返回前处理数据,比如Gzip解压
private final List interceptors = new ArrayList<>();
// 这个网络拦截器可就牛逼了。。。相较前面那个发生地更早,在HttpEngine的readResponse中被调用
// 可以做诸如CheckHandshake初始化的时候加进去了一个握手的拦截器去检查是否在黑名单;LoggingInterceptors则加了一个log的拦截器,输出请求和response的信息;Progress加了个包装response的拦截器,来增加progress功能;RewriteResponseCacheControl直接重写server的cache控制头
private final List networkInterceptors = new ArrayList<>();
// 代理服务器选择器
private ProxySelector proxySelector;
// Cookie处理器,可以接受get和put cookie的事件
private CookieHandler cookieHandler;
// 就是个接口
/** Non-null if this client is caching; possibly by {@code cache}. */
private InternalCache internalCache;
// 可以自定义,内部包含了上面的InternalCache实现
private Cache cache;
// 都是java的东西
private SocketFactory socketFactory;
private SSLSocketFactory sslSocketFactory;
private HostnameVerifier hostnameVerifier;
// 证书验证
private CertificatePinner certificatePinner;
// 回复服务器认证要求
private Authenticator authenticator;
// 连接池,同一个host的请求可能共享同一个connection,该类还实现了connection为以后的使用保持打开的策略
private ConnectionPool connectionPool;
// 接口,实际使用了InetAddress.getAllByName
private Network network;
private boolean followSslRedirects = true;
private boolean followRedirects = true;
private boolean retryOnConnectionFailure = true;
// 其实10_000就是10000,这里是10000毫秒即10秒
private int connectTimeout = 10_000;
private int readTimeout = 10_000;
private int writeTimeout = 10_000;
再看看构造函数:
public OkHttpClient() {
routeDatabase = new RouteDatabase();
dispatcher = new Dispatcher();
}
private OkHttpClient(OkHttpClient okHttpClient) {
// 反正就是各种参数拷贝过来
}
那么当我们调用new OkHttpClient()
的时候,其实就是new了两个成员变量。
嗯。。。好像很轻,还是看看具体的请求执行吧
Response response = client.newCall(request).execute();
看来具体执行是在Call
里面
public class Call {
private final OkHttpClient client;
// Guarded by this.
private boolean executed;
volatile boolean canceled;
/** The application's original request unadulterated by redirects or auth headers. */
Request originalRequest;
HttpEngine engine;
原来是Call
持有了HttpEngine
,Call是一个待准备执行的请求,可以被取消,由于它代表了单个的请求/回复 对,所以不能被执行两次。
具体execute的执行:
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
// 结果又交给了OkHttpClient的Dispatcher去执行
client.getDispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.getDispatcher().finished(this);
}
}
嗯…我们再跳Dispatcher
:
/** In-flight synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque executedCalls = new ArrayDeque<>();
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(Call call) {
executedCalls.add(call);
}
直接就丢进了队列里。坑爹呢?不执行了?!不是叫executed
嘛?!收拾好日了狗的心情,我们继续看下去…