OKHTTP是什么?
百度一搜:”一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso) 用于替代HttpUrlConnection和Apache HttpClient(android API23 6.0里已移除HttpClient,现在已经打不出来)”
OKHTTP的开发
同步请求
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();//创建单例
Request request = new Request.Builder()//创建请求
.url("http://www.baidu.com")
.build();
Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();//执行请求
} catch (IOException e) {
e.printStackTrace();
}
异步请求
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();//创建单例
Request request = new Request.Builder()//创建请求
.url("http://www.baidu.com")
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG,"失败");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG,"成功");
}
});
同步异步的区别:同步就好比食堂窗口排队吃饭,一个窗口一个一个的排队,只有前一个人排队完之后,后一个人才能打着饭,
而异步就好比多了一个个人窗口,你就不必去等待前一个人,那个窗口专门为你开了个小灶。
接下来分析一下OKHTTP同步
第一步创建了OKHttpClient
这个类其实是OKHttp请求的一个客户端类,很多的功能都需要通过这个客户端类来进行转发,或者直接由这个客户端类来进行实现,创建的方式有两种,第一种是可以直接new OKHttpClient(),第二种就是像例子中这种以builder(建造者设计模式)这种形式创建的,并且可以设计一些参数来做一些事情比如readTimeout
OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(10,TimeUnit.SECONDS).build();
设置写入超时的时间,默认10s,timeout超时时间,unit时间单位,一般是TimeUnit.SECOND或者TimeUnit.MILLIMETER。
connectTimeout(long timeout, TimeUnit unit)
连接超时的时间
writeTimeout(long timeout, TimeUnit unit)
写入超时的时间,等等
进入Builder的构造方法中来看
public Builder() {
this.dispatcher = new Dispatcher(); //OKHttp核心类,分发器(同步异步请求的分发,后续会详细分析)
this.protocols = OkHttpClient.DEFAULT_PROTOCOLS;
this.connectionSpecs = OkHttpClient.DEFAULT_CONNECTION_SPECS;
this.proxySelector = ProxySelector.getDefault();
this.cookieJar = CookieJar.NO_COOKIES;
this.socketFactory = SocketFactory.getDefault();
this.hostnameVerifier = OkHostnameVerifier.INSTANCE;
this.certificatePinner = CertificatePinner.DEFAULT;
this.proxyAuthenticator = Authenticator.NONE;
this.authenticator = Authenticator.NONE;
this.connectionPool = new ConnectionPool(); //连接池(可以理解为客户端与服务端一个连接叫connection,而每一个connection都放在这个ConnectionPool中,由它来管理)
this.dns = Dns.SYSTEM;
this.followSslRedirects = true;
this.followRedirects = true;
this.retryOnConnectionFailure = true;
this.connectTimeout = 10000;
this.readTimeout = 10000;
this.writeTimeout = 10000;
}
通过OHKttp的build()方法就可以来完成整个的OKHttpClient的对象创建。
第二步创建Request
它其实代表的是我们请求的报文信息,也是根据建造者模式来创建的,例子中通过建造者模式设置了url,然后调用build()方法来完成整个的Request的创建
第三步需要创建一个Call对象
什么是Call对象,其实它代表实际的一个http请求,通过将request传入call中连接request 和response的桥梁,这里是同步异步请求的分水岭,前三步都是一样的。
第四步同步请求代码
通过执行execute方法来进行同步操作
OKHttp同步需要注意的是:发送请求之后,就会进入阻塞状态,直到收到响应。
接下来分析一下OKHTTP异步
跟同步的前三步骤一样
第四步通过enqueue(new Callback(){})请求,开启一个新的工作线程,然后它会让Okhttp请求都在这个工作线程里工作,通过两个回调onResponse 和 onFailure来告知异步请求的成功与失败
之前查看了OKHttpClient中Builder的构造方法,接来下看一下Request中Builder的构造方法
public Builder() {
this.method = "GET";
this.headers = new okhttp3.Headers.Builder();
}
从代码中能够看出,默认的方法是get请求,当然你也可以设置post请求,创建了Headers.Builder的内部类对象.来进行保存它的Head头部信息
public Request.Builder post(RequestBody body) {
return this.method("POST", body);
}
然后在继续查看一下.build()做了什么?
public Request build() {
if (this.url == null) {
throw new IllegalStateException("url == null");
} else {
return new Request(this); //直接创建了,把当前的build对象传递过去(就是把之前配置好的请求方式,url 头部都赋值给Request这个对象)
}
}
继续看一下Request的构造方法
private Request(Request.Builder builder) {
//该方法为其指定了所需要的请求方式
this.url = builder.url; //url
this.method = builder.method; //请求方式
this.headers = builder.headers.build();//头部信息
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
从代码上可知:构建了携带请求信息的Request对象
准备工作已经完成,接下来看一下是如何通过call.execute()进行同步请求的,查看代码
Call call = okHttpClient.newCall(request); 进入newCall方法
public Call newCall(Request request) {
return new RealCall(this, request);
}
也就是说它的实现操作都是在RealCall里操作的,查看RealCall
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client; //初始化好的OKHttpClient 对象
this.originalRequest = originalRequest;//初始化好的Request 对象
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);//赋值了一个重定向拦截器,后续会讲解拦截器
}
最后一步同步请求的执行方法execute() 进入查看代码
public Response execute() throws IOException {
synchronized(this) {
if (this.executed) {//这个判断的所要表达的是,这个OKHttp请求只能执行一次,如果false那就会报出异常
throw new IllegalStateException("Already Executed");
}
this.executed = true; //如果么有执行过那么就设置成true
}
Response var2;
try {
this.client.dispatcher().executed(this);//①通过OKHttpClient调用分发器执行
Response result = this.getResponseWithInterceptorChain();//②获取Response,这个是拦截器链的方法,一次调用拦截器来进行相应的操作
if (result == null) {
throw new IOException("Canceled");
}
var2 = result;
} finally {
this.client.dispatcher().finished(this); //③主动的回收同步的请求
}
return var2;
}
①执行的方法如下
private final Deque<RealCall> runningSyncCalls = new ArrayDeque();//它是在dispather类中定义的
synchronized void executed(RealCall call) {
this.runningSyncCalls.add(call); //其实就是把它添加到同步请求的队列当中
}
关于dispatcher主要干什么呢?
先有个大概的概念,其实dispather的作用就是维持Call请求发给它的一次状态,同时它也维护了一个线程池,用于执行网络请求,而call这个请求在执行任务的时候通过这个dispatcher分发器类,把它的任务推到我们执行的队列当中。然后来进行操作,如果操作完成再执行我们的等待队列的任务。
③执行的方法如下
void finished(RealCall call) {
this.finished(this.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) { //同步的时候是走不到这个方法里面,但是异步的时候会进入该方法
this.promoteCalls();
}
runningCallsCount = this.runningCallsCount();//④返回了正在执行的异步请求。和返回了正在执行的同步请求的数量总和
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {//如果正在执行的请求数为0的时候,并且它的回调不为空的时候,执行run方法
idleCallback.run();
}
}
public synchronized int runningCallsCount() {
return this.runningAsyncCalls.size() + this.runningSyncCalls.size();//正在执行的异步请求数+正在执行的同步请求数
}
在同步请求其实dispatcher只做了两件事,添加同步请求,移除同步请求。
同步总结:第一点,创建了一个OKHttpClient对象
第二点,构建了一个Request对象,通过OKHttpClient和Request对象,构建出Call对象
第三点,执行call的execute方法