OkHttp完全解析

来源:互联网 发布:服务器网管软件 编辑:程序博客网 时间:2024/05/17 20:26


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);    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

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

public Response execute() throws IOException {        synchronized(this) {            //执行过不能再执行            if(this.executed) {                throw new IllegalStateException("Already Executed");            }            this.executed = true;        }        Response var2;        try {            //把这次请求加入到分发器里            this.client.getDispatcher().executed(this);            Response result = this.getResponseWithInterceptorChain(false);            if(result == null) {                throw new IOException("Canceled");            }            var2 = result;        } finally {        //            this.client.getDispatcher().finished(this);        }        return var2;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

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

synchronized void executed(Call call) {        this.executedCalls.add(call);    }    synchronized void finished(Call call) {        if(!this.executedCalls.remove(call)) {            throw new AssertionError("Call wasn\'t in-flight!");        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

再来看看异步如何走到getResponseWithInterceptorChain

public void enqueue(Callback responseCallback) {        this.enqueue(responseCallback, false);    }    void enqueue(Callback responseCallback, boolean forWebSocket) {        synchronized(this) {            if(this.executed) {                throw new IllegalStateException("Already Executed");            }            this.executed = true;        }        this.client.getDispatcher().enqueue(new Call.AsyncCall(responseCallback, forWebSocket, null));    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

是不是和同步很像,最后都是调用分发器的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);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

从上面代码我们看到异步请求是有条件限制的,默认最多64个请求,而同一个请求默认最多同时存在5个

private int 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;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

下面的两个参数可以通过从OKHttpClient getDispatch得到分发器,并根据分发器提提供的设置方法去修改,修改比较简单就不贴代码了 。

private int maxRequests = 64;    private int maxRequestsPerHost = 5;
  • 1
  • 2
  • 1
  • 2

再来看看AsyncCall 的run里面的代码

 public final void run() {        String oldName = Thread.currentThread().getName();        Thread.currentThread().setName(this.name);        try {            this.execute();        } finally {            Thread.currentThread().setName(oldName);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

显然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);            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

在代码第五行我们又看到了getResponseWithInterceptorChain。 
我们继续看getResponseWithInterceptorChain里面的实现

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

创建了一个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);            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

class LoggingInterceptor implements Interceptor {  @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;  }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

然后使用的时候把它添加到okhttpclient

OkHttpClient client = new OkHttpClient();client.interceptors().add(new LoggingInterceptor());Request request = new Request.Builder()    .url("http://www.publicobject.com/helloworld.txt")    .header("User-Agent", "OkHttp Example")    .build();Response response = client.newCall(request).execute();response.body().close();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

当我们执行到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);            }        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上面的例子执行结果如下

INFO: Sending request http://www.publicobject.com/helloworld.txt on nullUser-Agent: OkHttp ExampleINFO: Received response for https://publicobject.com/helloworld.txt in 1179.7msServer: nginx/1.4.6 (Ubuntu)Content-Type: text/plainContent-Length: 1759Connection: keep-alive
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

拦截器的整个机制如下图这里写图片描述


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

在处理完拦截器操作后,就进入到重要的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) {                throw new 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();        throw new IOException("Canceled");    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

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


这里写图片描述

整体看就是根据用户的请求Request放入到指定队列,并使用拦截器链的方式去执行我们的请求操作。 

原创粉丝点击