OkHttp源码探索
来源:互联网 发布:mac mini怎么接显示器 编辑:程序博客网 时间:2024/05/17 03:44
之前一直在使用volley作为网络请求模块,但发现volley已经过时(源码还是很值得反复品味的),现在基本是Retrofit和OkHttp的天下了,于是把OkHttp拿来学习一下。
首先,看一个OkHttp的简单示例:
Request.Builder builder = new Request.Builder().url(url).get(); Request okRequest = builder.build(); Call call = mOkHttpClient.newCall(okRequest); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, final Response response) throws IOException { mHandler.post(new Runnable() { @Override public void run() { listener.onFetchFinished(response.body().string()); } }); } });
该类框架都比较类似,创建一个请求及回调,然后加入请求队列,后面就由框架来进行任务调度,和结果回调。具体的细节,后面使用时再慢慢看,现在先探索一下OkHttp的主路线。
首先, 把请求加入到任务队列:
//okhttp3.RealCall.java @Override public void enqueue(Callback responseCallback) { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } client.dispatcher().enqueue(new AsyncCall(responseCallback)); }
此方法就为避免单个请求多次添加,
// Dispatcher.java synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } } /** Returns the number of running calls that share a host with {@code call}. */ private int runningCallsForHost(AsyncCall call) { int result = 0; for (AsyncCall c : runningAsyncCalls) { if (c.host().equals(call.host())) result++; } return result; } 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; }
这个方法比较有意思,很明显有两个任务队列,一个是正在执行的队列(runningAsyncCalls)和一个等待队列(readyAsyncCalls);不仅限制了最大并发请求的数量,还对同一个域名的并发请求做了限制,当然单个应用可能请求的域名都是一样的,所以这个就得看实际情况了。
当前并发请求数量小于限制时,就把请求直接丢到线程池中执行;否则就把请求加入到等待队列。在代码中搜索readyAsyncCalls,看看等待队列中的任务是什么时候被执行的。看到下面的方法:
// Dispatcher.java private void promoteCalls() { if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall call = i.next(); if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningAsyncCalls.add(call); executorService().execute(call); } if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } }
搜索这个方法是在哪里被调用的:
可以看到,在任务结束、改变最大并发请求和改变域名最大并发请求时会调用该方法,这些肯定是在意料之中的。
接下来就跟踪一下任务是如何执行的,我们就从队列中的请求任务入手。
AsyncCall的继承关系如下:
abstract class NamedRunnable implements Runnablefinal class AsyncCall extends NamedRunnable
/** * Runnable implementation which always sets its thread name. */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(); } finally { Thread.currentThread().setName(oldName); } } protected abstract void execute();}
任务的执行放在了execute()中执行。我们来看看AsyncCall的execute()方法:
@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); } }
可以看出来,主要的内容是在getResponseWithInterceptorChain()里面,因为这个方法直接返回了Response,也就是请求结果。
private Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. 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 (!retryAndFollowUpInterceptor.isForWebSocket()) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor( retryAndFollowUpInterceptor.isForWebSocket())); Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest); return chain.proceed(originalRequest); }
这个方法的主要内容是得到一个列表interceptors,从字面上看,这个列表是用于请求拦截。通过断点跟踪后发现,这个列表确实重要,并且顺序也是非常的重要,从下面的代码慢慢分析。
//RealInterceptorChain.java @Override public Response proceed(Request request) throws IOException { return proceed(request, streamAllocation, httpStream, connection); } public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream, Connection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); 、、、 // Call the next interceptor in the chain. //注意这里的index+1了 RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpStream, connection, index + 1, request); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); 、、、 return response; }
省略一些异常情况处理后,很清晰的看到,从interceptors列表的第一项(index初始化为0)开始执行interceptor.intercept(next),并且参数是RealInterceptorChain链条的下一个节点。
@Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); streamAllocation = new StreamAllocation( client.connectionPool(), createAddress(request.url())); int followUpCount = 0; Response priorResponse = null; while (true) { if (canceled) { streamAllocation.release(); throw new IOException("Canceled"); } Response response = null; boolean releaseConnection = true; try { response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null); releaseConnection = false; } catch (RouteException e) { 、、、 } 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()); 、、、 request = followUp; priorResponse = response; } // end while }
这个方法的主体是一个死循环,有两种方式退出循环:一个是throw new IOException(“Canceled”),另一个是return response。也就是说,当请求被取消,或者得到结果了就结束返回了。再看看response是什么时候被赋值的:
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
又回到刚才的proceed(request)里面了,而proceed(request)里面用到了index+1,也就是这时会使用interceptors列表中的第二个interceptor来进行intercept(next),酱紫看来,这是一个递归的过程。只有当进行到chain中的某一个节点,能够得response而不是继续向下传递时,递归开始反转退栈。
跟踪代码看到interceptor的顺序为RetryAndFollowUpInterceptor——>BridgeInterceptor——>CacheInterceptor——>ConnectInterceptor——>CallServerInterceptor. 至于为何需要这么几层,还不是很清楚,还需要继续学习。
至此,okhttp的请求主流程基本介绍完毕,后面还有大量的东西需要去学习。
- OkHttp源码探索
- 探索发现之Okhttp
- okhttp探索(一)
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OkHttp源码分析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OkHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- OKHttp源码解析
- 距离和相似度度量的区别和联系
- 约瑟夫环——孩子们的游戏(圆圈中最后剩下的数)
- Android系统篇之----Binder机制和远程服务调用机制分析
- hdu5120Intersection+圆环的面积交
- POJ1061青蛙的约会
- OkHttp源码探索
- C#控制台基础 把子类复制给父类,调用子类的函数(用虚方法)
- 深入理解Java虚拟机(二)——HotSpot虚拟机对象
- 【35.20%】【CF 706D】Vasiliy's Multiset
- CNN初探(一)------AlexNet
- 您在什么情况下会用到虚方法?它与接口有什么不同?
- 对象小结
- 使用iperf工具测试infiniband的速度
- OpenCV中Mat的详解