文档章节

OkHttp完全解析(八)源码解析一

k
 kim366
发布于 2016/05/13 19:08
字数 1862
阅读 11
收藏 0
点赞 2
评论 0

转自:

http://blog.csdn.net/chenzujie/article/details/47061095    OKHttp源码解析(一)

http://blog.csdn.net/chenzujie/article/details/47093723    OKHttp源码解析(二)

http://blog.csdn.net/chenzujie/article/details/47158645    OKHttp源码解析(三)


在上一篇博客中,我们介绍了OKHttp的基本用法,这一篇我们将从源码角度来看下OKHttp是如何完成一些列的网络的操作的。 
我们知道在okhttpclient同步请求和异步请求调用的接口不一样,但它们最后都是殊途同归地走到Call里面的

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
        Call.ApplicationInterceptorChain chain = new Call.ApplicationInterceptorChain(0, this.originalRequest, forWebSocket);
        return chain.proceed(this.originalRequest);
    }

首先我们来看看调用execute和enqueue是如何走到`getResponseWithInterceptorChain的。

public Response execute() throws IOException {
        synchronized(this) {
            //执行过不能再执行if(this.executed) {
                thrownew IllegalStateException("Already Executed");
            }

            this.executed = true;
        }

        Response var2;
        try {
            //把这次请求加入到分发器里this.client.getDispatcher().executed(this);
            Response result = this.getResponseWithInterceptorChain(false);
            if(result == null) {
                thrownew IOException("Canceled");
            }

            var2 = result;
        } finally {
        //this.client.getDispatcher().finished(this);
        }

        return var2;
    }

上面代码14行用到了一个从OkHttpClient获得的Dispatcher然后把它加入到分发器里面的队列 executedCalls中,在完成的时候会remove掉

synchronized void executed(Callcall) {
        this.executedCalls.add(call);
    }

    synchronized void finished(Callcall) {
        if(!this.executedCalls.remove(call)) {
            throw new AssertionError("Call wasn\'t in-flight!");
        }
    }
再来看看异步如何走到getResponseWithInterceptorChain
publicvoid enqueue(Callback responseCallback) {
        this.enqueue(responseCallback, false);
    }

    void enqueue(Callback responseCallback, boolean forWebSocket) {
        synchronized(this) {
            if(this.executed) {
                thrownew IllegalStateException("Already Executed");
            }

            this.executed = true;
        }

        this.client.getDispatcher().enqueue(new Call.AsyncCall(responseCallback, forWebSocket, null));
    }
是不是和同步很像,最后都是调用分发器的enqueue,但和同步不同的是,同步传入enqueue方法的参数是Call,异步传入的是AsyncCall,这个是什么呢,这个是Call里面的一个内部类,而且是一个继承了Runnable的内部类,我们先来看看这个execute怎么操作
synchronized void enqueue(AsyncCall call) {
        //判断当前运行的线程是否超过最大线程数,以及同一个请求是否要超过相同请求同时存在的最大数目
        if(this.runningCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) {
            this.runningCalls.add(call);
            //将请求放到线程池里运行
            this.getExecutorService().execute(call);
        } else {
            //不满足运行条件放到后备队列里
            this.readyCalls.add(call);
        }

    }
从上面代码我们看到异步请求是有条件限制的,默认最多64个请求,而同一个请求默认最多同时存在5个
privateint runningCallsForHost(AsyncCall call) {
        int result = 0;
        Iterator var3 = this.runningCalls.iterator();

        while(var3.hasNext()) {
            AsyncCall c = (AsyncCall)var3.next();
            通过比较每个请求的url,一样代表同一个请求
            if(c.host().equals(call.host())) {
                ++result;
            }
        }

        return result;
    }
下面的两个参数可以通过从OKHttpClient getDispatch得到分发器,并根据分发器提提供的设置方法去修改,修改比较简单就不贴代码了 。
privateint maxRequests = 64;
    privateint maxRequestsPerHost = 5;
再来看看AsyncCall 的run里面的代码
publicfinalvoid run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(this.name);

        try {
            this.execute();
        } finally {
            Thread.currentThread().setName(oldName);
        }

    }
显然AsyncCall的execute才是核心
protected void execute() {
            boolean signalledCallback = false;

            try {
                Response e = Call.this.getResponseWithInterceptorChain(this.forWebSocket);
                if(Call.this.canceled) {
                    signalledCallback = true;
//取消call调用onFailure回调                       this.responseCallback.onFailure(Call.this.originalRequest, new IOException("Canceled"));
                } else {
                    signalledCallback = true;
                    //请求成功,回调onResponse
                    this.responseCallback.onResponse(e);
                }
            } catch (IOException var6) {
                if(signalledCallback) {
                    Internal.logger.log(Level.INFO, "Callback failure for " + Call.this.toLoggableString(), var6);
                } else {
                    this.responseCallback.onFailure(Call.this.engine.getRequest(), var6);
                }
            } finally {
                //完成请求,调用finish,从队列中清空
                Call.this.client.getDispatcher().finished(this);
            }

        }
在代码第五行我们又看到了getResponseWithInterceptorChain。 

我们继续看getResponseWithInterceptorChain里面的实现

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
        Call.ApplicationInterceptorChain chain = new Call.ApplicationInterceptorChain(0, this.originalRequest, forWebSocket);
        return chain.proceed(this.originalRequest);
    }
创建了一个ApplicationInterceptorChain ,并且第一个参数传入0,这个0是有特殊用法的,涉及到OKHttp里面的一个功能叫做拦截器,从getResponseWithInterceptorChain这个名字里其实也能看出一二。先看看proceed做了什么
public Response proceed(Request request) throws IOException {
            //先判断是否每个拦截器都有对应的处理,没有的话先继续新建ApplicationInterceptorChain ,并执行当前拦截器的intercept方法
            if(this.index < Call.this.client.interceptors().size()) {
                Call.ApplicationInterceptorChain chain = Call.this.new ApplicationInterceptorChain(this.index + 1, request, this.forWebSocket);
                return ((Interceptor)Call.this.client.interceptors().get(this.index)).intercept(chain);
            } else {
                return Call.this.getResponse(request, this.forWebSocket);
            }
        }
这里碰到一个拦截器,OKHttp增加了一个拦截器机制,先来看看官方文档对Interceptors 的解释 

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls. 
解释下就是拦截器可以用来转换,重试,重写请求的机制,再来看看官方文档里提供的例子 
首先自定义一个拦截器用于打印一些发送信息

classLoggingInterceptorimplementsInterceptor {
  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    Response response = chain.proceed(request);

    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}
然后使用的时候把它添加到okhttpclient
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new LoggingInterceptor());

Requestrequest = newRequest.Builder()
    .url("http://www.publicobject.com/helloworld.txt")
    .header("User-Agent", "OkHttp Example")
    .build();

Responseresponse = client.newCall(request).execute();
response.body().close();
当我们执行到proceed,就会去判断是否有拦截器有的话先执行拦截器里的intercept,而在intercept里一般会进行一些自定义操作并且调用procced去判断是否要继续执行拦截器操作还是直接去获取网络请求,我们看看procced做了什么
public Response proceed(Request request) throws IOException {
//判断还有拦截器需要执行不,生成新的ApplicationInterceptorChain并调用它的intercept去执行用户定义的操作
            if(this.index < Call.this.client.interceptors().size()) {
                Call.ApplicationInterceptorChain chain = Call.this.new ApplicationInterceptorChain(this.index + 1, request, this.forWebSocket);
                return ((Interceptor)Call.this.client.interceptors().get(this.index)).intercept(chain);
            } else {
                return Call.this.getResponse(request, this.forWebSocket);
            }
        }
上面的例子执行结果如下
INFO: Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example

INFO: Received response for https://publicobject.com/helloworld.txt in 1179.7ms
Server: nginx/1.4.6 (Ubuntu)
Content-Type: text/plain
Content-Length: 1759
Connection: keep-alive
拦截器的整个机制如下图

上图中把拦截器氛围应用拦截器和网络拦截器,其实这个取决于你在拦截器里做了哪方面的操作,比如改变了请求头部之类的就可以成为网络拦截器。

在处理完拦截器操作后,就进入到重要的getResponse方法,真正的去进行发送请求,处理请求,接收返回结果。

Response getResponse(Request request, boolean forWebSocket) throws IOException {
        RequestBody body = request.body();
        if(body != null) {
            //如果是post方式,处理一些头部信息
            Builder followUpCount = request.newBuilder();
            MediaType response = body.contentType();
            if(response != null) {
                followUpCount.header("Content-Type", response.toString());
            }

            long followUp = body.contentLength();
            if(followUp != -1L) {
                followUpCount.header("Content-Length", Long.toString(followUp));
                followUpCount.removeHeader("Transfer-Encoding");
            } else {
                followUpCount.header("Transfer-Encoding", "chunked");
                followUpCount.removeHeader("Content-Length");
            }
            request = followUpCount.build();
        }
        //新建HttpEngine,用于进行发送请求和读取答复的细节处理this.engine = new HttpEngine(this.client, request, false, false, forWebSocket, (Connection)null, (RouteSelector)null, (RetryableSink)null, (Response)null);
        int var11 = 0;

        while(!this.canceled) {
            HttpEngine var13;
            try {
                 //发送请求this.engine.sendRequest();
                //读取答复this.engine.readResponse();
            } catch (RequestException var8) {
                throw var8.getCause();
            } catch (RouteException var9) {
                var13 = this.engine.recover(var9);
                if(var13 != null) {
                    this.engine = var13;
                    continue;
                }

                throw var9.getLastConnectException();
            } catch (IOException var10) {
                var13 = this.engine.recover(var10, (Sink)null);
                if(var13 != null) {
                    this.engine = var13;
                    continue;
                }

                throw var10;
            }

            Response var12 = this.engine.getResponse();
            //得到该请求对应的后续请求,比如重定向之类的
            Request var14 = this.engine.followUpRequest();
            if(var14 == null) {
                if(!forWebSocket) {
                    this.engine.releaseConnection();
                }

                return var12;
            }

            ++var11;
            if(var11 > 20) {
                thrownew ProtocolException("Too many follow-up requests: " + var11);
            }

            if(!this.engine.sameConnection(var14.url())) {
                this.engine.releaseConnection();
            }

            Connection connection = this.engine.close();
            this.engine = new HttpEngine(this.client, var14, false, false, forWebSocket, connection, (RouteSelector)null, (RetryableSink)null, var12);
        }

        this.engine.releaseConnection();
        thrownew IOException("Canceled");
    }

可以看到如果是post请求,先做一定的头部处理,然后新建一个HttpEngine去处理具体的操作,通过sendRequest发送具体请求操作,readResponse对服务器的答复做一定处理,在代码52行处getResponse得到从服务器返回的Response,讲到这里,我们整个的流程大概疏通了,代码贴了很多,简单的可以用下面一张图概括 

整体看就是根据用户的请求Request放入到指定队列,并使用拦截器链的方式去执行我们的请求操作。 
上图中的sendRequest,缓存处理,readReponse,介于篇幅问题(本来想一篇写完的,结果发现篇幅好长,分几篇完成吧)将在后续的文章中继续介绍。


本文转载自:http://blog.csdn.net/oyangyujun/article/details/50039809

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
手写Android网络访问框架OkHttp(简易版)

开篇废话 趁着周末两天的时间,跟着大神的脚步,把我们经常使用的网络框架OkHttp的源码好好跟了一下,初次观看,确实非常容易钻进去,搞得云里雾里,在大神的指导下,才勉强把整个逻辑走通。...

进击的欧阳
05/06
0
0
Android Okhttp缓存:精细化每一个Request的CacheControl缓存控制策略(二)

Android Okhttp缓存:精细化每一个Request的CacheControl缓存控制策略(二) 之前我写的附录文章1,只是简单的使用缺省的方法实现Okhttp的缓存。现在使用CacheControl,精细化到每一个Reque...

开开心心过
2017/10/24
0
0
Android中流行的第三方库资源收集

1、GSON GSON是将JSON解析成POJO的Java库。GSON也可以将POJO解析成JSON。 2、OkHttp OKHttp是Android版Http客户端。非常高效,支持SPDY、连接池、GZIP和 HTTP 缓存。默认情况下,OKHttp会自动...

jdroid
2014/05/02
0
0
说说在 Android 中如何发送 HTTP 请求

客户端会向服务器发出一条 HTTP 请求,服务器收到请求后会返回一些数据给客户端,然后客户端再对这些数据进行解析与处理。 1 HttpURLConnection 可以使用 HttpURLConnection(官方推荐) 来发...

deniro
06/09
0
0
OkHttp3源码解析内部缓存

OkHttp3系列文章 OkHttp3 源码解析执行流程 OkHttp3 源码解析 连接池的复用 如果有了解过OkHttp的执行流程,可以知道,在拦截器链中有一个缓存拦截器CacheInterceptor,里面决定了是由缓存中...

Gillben
05/22
0
0
使用Android API最佳实践

本文由 伯乐在线 - imesong 翻译自 meetme。欢迎加入Android小组。转载请参见文章末尾处的要求。 写在前面 现在,Android应用程序中集成第三方API已十分流行。应用程序都有自己的网络操作和缓...

mingxun
2014/04/17
0
0
Retrofit源码分析之OKHttpCall

之前在Retrofit源码初探一文中我们提出了三个问题: 什么时候开始将注解中参数拼装成http请求的信息的? 如何产生发起http请求对象的? 如何将对象转换成我们在接口中指定的返回值的? 其中第...

低情商的大仙
05/13
0
0
Android逆向之旅---爆破一款资讯类应用「最右」防抓包策略原理分析

一、逆向分析 首先感谢王同学提供的样本,因为王同学那天找到我咨询我说有一个应用Fiddler抓包失败,其实对于这类问题,我一般都会这么回答:第一你是否安装Fiddler证书了,他说他安装了。第...

jiangwei0910410003
04/25
0
0
Android Retrifot2简单使用学习

简介 Retrofit是一个现在比较火的网络请求框架,它的底层是依靠okhttp实现的(okhttp也是一款很不错的框架之前有使用过),确切的讲,Retrofit是对okhttp的进一步封装,它功能强大,支持同步...

reggie1996
05/27
0
0
feign和okhttp的结合

背景 使用feign可以很方便的调用各种http接口 http请求神器之Feign 那么feign是如何做到的呢? 分析 本质上默认场景feign仍然是使用httpClient进行调用的。 通过声明式的RequestMapping等注解...

Mr_Qi
07/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

npm profile 新功能介绍

转载地址 npm profile 新功能介绍 npm新版本新推来一个功能,npm profile,这个可以更改自己简介信息的命令,以后可以不用去登录网站来修改自己的简介了 具体的这个功能的支持大概是在6这个版...

durban
7分钟前
0
0
Serial2Ethernet Bi-redirection

Serial Tool Serial Tool is a utility for developing serial communications, custom protocols or device testing. You can set up bytes to send accordingly to your protocol and save......

zungyiu
13分钟前
0
0
python里求解物理学上的双弹簧质能系统

物理的模型如下: 在这个系统里有两个物体,它们的质量分别是m1和m2,被两个弹簧连接在一起,伸缩系统为k1和k2,左端固定。假定没有外力时,两个弹簧的长度为L1和L2。 由于两物体有重力,那么...

wangxuwei
28分钟前
0
0
apolloxlua 介绍

##项目介绍 apolloxlua 目前支持javascript到lua的翻译。可以在openresty和luajit里使用。这个工具分为两种模式, 一种是web模式,可以通过网页使用。另外一种是tool模式, 通常作为大规模翻...

钟元OSS
35分钟前
0
0
Mybatis入门

简介: 定义:Mybatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。 途径:MyBatis通过XML文件或者注解的形式配置映射,实现数据库查询。 特性:动态SQL语句。 文件结构:Mybat...

霍淇滨
43分钟前
0
0
开发技术瓶颈期,如何突破

前言 读书、学习的那些事情,以前我也陆续叨叨了不少,但总觉得 “学习方法” 就是一个永远在路上的话题。个人的能力、经验积累与习惯方法不尽相同,而且一篇文章甚至一本书都很难将学习方法...

_小迷糊
44分钟前
0
0
安装tensorflow-XXX报错

报错: tensorflow-0.5.0-cp27-none-linux_x86_64.whl is not a supported wheel on this platform. 解决: wget https://bootstrap.pypa.io/get-pip.py sudo python2.7 get-pip.py sudo p......

Yao--靠自己
46分钟前
0
0
JVM学习手册(一):JVM模型

一直从事JAVA开发,天天和JVM打交道,仔细想想对JVM还真的不是特别了解,实在是不应该.周六看了许多资料,也算有点心得,记录一下。 JVM内存模型分为5个区域:方法区,堆,虚拟机栈,本地方法栈,程序计...

勤奋的蚂蚁
今天
0
0
转行零基础该如何学Python?这些一定要明白!

转行零基础学Python编程开发难度大吗?从哪学起?近期很多小伙伴问我,如果自己转行学习Python,完全0基础能否学会呢?Python的难度到底有多大?今天,小编就来为大家详细解读一下这个问题。...

猫咪编程
今天
2
0
205. Isomorphic Strings - LeetCode

Question 205. Isomorphic Strings Solution 题目大意:判断两个字符串是否具有相同的结构 思路:构造一个map,存储每个字符的差,遍历字符串,判断两个两个字符串中相同位置字符的差是否相同 Ja...

yysue
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部