前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OKHttp源码学习同步请求和异步请求(二)

OKHttp源码学习同步请求和异步请求(二)

作者头像
yuanyuan
发布2019-09-02 17:29:51
8080
发布2019-09-02 17:29:51
举报
文章被收录于专栏:小满小满

OKHttp get

代码语言:javascript
复制
 1 private void doGet(String method, String s) throws IOException {
 2         String url = urlAddress + method + "?sex=" + s;
 3         Request request = new Request.Builder().url(url).get().build();
 4         Response respone = okHttpClient.newCall(request).execute();
 5         if (respone.isSuccessful()) {
 6             Log.d("test", respone.body().string());
 7         } else {
 8             Log.d("test", "get failed");
 9         }
10 
11     }

在get请求,用到了 Request Response okHttpClient,分别学习一下这三个类

Request:用于构建一个HTTP请求,使用了建造这模式.如果它们的{@link #body}为null或者它本身是不可变的,那么这个类的实例是不可变的。

Response:用于构建一个HTTP响应。 这个类的实例不是不可变的:响应体是一次性的值,可能只消耗一次然后关闭。 所有其他属性都是不可变的。 <p>这个类实现{@link Closeable}。 关闭它只是关闭其响应主体。

okHttpClient:{@linkplain Call calls}的工厂,可用于发送HTTP请求并读取其响应。

我们创建一个OKHttpClient时,完成了如下初始化的工作:

代码语言:javascript
复制
1  public OkHttpClient() {
2     this(new Builder());
3   }
代码语言:javascript
复制
 1 OkHttpClient(Builder builder) {
 2     this.dispatcher = builder.dispatcher;
 3     this.proxy = builder.proxy;
 4     this.protocols = builder.protocols;
 5     this.connectionSpecs = builder.connectionSpecs;
 6     this.interceptors = Util.immutableList(builder.interceptors);
 7     this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
 8     this.eventListenerFactory = builder.eventListenerFactory;
 9     this.proxySelector = builder.proxySelector;
10     this.cookieJar = builder.cookieJar;
11     this.cache = builder.cache;
12     this.internalCache = builder.internalCache;
13     this.socketFactory = builder.socketFactory;
14 
15     boolean isTLS = false;
16     for (ConnectionSpec spec : connectionSpecs) {
17       isTLS = isTLS || spec.isTls();
18     }
19 
20     if (builder.sslSocketFactory != null || !isTLS) {
21       this.sslSocketFactory = builder.sslSocketFactory;
22       this.certificateChainCleaner = builder.certificateChainCleaner;
23     } else {
24       X509TrustManager trustManager = systemDefaultTrustManager();
25       this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
26       this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
27     }
28 
29     if (sslSocketFactory != null) {
30       Platform.get().configureSslSocketFactory(sslSocketFactory);
31     }
32 
33     this.hostnameVerifier = builder.hostnameVerifier;
34     this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
35         certificateChainCleaner);
36     this.proxyAuthenticator = builder.proxyAuthenticator;
37     this.authenticator = builder.authenticator;
38     this.connectionPool = builder.connectionPool;
39     this.dns = builder.dns;
40     this.followSslRedirects = builder.followSslRedirects;
41     this.followRedirects = builder.followRedirects;
42     this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
43     this.connectTimeout = builder.connectTimeout;
44     this.readTimeout = builder.readTimeout;
45     this.writeTimeout = builder.writeTimeout;
46     this.pingInterval = builder.pingInterval;
47 
48     if (interceptors.contains(null)) {
49       throw new IllegalStateException("Null interceptor: " + interceptors);
50     }
51     if (networkInterceptors.contains(null)) {
52       throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
53     }
54   }

new Builder()的时候做了什么呢?

代码语言:javascript
复制
 1  public Builder() {
 2       dispatcher = new Dispatcher();
 3       protocols = DEFAULT_PROTOCOLS;
 4       connectionSpecs = DEFAULT_CONNECTION_SPECS;
 5       eventListenerFactory = EventListener.factory(EventListener.NONE);
 6       proxySelector = ProxySelector.getDefault();
 7       cookieJar = CookieJar.NO_COOKIES;
 8       socketFactory = SocketFactory.getDefault();
 9       hostnameVerifier = OkHostnameVerifier.INSTANCE;
10       certificatePinner = CertificatePinner.DEFAULT;
11       proxyAuthenticator = Authenticator.NONE;
12       authenticator = Authenticator.NONE;
13       connectionPool = new ConnectionPool();
14       dns = Dns.SYSTEM;
15       followSslRedirects = true;
16       followRedirects = true;
17       retryOnConnectionFailure = true;
18       connectTimeout = 10_000;
19       readTimeout = 10_000;
20       writeTimeout = 10_000;
21       pingInterval = 0;
22     }

所以到这里明白了,new OKHttpClient()时初始化的参数其实是从Builder中获取到的。当然这些都是默认值,我们也可以重新设置一些值比如

okHttpClient.Builder() .readTimeout(30, TimeUnit.SECONDS) .build();

接下来再回到doGet方法的第四行看 OkHttpClient.newCall()返回一个RealCall对象并调用RealCall.enqueue()方法,最后会进入Dispacher.enqueue()方法中,这里会将RealCall对象放入线程池中调度执行。

代码语言:javascript
复制
1  /**
2    * Prepares the {@code request} to be executed at some point in the future.
3    */
4   @Override public Call newCall(Request request) {
5     return RealCall.newRealCall(this, request, false /* for web socket */);
6   }
代码语言:javascript
复制
  //RealCall 类中
1 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
2     // Safely publish the Call instance to the EventListener.
3     RealCall call = new RealCall(client, originalRequest, forWebSocket);
4     call.eventListener = client.eventListenerFactory().create(call);
5     return call;
6   }
代码语言:javascript
复制
//RealCall

第9行

代码语言:javascript
复制
client.dispatcher().executed(this);
Dispatcher是什么呢? dispatcher是new Builder的时候new的一个Dispatcher 对象
接下来接着看Dispatcher:何时执行异步请求的策略。
代码语言:javascript
复制
//Dispatcher类中
1   /** Used by {@code Call#execute} to signal it is in-flight. */    
2   synchronized void executed(RealCall call) {
3     runningSyncCalls.add(call);
4   }

到这里只是将人物添加到runningSyncCalls这个队列当中,那什么时候会执行这个任务呢?回到RealCall的execute方法,

代码语言:javascript
复制
dispatcher的executed方法运行结束之后会接着第10行  Response result = getResponseWithInterceptorChain();方法获取响应,最后调用Dispatcher的finished方法
代码语言:javascript
复制
1  /** Used by {@code Call#execute} to signal completion. */
2   void finished(RealCall call) {
3     finished(runningSyncCalls, call, false);
4   }
代码语言:javascript
复制
 1 private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
 2     int runningCallsCount;
 3     Runnable idleCallback;
 4     synchronized (this) {
 5       if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
 6       if (promoteCalls) promoteCalls();
 7       runningCallsCount = runningCallsCount();
 8       idleCallback = this.idleCallback;
 9     }
10 
11     if (runningCallsCount == 0 && idleCallback != null) {
12       idleCallback.run();
13     }
14   }

在这个finish方法中,runningSyncCalls队列做为参数calls被传入。

首先是从队列中移除请求,如果不能移除,则抛出异常;

然后调用runningCallsCount统计目前还在运行的请求,最后,如果正在运行的请求数为0表示Dispatcher中没有可运行的请求了,进入Idle状态,此时如果idleCallback不为null,则调用其run方法。下面是runningCallsCount()方法的实现:

代码语言:javascript
复制
1 public synchronized int runningCallsCount() {
2     return runningAsyncCalls.size() + runningSyncCalls.size();
3   }

至此,同步请求的执行流程分析完成。接下来学习异步请求的流程。

代码语言:javascript
复制
 1  private void doPost(String method, String s) {
 2         FormBody formBody = new FormBody.Builder().add("sex", s).build();
 3         RequestBody body =  RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "{\"sex\",\""+s+"\"}");
 4         Request request = new Request.Builder().url(urlAddress + method).post(body).build();
 5         okHttpClient.newCall(request).enqueue(new Callback() {
 6             @Override
 7             public void onResponse(Call arg0, Response arg1) throws IOException {
 8                 Log.d("test", arg1.body().string());
 9             }
10             @Override
11             public void onFailure(Call arg0, IOException arg1) {
12                 Log.d("test", "post failed");
13             }
14         });
15     }

异步请求会调用qnqueue方法:

代码语言:javascript
复制
1 @Override public void enqueue(Callback responseCallback) {
2     synchronized (this) {
3       if (executed) throw new IllegalStateException("Already Executed");
4       executed = true;
5     }
6     captureCallStackTrace();
7     eventListener.callStart(this);
8     client.dispatcher().enqueue(new AsyncCall(responseCallback));
9   }
代码语言:javascript
复制
在这里有一个新的类AsyncCall,AsyncCall是RealCall的一个内部类并且继承NamedRunnable,NamedRunnable实现了Runnable接口,并且在run方法中调用了需要子类复写的execute

接下来看dispatcher的enqueue的方法:

代码语言:javascript
复制
1 synchronized void enqueue(AsyncCall call) {
2     if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
3       runningAsyncCalls.add(call);
4       executorService().execute(call);
5     } else {
6       readyAsyncCalls.add(call);
7     }
8   }
代码语言:javascript
复制
这里先判断正在运行的异步请求的数量 如果小于maxRequests  并且与该请求相同的主机数量小于maxRequestsPerHost,也就是说符合放入runningAsyncCalls队列的要求,那么放入队列,然后将AsyncCall交给线程池;如果不符合,那么就放入到readyAsyncCalls队列中。

 当线程池执行AsyncCall任务时,它的execute方法会被调用,下面看AsyncCall的execute方法
代码语言:javascript
复制
 1 @Override protected void execute() {
 2       boolean signalledCallback = false;
 3       try {
 4         Response response = getResponseWithInterceptorChain();
 5         if (retryAndFollowUpInterceptor.isCanceled()) {
 6           signalledCallback = true;
 7           responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
 8         } else {
 9           signalledCallback = true;
10           responseCallback.onResponse(RealCall.this, response);
11         }
12       } catch (IOException e) {
13         if (signalledCallback) {
14           // Do not signal the callback twice!
15           Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
16         } else {
17           eventListener.callFailed(RealCall.this, e);
18           responseCallback.onFailure(RealCall.this, e);
19         }
20       } finally {
21         client.dispatcher().finished(this);
22       }
23     }

在execute方法中,首先是调用getResponseWithInterceptorChain()方法获取响应,然后获取成功后,就调用回调的onReponse方法,如果失败,就调用回调的onFailure方法。最后,调用Dispatcher的finished方法。

然后看dispatcher的finished方法:

代码语言:javascript
复制
1 /** Used by {@code AsyncCall#run} to signal completion. */
2   void finished(AsyncCall call) {
3     finished(runningAsyncCalls, call, true);
4   }
代码语言:javascript
复制
 1 private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
 2     int runningCallsCount;
 3     Runnable idleCallback;
 4     synchronized (this) {
 5       if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
 6       if (promoteCalls) promoteCalls();
 7       runningCallsCount = runningCallsCount();
 8       idleCallback = this.idleCallback;
 9     }
10 
11     if (runningCallsCount == 0 && idleCallback != null) {
12       idleCallback.run();
13     }
14   }

与同步调用不同的是最后一个参数是true所以会执行promoteCalls方法。

代码语言:javascript
复制
 1 private void promoteCalls() {
 2     if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
 3     if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
 4 
//遍历等待队列
 5     for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
 6       AsyncCall call = i.next();
 7 //如果当前请求的主机处理的请求数量小于最大数量就将该请求从等待队列移除并添加到runningAsyncCalls队列中,然后交给线程池
 8       if (runningCallsForHost(call) < maxRequestsPerHost) {
 9         i.remove();
10         runningAsyncCalls.add(call);
11         executorService().execute(call);
12       }
13 
14       if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
15     }
16   }
代码语言:javascript
复制
 至此,异步请求的过程学习完毕,不管是同步请求还是异步请求,最终都会调用getResponseWithInterceptorChain()方法进行具体的网络请求,接下来学习一下具体的网络请求  getResponseWithInterceptorChain()
代码语言:javascript
复制
有用的集合类
Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

参考一篇比较好的讲解Dispatcher的文章

https://www.cnblogs.com/laughingQing/p/7296486.html

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-04-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档