面试准备之okhttp的使用及源码分析(一)
来源:互联网 发布:数据挖掘 关联规则 编辑:程序博客网 时间:2024/05/18 02:45
对于网络请求,一直都是在用一些优秀的开源框架如Vollery,okhttp,Retrofit等,虽然项目中一般不会让自己手写网络请求框架,但是我们在使用中不仅要会使用,还要“知其所以然”,特别现在面试还需要你至少看过一个项目的源码,正常开发中我们也应该多读别人的代码,来提高自己,
一、集成与使用
1、集成
Ecipse:下载jar包,放入libs文件夹下,
AndroidStudio:直接再当前项目的gradle内引入:
compile 'com.squareup.okhttp:okhttp:2.4.0'
2、使用
2.1、同步请求
2.1.1、get请求
//创建okhttpclient对象 OkHttpClient okHttpClient = new OkHttpClient(); //创建一个请求 Request request = new Request.Builder() .url("http://xxxx") .build(); //执行网络请求,并返回数据 Response response=okHttpClient.newCall(request).execute(); String data = response.body().string();
解释:首先构建okhttpClient对象,然后创建请求体,然后通过okHttpClient构建Call并执行execute()方法,返回数据,
注意:请求回的数据response会包含很多信息,例如head,body,内容在body内,然后调用string()方法,而不是toString()方法获取,
2.1.2、post请求
//创建okhttpclient对象 OkHttpClient okHttpClient = new OkHttpClient(); //创建body RequestBody formBody = new FormEncodingBuilder() .add("","") .build(); //通过url,body构建一个请求Request request = new Request.Builder() .url("http://www.xxxxxx") .post(formBody) .build();//执行网络请求,并返回数据 Response response=okHttpClient.newCall(request).execute();String data = response.body().string();
解释:post再官方文档中可以添加很多格式如JSON、文件、流等,此处的body构建方法是常用的简单参数请求方法,想了解更多可以参考此博客:http://blog.csdn.net/mynameishuangshuai/article/details/51303446,此处就不多分析了。
2.2、异步
//创建okhttpclient对象 OkHttpClient okHttpClient = new OkHttpClient(); //创建一个请求 Request request = new Request.Builder() .url("http://xxxx") .build();//创建一个callCall call =okHttpClient.newCall(request);//进行网络请求call.enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { //请求失败 Log.e("--json--"," shibaile "); } @Override public void onResponse(Response response) throws IOException { //请求成功 try { //获取数据,此线程为子线程,想要操作主线程需要使用runOnUiThread()方法 String jsondata =response.body().string(); } catch (JSONException e) { e.printStackTrace(); } } });
解释:异步与同步相比是因为调用了enqueue()方法实现了call.back,数据会通过back里的回调方法onResponse()传递过来。
二、源码分析
思路:
1、要分析源码,我们理所当然的要从头开始,再这里我们的头是什么?是okhttpClient,我们需要去okhttpClient类中看构造方法都做什么了记录下来,
2、然后看Request,这个请求体都包含什么内容,定义了什么,再往下,
3、然后就是newcall方法了,我们再去看okhttpClient的newcall方法内部调用那些方法再往下看,
4、最终我们再看call的enqueue()(异步调用)和execute()(同步调用)方法到底怎样讲我们的请求与服务器交互的,又是怎样实现了callback来实现数据的回传,基本大致就是这个思路,接下来我们分析:
**1、okhttpClient的构建:
直接看okhttpClient的构建方法:源码如下:**
//内部构造器 public OkHttpClient() { this(new Builder()); //调用下面的Builder()方法 }
接下来看new Builder()内都做了什么,Builder是OkHttpClient的内部类,如下:
final Dispatcher dispatcher; //分发器 final Proxy proxy; //代理 final List<Protocol> protocols; //协议 final List<ConnectionSpec> connectionSpecs; //传输层版本和连接协议 final List<Interceptor> interceptors; //拦截器 final List<Interceptor> networkInterceptors; //网络拦截器 final ProxySelector proxySelector; //代理选择 final CookieJar cookieJar; //cookie final Cache cache; //缓存 final InternalCache internalCache; //内部缓存 final SocketFactory socketFactory; //socket 工厂 final SSLSocketFactory sslSocketFactory; //安全套接层socket 工厂,用于HTTPS final CertificateChainCleaner certificateChainCleaner;//验证确认响应证书 final HostnameVerifier hostnameVerifier; // 主机名字确认 final CertificatePinner certificatePinner; // 证书链 final Authenticator proxyAuthenticator; //代理身份验证 final Authenticator authenticator; // 本地身份验证 final ConnectionPool connectionPool; //连接池,复用连接 final Dns dns; //域名 final boolean followSslRedirects; //安全套接层重定向 final boolean followRedirects; //本地重定向 final boolean retryOnConnectionFailure; //重试连接失败 final int connectTimeout; //连接超时时长 final int readTimeout; //read 超时 final int writeTimeout; //write 超时
这里main定义了很多如connectTimeout(连接超时时长),dns(域名)等再构建OkHttpClient时可以通过builde添加,如果不添加则去builde内部则默认定义这些参数:
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; }
2、来看Request的内容,request是通过其builder创建的,看下面:
Builder(Request request) { this.url = request.url; this.method = request.method; this.body = request.body; this.tag = request.tag; this.headers = request.headers.newBuilder(); }public HttpUrl url() { return url; } public String method() { return method; } public Headers headers() { return headers; } public String header(String name) { return headers.get(name); } public List<String> headers(String name) { return headers.values(name); } public @Nullable RequestBody body() { return body; } public Object tag() { return tag; } ...
解释:由上面源码可知我们构建Request时可以添加请求的url、设置get或者post请求的method 、body、tag等,类似一个实体类的bean,用来存储这些信息,
3、根据上面的第三部我们来看下newcall方法内部执行了什么:
newcall是由okhttpClient调用的,那我们去这里找一下,看源码:
/** * Prepares the {@code request} to be executed at some point in the future. */ @Override public Call newCall(Request request) { return RealCall.newRealCall(this, request, false /* for web socket */); }
我们可以看到这里返回的是RealCall,我们接下来去找RealCall.newRealCall()方法分析
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; }再看这里调用的RealCall 的构造方法,如下:private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { this.client = client; // @1 this.originalRequest = originalRequest; // @2 this.forWebSocket = forWebSocket; // @3 this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); // @4 }
解释:从上面可以看到
3.1、this.client 就是我们构建的OkHttpClient
3.2、this.originalRequest是我们上面构建包含url、method等的Request;
3.3,forWebSocket 是一个boolean类型的数据表示:
The application's original request unadulterated by redirects or auth headers//应用程序的原始请求不掺杂重定向或身份验证头,就是表示是否胃最开始的原始请求
3.4、我们可以卡他的源码声明表示这是一个:从失败中恢复,并根据需要跟踪重定向的拦截器,用于重定向的
4、接下来我们需要分析call的enqueue()(异步调用)和execute()(同步调用)具体是怎么实现与服务器通信的,由于上面知道我们这里的call是RealCall,所以我们去RealCall内部去找enqueue()和execute()方法:
4.1、execute()—–同步调用
看源码:
@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); // @1 executed = true; } captureCallStackTrace(); eventListener.callStart(this); try { client.dispatcher().executed(this); // @2 Response result = getResponseWithInterceptorChain(); // @3 if (result == null) throw new IOException("Canceled"); return result; } catch (IOException e) { eventListener.callFailed(this, e); throw e; } finally { client.dispatcher().finished(this); // 4 } }
由上面我们可以看到:
1、判断当前的call是否被执行过,一个call只能执行一次,如果想要一个完全一样的 call,可以利用 all#clone 方法进行克隆。
2、利用 client.dispatcher().executed(this) 来进行实际执行,dispatcher 是刚才看到的 OkHttpClient.Builder 的成员之一,它的文档说自己是异步 HTTP 请求的执行策略,现在看来,同步请求它也有掺和。
3、最终的Response 是由getResponseWithInterceptorChain()函数获取 HTTP 返回结果,从函数名可以看出,这一步还会进行一系列“拦截”操作。
4、最后还要通知 dispatcher 自己已经执行完毕
其实最重要的还是看respone是怎么来的,所以我们来看getResponseWithInterceptorChain():
Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. List<Interceptor> interceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); // @1 interceptors.add(retryAndFollowUpInterceptor); // @2 interceptors.add(new BridgeInterceptor(client.cookieJar())); // @3 interceptors.add(new CacheInterceptor(client.internalCache())); // @4 interceptors.add(new ConnectInterceptor(client)); // @5 if (!forWebSocket) { //上面定义的是否胃原始请求 interceptors.addAll(client.networkInterceptors()); // @6 } interceptors.add(new CallServerInterceptor(forWebSocket)); // @7 Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); return chain.proceed(originalRequest); // 8 }
由上面可知:
1、在配置 OkHttpClient 时设置的 interceptors;
2、负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
3、负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
4、负责读取缓存直接返回、更新缓存的 CacheInterceptor;
5、负责和服务器建立连接的 ConnectInterceptor;
6、配置 OkHttpClient 时设置的 networkInterceptors;
7、负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
8、开始链式调用
重点:整个网络请求这里最重要了,再上面我们知道interceptors 集合添加了各种参数,但是最终返回数据的是 8 位置,所以我们先看 8 这个位置是怎么样讲这些参数调用的:
4.1.1 RealInterceptorChain.proceed源码如下:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { //index由其构造器中传入,我们可以看到是0,当interceptors长度小于等于0时,抛出“声明错误”异常 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, call, eventListener, connectTimeout, readTimeout, writeTimeout); Interceptor interceptor = interceptors.get(index); 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"); } if (response.body() == null) { throw new IllegalStateException( "interceptor " + interceptor + " returned a response with no body"); } //将respone返回 return response; }
还是看根本找respone是怎么来的,
// Call the next interceptor in the chain. //在链中调用下一个拦截器。 RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); // @1 Interceptor interceptor = interceptors.get(index); // @2 Response response = interceptor.intercept(next); // @3
解释:
1、与我们上面getResponseWithInterceptorChain()内调用的方法一样,重新实例化下一个拦截器对应的RealIterceptorChain对象,
2、获取当前的interceptor,由上面知道interceptors是一个装载interceptor的List集合;
3、调用当前拦截器的intercept()方法,并将下一个拦截器的RealIterceptorChain对象传递下。
有上面的getResponseWithInterceptorChain()方法中我们知道她分贝添加的是
interceptors----retryAndFollowUpInterceptor---BridgeInterceptor==CacheInterceptor--ConnectInterceptor--networkInterceptors--CallServerInterceptor,
我们刚刚说过respone是当前拦截器调用intercept方法并将下一个拦截器的RealIterceptorChain对象传递下去,下面分析retryAndFollowUpInterceptor;(都说上面的方法是责任链模式,因为对此不精通所以没有说明,以后研究之后改正)
4.1.2 retryAndFollowUpInterceptor –重定向拦截器 ,我们看他重写的方法intercept:
@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); RealInterceptorChain realChain = (RealInterceptorChain) chain; Call call = realChain.call(); EventListener eventListener = realChain.eventListener(); streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()), call, eventListener, callStackTrace); int followUpCount = 0; Response priorResponse = null; while (true) { if (canceled) { streamAllocation.release(); throw new IOException("Canceled"); } Response response; boolean releaseConnection = true; try { response = realChain.proceed(request, streamAllocation, null, null); releaseConnection = false; } catch (RouteException e) { // The attempt to connect via a route failed. The request will not have been sent. //他试图通过一条线路连接失败。请求将不会被发送。 if (!recover(e.getLastConnectException(), false, request)) { throw e.getLastConnectException(); } releaseConnection = false; continue; } catch (IOException e) { // An attempt to communicate with a server failed. The request may have been sent. //试图与服务器通信的尝试失败了。请求可能已经发送了。 boolean requestSendStarted = !(e instanceof ConnectionShutdownException); if (!recover(e, requestSendStarted, request)) throw e; releaseConnection = false; continue; } finally { // We're throwing an unchecked exception. Release any resources. //我们抛出了一个未检查的异常。释放任何资源。 if (releaseConnection) { streamAllocation.streamFailed(null); streamAllocation.release(); } } // Attach the prior response if it exists. Such responses never have a body. if (priorResponse != null) { response = response.newBuilder() .priorResponse(priorResponse.newBuilder() .body(null) .build()) .build(); } Request followUp = followUpRequest(response); if (followUp == null) { if (!forWebSocket) { streamAllocation.release(); } return response; } closeQuietly(response.body()); if (++followUpCount > MAX_FOLLOW_UPS) { streamAllocation.release(); throw new ProtocolException("Too many follow-up requests: " + followUpCount); } if (followUp.body() instanceof UnrepeatableRequestBody) { streamAllocation.release(); throw new HttpRetryException("Cannot retry streamed HTTP body", response.code()); } if (!sameConnection(response, followUp.url())) { streamAllocation.release(); streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(followUp.url()), call, eventListener, callStackTrace); } else if (streamAllocation.codec() != null) { throw new IllegalStateException("Closing the body of " + response + " didn't close its backing stream. Bad interceptor?"); } request = followUp; priorResponse = response; } }
可以看出在response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);中直接调用了下一个拦截器,然后捕获可能的异常来进行操作,然后我们看下一个拦截器中的proceed
4.1.3 BridgeInterceptor 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应,我们看它重写的方法intercept():
@Override public Response intercept(Chain chain) throws IOException { Request userRequest = chain.request(); Request.Builder requestBuilder = userRequest.newBuilder(); RequestBody body = userRequest.body(); // @1 if (body != null) { MediaType contentType = body.contentType(); if (contentType != null) { requestBuilder.header("Content-Type", contentType.toString()); } long contentLength = body.contentLength(); if (contentLength != -1) { requestBuilder.header("Content-Length", Long.toString(contentLength)); requestBuilder.removeHeader("Transfer-Encoding"); } else { requestBuilder.header("Transfer-Encoding", "chunked"); requestBuilder.removeHeader("Content-Length"); } } if (userRequest.header("Host") == null) { requestBuilder.header("Host", hostHeader(userRequest.url(), false)); } if (userRequest.header("Connection") == null) { requestBuilder.header("Connection", "Keep-Alive"); } // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing // the transfer stream. boolean transparentGzip = false; if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) { transparentGzip = true; requestBuilder.header("Accept-Encoding", "gzip"); } List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url()); if (!cookies.isEmpty()) { requestBuilder.header("Cookie", cookieHeader(cookies)); } if (userRequest.header("User-Agent") == null) { requestBuilder.header("User-Agent", Version.userAgent()); } Response networkResponse = chain.proceed(requestBuilder.build()); // @2 HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers()); Response.Builder responseBuilder = networkResponse.newBuilder() .request(userRequest); if (transparentGzip && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding")) && HttpHeaders.hasBody(networkResponse)) { // @3 GzipSource responseBody = new GzipSource(networkResponse.body().source()); Headers strippedHeaders = networkResponse.headers().newBuilder() .removeAll("Content-Encoding") .removeAll("Content-Length") .build(); responseBuilder.headers(strippedHeaders); String contentType = networkResponse.header("Content-Type"); responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody))); } return responseBuilder.build(); }
解释:
1、1到2位置都是校验我们的请求Request,并且通过requestBuilder重新构建了一个能更规范的request,
2、调用下一个拦截器返回数据;
3、对返回的数据进行处理
接下来看下一个拦截器
4.1.4 CacheInterceptor 负责读取缓存直接返回、更新缓存的 ,看源码:
@Override public Response intercept(Chain chain) throws IOException { Response cacheCandidate = cache != null // @1 ? cache.get(chain.request()) //通过request得到缓存 : null; long now = System.currentTimeMillis(); CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get(); //根据request来得到缓存策略 @2 Request networkRequest = strategy.networkRequest; Response cacheResponse = strategy.cacheResponse; if (cache != null) { cache.trackResponse(strategy); } if (cacheCandidate != null && cacheResponse == null) { //存在缓存的response,但是不允许缓存 closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it. 缓存不适合,关闭 } // If we're forbidden from using the network and the cache is insufficient, fail. //如果我们禁止使用网络,且缓存为null,失败 if (networkRequest == null && cacheResponse == null) { return new Response.Builder() .request(chain.request()) .protocol(Protocol.HTTP_1_1) .code(504) .message("Unsatisfiable Request (only-if-cached)") .body(EMPTY_BODY) .sentRequestAtMillis(-1L) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); } // If we don't need the network, we're done. //没有网络请求,跳过网络,返回缓存 if (networkRequest == null) { return cacheResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .build(); } Response networkResponse = null; try { networkResponse = chain.proceed(networkRequest);//网络请求拦截器 // @3 } finally { // If we're crashing on I/O or otherwise, don't leak the cache body. //如果我们因为I/O或其他原因崩溃,不要泄漏缓存体 if (networkResponse == null && cacheCandidate != null) { closeQuietly(cacheCandidate.body()); } } // If we have a cache response too, then we're doing a conditional get. @4 //如果我们有一个缓存的response,然后我们正在做一个条件GET if (cacheResponse != null) { if (validate(cacheResponse, networkResponse)) { //比较确定缓存response可用 Response response = cacheResponse.newBuilder() .headers(combine(cacheResponse.headers(), networkResponse.headers())) .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); networkResponse.body().close(); // Update the cache after combining headers but before stripping the // Content-Encoding header (as performed by initContentStream()). //更新缓存,在剥离content-Encoding之前 cache.trackConditionalCacheHit(); cache.update(cacheResponse, response); return response; } else { closeQuietly(cacheResponse.body()); } } Response response = networkResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); if (HttpHeaders.hasBody(response)) { // =========(5) CacheRequest cacheRequest = maybeCache(response, networkResponse.request(), cache); response = cacheWritingResponse(cacheRequest, response); } return response; }
1、三元运算符判断是否有缓存,有的话直接通过缓存的request来获取respone,然后进行判断当前response是否有效,没有将cacheCandate赋值为空。
2、根据request判断缓存的策略,是否要使用了网络,缓存 或两者都使用
3、调用下一个拦截器,与服务器交互得到response
4、如果本地已经存在cacheResponse,那么让它和网络得到的networkResponse做比较,决定是否来更新缓存的cacheResponse
5、缓存未经缓存过的response
当没有缓存的时候我们需要去请求网络,接下来看如何请求建立链接的
4.1.5 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. //我们需要网络来满足这个请求。可能是为了验证条件GET。 boolean doExtensiveHealthChecks = !request.method().equals("GET"); HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks); RealConnection connection = streamAllocation.connection(); return realChain.proceed(request, streamAllocation, httpCodec, connection); }
实际上建立连接就是创建了一个 HttpCodec 对象,它将在后面的步骤中被使用,那它又是何方神圣呢?它是对 HTTP 协议操作的抽象,有两个实现:Http1Codec和Http2Codec,顾名思义,它们分别对应 HTTP/1.1 和 HTTP/2 版本的实现。
在Http1Codec中,它利用 Okio 对Socket的读写操作进行封装,Okio 以后有机会再进行分析,现在让我们对它们保持一个简单地认识:它对java.io和java.nio进行了封装,让我们更便捷高效的进行 IO 操作。
而创建HttpCodec对象的过程涉及到StreamAllocation、RealConnection,代码较长,这里就不展开,这个过程概括来说,就是找到一个可用的RealConnection,再利用RealConnection的输入输出(BufferedSource和BufferedSink)创建HttpCodec对象,供后续步骤使用。(还在研究这里)
4.1.6 NetworkInterceptors 配置OkHttpClient时设置的 NetworkInterceptors。就是正常的配置,
4.1.7 CallServerInterceptor 发送和接收数据
@Override public Response intercept(Chain chain) throws IOException { HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream(); StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation(); Request request = chain.request(); long sentRequestMillis = System.currentTimeMillis(); httpCodec.writeRequestHeaders(request); if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { // @1 Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength()); BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut); request.body().writeTo(bufferedRequestBody); bufferedRequestBody.close(); } httpCodec.finishRequest(); Response response = httpCodec.readResponseHeaders() // @2 .request(request) .handshake(streamAllocation.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); if (!forWebSocket || response.code() != 101) { response = response.newBuilder() .body(httpCodec.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; }
解释:
1、检查请求方法,用Httpcodec处理request
2、进行网络请求得到response,最终返回respone
同步请求总结:前面说了拦截器用了责任链设计模式,它将请求一层一层向下传,知道有一层能够得到Resposne就停止向下传递,然后将response向上面的拦截器传递,然后各个拦截器会对respone进行一些处理,最后会传到RealCall类中通过execute来得到esponse。
4.2异步请求
上面我们看的是call的同步,接下来看call的enqueue()(异步调用)
//异步调用的方法@Override 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)); }
看最好一行代码,调用了Dispatcher类中的enqueue(Call ),继续看:
synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
解释:如果中的runningAsynCalls不满,且call占用的host小于最大数量,则将call加入到runningAsyncCalls中执行,同时利用线程池执行call;否者将call加入到readyAsyncCalls中。runningAsyncCalls和readyAsyncCalls是什么呢?看下面:
/** 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<>(); //同步请求
call加入到线程池中执行了。现在再看AsynCall的代码,它是RealCall中的内部类:
//异步请求 final class AsyncCall extends NamedRunnable { private final Callback responseCallback; private AsyncCall(Callback responseCallback) { super("OkHttp %s", redactedUrl()); this.responseCallback = responseCallback; } String host() { return originalRequest.url().host(); } Request request() { return originalRequest; } RealCall get() { return RealCall.this; } @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); } } }
AysncCall中的execute()中的方法,同样是通过Response response = getResponseWithInterceptorChain();来获得response,这样异步任务也同样通过了interceptor,剩下的流程就和上面一样了。
五、整体流程图(正在绘制。。。)
六、总结:本文就okhttp的使用及源码进行分析,但是分析过程是由调用的方法去源码中查找,然后一点一点的拨开,感觉还是欠缺思路,在此特备感谢我参考两篇文章,给我指引了方向,如有侵权还请告知,接下还会继续阅读该源码,因为有的地方还么有吃透,再此提醒各位,看源码时不要太纠结于代码如何实现,“只见树木,不见森林”,要自己跳出来,希望本文对有需要得人能有所帮助,
参考博客:https://blog.piasy.com/2016/07/11/Understand-OkHttp/ 源码分析
参考博客:http://www.jianshu.com/p/27c1554b7fee 更透彻
如有侵权还请告知,立即改正!感谢生活、感谢科技、感谢分享!
- 面试准备之okhttp的使用及源码分析(一)
- OKHttp源码分析(一)
- OkHttp源码分析(一)
- 面试准备之最详细的Handler的使用、源码分析
- OKHTTP源码分析(一)异步方法
- OkHttp源码阅读之旅(一)
- OKHttp框架源码分析(一)
- 安卓面试清单----OKHttp源码解析(一)
- Okhttp使用和源码分析一(OkHttp2.x用法)
- Weevely使用及源码分析(一)
- Okhttp使用和源码分析三(OkHttp源码分析)
- (4.2.36.1)HTTP之OkHttp(一): Okhttp使用详解
- 做一个合格的程序猿之浅析Spring IoC源码(一)分析前的准备和思考
- 做一个合格的程序猿之浅析Spring IoC源码(一)分析前的准备和思考
- OkHttp 3.7源码分析(一)——整体架构
- OkHttp 3.7源码分析(一)——整体架构
- 关于面试的准备(一)
- Java菜鸟的面试准备(一)
- codevs 1983 等式问题 dfs
- git笔记
- 限制长度的字符串函数
- CSS属性的代码以及功能的说明
- caioj 1078 dp
- 面试准备之okhttp的使用及源码分析(一)
- 邮件发送(未加SSL)
- 机器学习笔记(三)之朴素贝叶斯
- 深度学习图像识别
- 51nod-1103-N的倍数
- Codevs 1039 数的划分
- fg、bg的区别
- Android监听自身被卸载与监听其他应用被卸载、安装
- jackson实体类转换json大小写及空字段忽略