简介
Http是现在应用常用的一种交换数据和媒体的网络方式,高效地使用http能让资源加载更快,节省带宽。Okhttp是一个高效的Http客户端,它的特性如下:
- 支持Http/2,允许所有同一个主机地址的请求共享同一个socket连接
- 连接池能够减少请求延时
- 透明的GZIP压缩能够减少响应数据的大小
- 缓存响应内容,避免一些完全重复的请求
当网络请求出现问题的时候okhttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个ip地址,当第一个ip请求失败时,Okhttp会交替尝试你配置的其他Ip,okhttp使用现代TLS技术初始化新的连接,当握手失败时会回退到TLS1.0
使用
异步GET请求
String url = "http://www.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
{
Log.d(TAG,"onFailure");
}
@Override
public void onResponse(Call call, Response response) throws IOException
{
Log.d(TAG,"onResponse: " + response.body().string());
}
});
异步发起的请求会被加入到Dispatcher中的runningAsyncCalls双端队列中通过线程池来执行
同步GET请求
它的最后一步是通过execute()来提交请求,这种方式会阻塞调用线程,所以在Android中应放在子线程中执行,否则可能引起ANR异常
String url = "http://www.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()
.build();
final Call call = okHttpClient.newCall(request);
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
Response response = call.execute();
Log.d(TAG,"run: "+response.body().string());
}catch (IOException e){
e.printStackTrace();
}
}
}).start();
POST方式提交String
这种方式与前面的区别就是在构造Request对象时,需要多构造一个RequestBody对象,用它来携带我们要提交的数据。在构造RequestBody时需要指定MediaType,用于描述请求/响应body的内容类型
MediaType mediaType = MediaType.parse("text/x-markdown;charset=utf-8");
String requestBody = "I'm Jdqm";
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(mediaType,requestBody))
.build();
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
{
Log.d(TAG,"onFailure:" + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException
{
Log.d(TAG,response.protocol()+" "+response.code() + " " + response.message());
Headers headers = response.headers();
for (int i=0;i<headers.size();i++){
Log.d(TAG,headers.name(i)+":"+headers.value(i));
}
Log.d(TAG,"onResponse: "+response.body().toString());
}
});
拦截器-interceptor
用户可以传入的interceptor分为两类:
-
一类是全局的interceptor,该类interceptor在整个拦截器链中最早被调用,通过OkHttpClient.Builder().addInterceptor(Interceptor)传入
-
另外一类是非网页请求的interceptor,这类拦截器只会在非网页请求中被调用,并且是在组装完请求之后,真正发起网络请求前被调用,所有的interceptor被保存在Listinterceptor集合中,按照添加顺序来逐个调用
其他
- 推荐让OkHttpClient保持单例,用同一个OkHttpClient实例来执行你的所有请求,因为每一个OkHttpClient实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个OkHttpClient实例,显然就是一种资源的浪费
- 每一个Call只能执行一次,否则会报异常
源码分析
在Okhttp中,其灵活性很大程度上体现在可以intercept任意一个环节,而这个优势便是okhttp整个请求响应架构体系的精髓所在
在Okhttp内部使用构造器模式初始化了一些配置信息:支持协议、任务分发器(其内部包含一个线程池,执行异步请求)、连接池(其内部包含一个线程池,维护connection)、连接/读/写超时时长等信息
public Builder() {
dispatcher = new Dispatcher(); //任务调度器
protocols = DEFAULT_PROTOCOLS; //支持的协议
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool(); //连接池
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;//超时时间
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
通过new Dispatcher()创建了一个任务调度器:Dispatcher,它定义了三个双向任务队列,其中有两个是异步队列:准备执行的请求队列readyAsyncCalls、正在执行的请求队列runningAsyncCalls;一个正在运行的同步请求队列runningSyncCalls
public final class Dispatcher {
private int maxRequests = 64; //最大请求数量
private int maxRequestsPerHost = 5; //每台主机最大的请求数量
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService; //线程池
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
/** 这个线程池没有核心线程,线程数量没有限制,空闲60s就会回收*/
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;
}
}
另外还有一个线程池executorService,这个线程池跟Android中的CachedThreadPoll非常类似,适用于大量的耗时较短的异步任务
接着会通过OkHttpclient和Request构造一个Call对象,它的实现是RealCall
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket){
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
在RealCall的构造方法中创建了一个RetryAndFollowUpInterceptor,用于处理请求错误和重定向等,默认情况下它也是第一个拦截器,除非调用了OkHttpClient.Builder.addInterceptor(Interceptor)来添加全局的拦截器。
RealCall.enqueue(Callback)
public void enqueue(Callback responseCallback) {
synchronized (this) {
//每个请求只能之执行一次
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
一个Call只能执行一次,否则会抛出异常,这里创建了一个AsyncCall并将Callback传入,接着再交给任务分发器Dispatcher来进一步处理
synchronized void enqueue(AsyncCall call) {
//正在执行的任务数量小于最大值(64),并且此任务所属主机的正在执行任务小于最大值(5)
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
从enqueue()中可以发现对请求的入队做了一些限制,若正在执行的请求数量小于最大值(默认64),并且此请求所属主机的正在执行任务小于最大值(默认5),就加入正在运行的队列并通过线程池来执行任务,否则加入准备执行队列中
AsyncCall
AsyncCall继承自NamedRunnable,而NamedRunnable实现了Runnable接口,他的作用有两个:
- 采用模板方法的设计模式,让子类将具体的操作放在execute()方法中
- 给线程指定一个名字,方便监控线程的活动状态
NamedRunnable
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
//采用模板方法让子类将具体的操作放到此execute()方法
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
AsyncCall
final class AsyncCall extends NamedRunnable {
//省略...
@Override protected void execute() {
boolean signalledCallback = false;
try {
//调用 getResponseWithInterceptorChain()获得响应内容
Response response = getResponseWithInterceptorChain(); //①
if (retryAndFollowUpInterceptor.isCanceled()) {
//这个标记为主要是避免异常时2次回调
signalledCallback = true;
//回调Callback告知失败
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
//回调Callback,将响应内容传回去
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//不管请求成功与否,都进行finished()操作
client.dispatcher().finished(this);//②
}
}
}
client.dispatcher().finish(this)
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
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;
}
//如果没有正在执行的任务,且idleCallback不为null,则回调通知空闲了
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
其中,promoteCalls()推动下一个任务执行,在条件满足的情况下, 将readyAsyncCalls中的任务移动到runningAsyncCalls中。并交给线程池来执行。
getResponseWithInterceptorChain()
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>(); //这是一个List,是有序的
interceptors.addAll(client.interceptors());//首先添加的是用户添加的全局拦截器
interceptors.add(retryAndFollowUpInterceptor); //错误、重定向拦截器
//桥接拦截器,桥接应用层与网络层,添加必要的头、
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//缓存处理,Last-Modified、ETag、DiskLruCache等
interceptors.add(new CacheInterceptor(client.internalCache()));
//连接拦截器
interceptors.add(new ConnectInterceptor(client));
//从这就知道,通过okHttpClient.Builder#addNetworkInterceptor()传进来的拦截器只对非网页的请求生效
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
//真正访问服务器的拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
interceptor会将前面的client.interceptors()全部加入其中,还有在创建的RealCall时的retryAndFollowUpInterceptor加入其中,接着还创建并添加了BridgeInterceptor、CacheInterceptor、ConnectInterceptor、CallServerInterceptor,最后通过RealInterceptorChain.proceed(Request)来执行整个interceptor chain