OkHttp-android网络请求框架
文章目录
基础使用方式
基础请求
不啰嗦,直接上代码
导包:
implementation 'com.squareup.okhttp3:okhttp:3.7.0' implementation 'com.squareup.okio:okio:1.12.0'
private void requestDataByOkhttp() {
//OkHttp是支持链式调用的,这里为了说明重点先区分开
//1.创建OkHttpClient
OkHttpClient client = new OkHttpClient();
//2.创建一个request,这个request中包含很多的请求参数,比如url,body,header等
Request request = new Request.Builder().url("https://www.baidu.com").build();
//3.将请求封装成一个任务,获得call对象
Call call = client.newCall(request);
//4.使用call发起请求,这里只写异步
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
}
post请求
private void postDtataByOkhttp() {
//1.创建OkHttpClient
OkHttpClient client = new OkHttpClient();
//2.创建要提交的数据封装成RequestBody类型
RequestBody body = RequestBody.create(MediaType.parse("len.hu"), "a");
//3.将参数封装创建request将参数封装创
Request request = new Request.Builder().url("https://www.baidu.com").post(body).build();
//4.发起请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
}
这里是基本使用,其实post有很多的用法,上次图片,文件,流等,具体可以参考跳转至OkHttp的用法
OkHttp的表层原理
使用的方法在使用的时候查询一下就ok了,作为代码人员,查看这是怎么做到的才是关键对吧
创建部分(okHttpClient,request,call)
1.okHttpClient
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
}
上图中是okHttpClient中的构造方法,了解build构建者模式跳转至build设计模式的一眼就明白了为什么可以链式调用了;在第一个构造方法中(以下简称A,第二个称为B),A中有个Builder对象,那么来看这个对象
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
this.eventListenerFactory = okHttpClient.eventListenerFactory;
this.proxySelector = okHttpClient.proxySelector;
this.cookieJar = okHttpClient.cookieJar;
this.internalCache = okHttpClient.internalCache;
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
this.authenticator = okHttpClient.authenticator;
this.connectionPool = okHttpClient.connectionPool;
this.dns = okHttpClient.dns;
this.followSslRedirects = okHttpClient.followSslRedirects;
this.followRedirects = okHttpClient.followRedirects;
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
this.writeTimeout = okHttpClient.writeTimeout;
this.pingInterval = okHttpClient.pingInterval;
}
看上去好像密密麻麻,其实就是一些成员变量对吧,那么这个意思即使我们在使用A构造方法时,事实上,Builder对象已经产生了,并且这个对象的默认值赋给了OKHttpClient中的对象。
好了,第一步的创建结束了,总体来说是使用构建者模式创建了一个okHttpClient对象,并且将成员变量赋了默认值,当然,对于某些成员变量,我们应当是需要自己去修改的,这些,比如说这几个:
connectTimeout、readTimeout、writeTimeout;修改的方式也很简单,先通过获取Builder对象再修改,也就是修改一下创建方式:
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(20,TimeUnit.MILLISECONDS)
.writeTimeout(20,TimeUnit.MILLISECONDS)
.readTimeout(20,TimeUnit.MILLISECONDS);
OkHttpClient client = builder.build();
//这里当然是可以链式调用的,这样写只是为了看起来方便一下
对于这些成员变量有什么作用呢,后面提到再说。
####2. request
打开ruquest的方法发现构造方法
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
同样有一个builder对象,那么这肯定也是符合链式调用的,然后说一下参数:url不多说了,method方法,headers请求头,body请求体,tag请求标志,这些弄不清楚的话可以自行百度。
3.call
public interface Call extends Cloneable {
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
Call clone();
interface Factory {
Call newCall(Request request);
}
}
为了看起来清晰,我将源码中的注释删除了,可以看到call是一个接口,这个接口中还有一个接口,内部的接口中有方法可以获得Call对象,那么外部的接口被谁实现了呢,当然是okHttpClient了,不明白?
Call call = client.newCall(request);
我们是通过client.newCall(request);来获取call对象的对吧,好了到这里,创建部分就已经理解清楚了,(其实这还不是最终的call,在源码中还有一个realCall)
okHttp核心原理
掌握上面的就大致明白了基础原理了,但是离彻底掌握还有点距离,下面看核心原理。
先上一张图
一步一步来吧
创建realCall
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
上面聊到这里,可以看到有三个参数被传递了,继续看
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// TODO(jwilson): this is unsafe publication and not threadsafe.
this.eventListener = eventListenerFactory.create(this);
}
这个构造方法就是在原有的call上增加了一个拦截器(后面会聊到)RetryAndFollowUpInterceptor和一个事件监听。
realCall的enqueue方法
现在这个realCall已经有了,接着往下看
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
接着看enqueue这个方法:(我们同样只研究异步请求)
@Override public void enqueue(Callback responseCallback) {
//第一部分
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//第二部分
captureCallStackTrace();
//第三部分
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
1.第一部分是一个同步线程锁,这个是因为同一个call只能请求一次,如果执行两次那么就会抛出异常。
2.第二部分这个方法可以看到主要是为retryAndFollowUpInterceptor增加了一个callStackTrace,主要是用来追踪堆栈的信息。然后是前面创建的eventevenListener进行了一个回调。
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
3.第三部分就到了我们这个异步请求的核心
client.dispatcher().enqueue(new AsyncCall(responseCallback));
继续向下看enqueue()这个方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以看到这个方法同样加了锁,这里调用这个方法,传了一个response的callback进来,这里看一下这个图
图结合代码就可以看出来,dispatcher.enqueue方法new 了一个AsyncCall(responseCallback)并传入了responseCallback,在这个方法内部,先做正在运行的请求数判断,如果没有大于同步请求的最大size,就调用runningAsyncCalls.add(call)加入这个队列,如果大于了,就先加到准备队列。
然后看一下这行代码
线程池执行任务
executorService().execute(call);
进入这个executorService()
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;
可以看到,创建了一个线程池,线程池的最大长度无限制,但如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。到这里,其实最终就是依靠线程池执行了任务,那么就关注一下这个rennable吧(其实就是executorService().execute(call)中的call),OK,进去看一眼:
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
... ...
@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);
}
}
}
构造方法很简单,看execute方法把,注意到
getResponseWithInterceptorChain()获取response
getResponseWithInterceptorChain();//这个方法执行得到了结果
那么就进入:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//创建一个List寸拦截器
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 (!forWebSocket) {
//添加自定义网络拦截器
interceptors.addAll(client.networkInterceptors());
}
//添加请求服务拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));
//8.把这些拦截器们一起封装在一个拦截器链条上面
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
//执行proceed方法
return chain.proceed(originalRequest);
}
进入这个方法:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection 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.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != 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, httpCodec, connection, index + 1, request);
//获取当前index对应的拦截器里面的具体的某个拦截器,
Interceptor interceptor = interceptors.get(index);
//然后执行拦截器的intercept方法,同时传入新的RealInterceptorChain对象(主要的区别在于index+1了)
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != 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;
}
注意注释的两行,在proceed的方法内,又实例化了一个RealInterceptorChain,然后取出拦截器,执行方法,注意
Response response = interceptor.intercept(next);
intercept这是一个借口中的方法,所以执行这个就相当于每个实现类都会执行,于是在这一步,每个拦截器都会执行这个方法,各司其职,各自完成自己的功能,至于每个拦截器完成怎样的功能,分析的文章太多了,可以参考点击跳转到拦截器分析
说两句
以上只分析了异步处理,其实不管是异步还是同步,都是一样的三部曲:
1.加入到Dispatcher里面的同步(或异步)队列
2.执行getResponseWithInterceptorChain方法
3.从Dispatcher里面的同步(或异步)队列移除。(只不过同步操作是直接运行了getResponseWithInterceptorChain方法,而异步是通过线程池执行Runnable再去执行getResponseWithInterceptorChain方法)