OkHttp源码解析

OKHttp简介

OkHttp是一款优秀的HTTP框架,它支持get请求和post请求,支持基于Http的文件上传和下载,支持加载图片,支持下载文件透明的GZIP压缩,支持响应缓存避免重复的网络请求,支持使用连接池来降低响应延迟问题。OkHttp由Square公司开发,是目前Android最热门的网络框架之一。

OKHttp特点

(1)支持HTTP2/SPDY
(2)socket自动选择最好路线,并支持自动重连
(3)拥有自动维护的socket连接池,减少握手次数
(4)拥有队列线程池,轻松写并发
(5)拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩)基于Headers的缓存策略

为什么要用OkHttp?

目前Android开发中,主要的网络框架有HttpClient、Volley、HttpURLConnection、OkHttp。其中Android早就不推荐httpclient,5.0之后干脆废弃,6.0删除了HttpClient。所以HttpClient不考虑。Volley框架现在也已经不再升级了,故目前考虑使用的有、HttpURLConnection及OkHttp。相对HttpURLConnection,OkHttp使用更加便捷及灵活,且第三方社区活跃,相关资料齐全,成熟稳定性高。OkHttp也得到了官方的认可,并在不断优化更新,所以建议应用优先选择OkHttp作为网络框架。

为什么OKHttp很重要?

在Android网络请求库中,Retrofit是当下最热的一个网络请求库,Retrofit和OKHttp对比如下所示,Retrofit网络请求的工作本质上是OkHttp完成,而Retrofit仅负责网络请求接口的封装。
OkHttp源码解析_第1张图片
网络请求库对比.png

OkHttp的底层网络实现是什么?

OkHttp使用okio进行io的操作。okio是由square公司开发的,它补充了java.io和java.nio的不足,以便能够更加方便,快速的访问、存储和处理你的数据。OKHttp底层也是用该库作为支持。而且okio使用起来很简单,减少了很多io操作的基本代码,并且对内存和CPU使用做了优化。没有依赖其他的关于Http实现的库,底层使用了Socket,自己实现了Http1.X及2.X的协议。

OKHtttp缓存机制

HTTP缓存机制
HTTP报文就是客户端和服务器之间通信时发送及其响应的数据块,报文信息主要分为两部分:
(1)包含属性的头部(header),附加信息(cookie,缓存信息等),与缓存相关的规则信息,均包含在header中
(2)包含数据的主体部分(body)HTTP请求真正想要传输的部分;
缓存的几种方法:
(1)设置资源有效时间,超过指定时间重新想服务器获取资源,小于指定时间直接使用缓存;
(2)通过告知客户端资源最后修改时间,让客户端对比资源最后修改时间来确认是否使用缓存;
(3)通过告知客户端当前资源在服务器的唯一标识,客户端根据此标识确认资源是否有变换,来确认是否使用缓存。
OKHTTP的缓存的实现
原理
(1)OKHttp的网络缓存是基于HTTP缓存机制;
(2)使用DiskLruCache的缓存策略,通过LinkedHashMap实现LRU缓存算法,图片加载框架ImageLoader也是通过LinkedHashMap实现缓存机制。
注意事项:
(1)目前只支持GET,其他请求方式需要自己实现。
(2)需要服务器配合,通过head设置相关头来控制缓存
(3)创建OkHttpClient时候需要配置Cache
流程:
(1)如果配置了缓存,则从缓存中取出(可能为null)
(2)获取缓存的策略.
(3)监测缓存
(4)如果禁止使用网络(比如飞行模式),且缓存无效,直接返回
(5)如果缓存有效,使用网络,不使用网络
(6)如果缓存无效,执行下一个拦截器
(7)本地有缓存、根据条件判断是使用缓存还是使用网络的response
(8)把response缓存到本地
OKHTTP的缓存的实现位于CacheInterceptor类中,具体内容请参考:
OKHttp源码解析(六)--中阶之缓存基础
OKHttp源码解析(七)--中阶之缓存机制

OKHttp使用

步骤一:gradle引入库:implementation 'com.squareup.okhttp3:okhttp:(insert latest version)'
步骤二:初始化OkHttpClient对象

    OkHttpClient client;
    private void initClient(){
        client = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(15, TimeUnit.SECONDS)
                .build();
    }

步骤三:
同步请求

    public void okHttpSync() {
        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        Call call = client.newCall(request);
        try {
            Response response = call.execute();
            if (response.isSuccessful()) {
                System.out.println("response.code()==" + response.code());
                System.out.println("response.heard()==" + response.headers());
                System.out.println("response.message()==" + response.message());
                System.out.println("res==" + response.body().string());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

异步请求

    public void okHttpAsync() {
        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                System.out.println("url==" + call.request().url());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    System.out.println("response.code()==" + response.code());
                    System.out.println("response.heard()==" + response.headers());
                    System.out.println("response.message()==" + response.message());
                    System.out.println("res==" + response.body().string());
                }
            }
        });
    }

OKHTTP源码流程分析

OKHTTP同步请求流程分析
    OkHttpClient client;
    private void initClient(){
        client = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(15, TimeUnit.SECONDS)
                .build();
    }
    public void okHttpSync() {
        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        Call call = client.newCall(request);
        try {
            Response response = call.execute();
            if (response.isSuccessful()) {
                System.out.println("response.code()==" + response.code());
                System.out.println("response.heard()==" + response.headers());
                System.out.println("response.message()==" + response.message());
                System.out.println("res==" + response.body().string());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

(1)如上代码所示,先是new了一个OKHttpClient对象。OKhttp里面包含了很多对象,其实OKhttp的很多功能模块都包装进这个类,让这个类单独提供对外的API,这种外观模式的设计十分的优雅。外观模式。
(2)然后又new了一个Request对象,OKHttp的封装类Request和Response为了应用程序编程方便,会把一些常用的Header信息专门提取出来,作为局部变量。比如contentType,contentLength,code,message,cacheControl,tag...它们其实都是以name-value对的形势,存储在网络请求的头部信息中。
(3)然后调用OKHttpClient的newcall方法创建Call对象(RealCall对象),Call是HTTP请求任务封装,可以说我们能用到的操纵基本上都定义在这个接口里面了,
所以也可以说这个类是OKHttp类的核心类了。我们可以通过Call对象来操作请求了。代码如下所示:

OKHttpClient.java
    @Override 
    public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
    }

(4)执行RealCall.execute()方法开始网络请求

    @Override
    public Response execute() throws IOException {
        try {
            //将该RealCall添加到runningSyncCalls队列中。
            client.dispatcher().executed(this);
            //开始请求,并返回结果。
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } catch (IOException e) {
            eventListener.callFailed(this, e);
            throw e;
        } finally {
            //结束请求,从runningSyncCalls队列中移除该RealCall。
            client.dispatcher().finished(this);
        }
    }

(5)开始执行getResponseWithInterceptorChain()方法

    Response getResponseWithInterceptorChain() throws IOException {
        List interceptors = new ArrayList<>();
        //添加开发者应用层自定义的Interceptor
        interceptors.addAll(client.interceptors());
        //这个Interceptor是处理请求失败的重试,重定向    
        interceptors.add(retryAndFollowUpInterceptor);
        //这个Interceptor工作是添加一些请求的头部或其他信息
        //并对返回的Response做一些友好的处理(有一些信息你可能并不需要)
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        //这个Interceptor的职责是判断缓存是否存在,读取缓存,更新缓存等等
        interceptors.add(new CacheInterceptor(client.internalCache()));
        //这个Interceptor的职责是建立客户端和服务器的连接
        interceptors.add(new ConnectInterceptor(client));
        if (!forWebSocket) {
            //添加开发者自定义的网络层拦截器
            interceptors.addAll(client.networkInterceptors());
        }
        interceptors.add(new CallServerInterceptor(forWebSocket));
        //一个包裹这request的chain
        Interceptor.Chain chain = new RealInterceptorChain(
                interceptors, null, null, null, 0, originalRequest);
        //把chain传递到第一个Interceptor手中
        return chain.proceed(originalRequest);
    }

(6)执行RealInterceptorChain.proceed()方法

    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
                            RealConnection connection) throws IOException {
        // Call the next interceptor in the chain.
        //创建一个RealInterceptorChain实例
        RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
                connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
                writeTimeout);
        //取出下一个拦截器,如果有自定义Interceptor,先取出自定义Interceptor,
        //如果没有则先取出RetryAndFollowUpInterceptor,然后执行RetryAndFollowUpInterceptor.intercept()方法
        Interceptor interceptor = interceptors.get(index);
        Response response = interceptor.intercept(next);
        return response;
    }

(7)如果没有自定义Interceptor,则执行先RetryAndFollowUpInterceptor.intercept()方法

    @Override 
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Call call = realChain.call();

        while (true) {
            try {
                //继续调用RealInterceptorChain执行下一个拦截器的intercept方法()
                response = realChain.proceed(request, streamAllocation, null, null);
            }
            return response;
        }
    }

可以看出在RetryAndFollowUpInterceptor.intercept()会继续调用RetryAndFollowUpInterceptor.intercept()方法,然后继续执行下一个Interceptor.intercept()方法,直到RealCall.getResponseWithInterceptorChain()方法中的interceptor执行完毕,最后一个Interceptor是CallServerInterceptor,在CallServerInterceptor.intercept()方法中将response返回给上一个interceptor(),上一级再返回给上上一级,依次类推返回给第一个interceptor,这时候就返回到RealCall.execute()方法中

    @Override
    public Response execute() throws IOException {
        try {
            client.dispatcher().executed(this);
            //开始请求,并返回结果
            //最后拦截器把response返回给get请求的返回值
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } catch (IOException e) {
            eventListener.callFailed(this, e);
            throw e;
        } finally {
            //结束请求,从runningSyncCalls队列中移除该RealCall。
            client.dispatcher().finished(this);
        }
    }

至此同步请求大体流程已执行完毕,最后执行client.dispatcher().finished(this),从runningSyncCalls队列中移除该RealCall。
大体流程如下图所示:


OkHttp源码解析_第2张图片
同步请求大体流程.png

其中拦截器执行流程如下图所示:


OkHttp源码解析_第3张图片
拦截器工作流程.png

每个拦截器主要做两件事,第一是对Request进行处理,第二是对Response处理,对Request是处理时顺序执行的,对Response处理时倒序执行的,每个拦截器都有自己的作用,根据自己的职责和任务对Request和Response做响应的处理,如CacheInterceptor拦截器,根据Request请求报文的头部(header)有关缓存的设置,来判断是否使用缓存、判断缓存是否存在,读取缓存,更新缓存等等,最终将Response返回。下图有助于理解拦截器
OkHttp源码解析_第4张图片
拦截器.png
OKHTTP异步请求流程分析
    public void okHttpAsync() {
        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                System.out.println("url==" + call.request().url());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    System.out.println("response.code()==" + response.code());
                    System.out.println("response.heard()==" + response.headers());
                    System.out.println("response.message()==" + response.message());
                    System.out.println("res==" + response.body().string());
                }
            }
        });
    }

(1)异步请求调用RealCall.enqueue()方法

    @Override 
    public void enqueue(Callback responseCallback) {
        //先new一个AsyncCall,然后调用Dispatcher.enqueue()方法,
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

(2)NamedRunnable和AsyncCall代码如下

    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() {
            try {
                //执行NamedRunnable时会调用AsyncCall.execute方法;
                execute();
            } finally {
            }
        }
        protected abstract void execute();
    }
    
    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;
        
        //responseCallback即我们代码中call.enqueue(new CallBack()),请求完成后回调CallBack响应方法。
        AsyncCall(Callback responseCallback) {
            this.responseCallback = responseCallback;
        }

        //执行NamedRunnable时会调用AsyncCall.execute方法;
        @Override
        protected void execute() {
            boolean signalledCallback = false;
            try {
                //发起请求,并返回结果,和同步请求执行逻辑一致。
                Response response = getResponseWithInterceptorChain();
                if (retryAndFollowUpInterceptor.isCanceled()) {
                    signalledCallback = true;
                    //请求失败,回调CallBack.onFailure方法,
                    responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
                } else {
                    signalledCallback = true;
                    //请求成功,回调CallBack.onResponse方法
                    responseCallback.onResponse(RealCall.this, response);
                }
            } catch (IOException e) {

            } finally {
                //结束请求
                client.dispatcher().finished(this);
            }
        }
    }

AsyncCall.execute方法中的Response response = getResponseWithInterceptorChain();执行流程和同步请求一致,相关内容就不再过多介绍,然后根据返回结果回调我们代码中Callback相关方法。
(3)Dispatcher.enqueue方法如下

    private int maxRequests = 64; // 最大并发请求数为64
    private int maxRequestsPerHost = 5; //每个主机最大请求数为5
    synchronized void enqueue(RealCall.AsyncCall call) {
        //如果正在执行的请求小于设定值即64,并且请求同一个主机的request小于设定值即5
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            //将异步请求添加到执行队列,开始执行请求
            runningAsyncCalls.add(call);
            //获得当前线程池,没有则创建一个
            executorService().execute(call);
        } else {
            //当前异步请求队列已满,先添加到等待队列中,
            //待有异步请求队执行完毕后,再开始执行等待队列中的请求
            readyAsyncCalls.add(call);
        }
    }
    
    //获取线程池,没有的话创建一个
    public synchronized ExecutorService executorService() {
        if (executorService == null) {
            /*1、0:核心线程数量,保持在线程池中的线程数量(即使已经空闲),为0代表线程空闲后不会保留,等待一段时间后停止。
            2、Integer.MAX_VALUE:表示线程池可以容纳最大线程数量
            3、TimeUnit.SECOND:当线程池中的线程数量大于核心线程时,空闲的线程就会等待60s才会被终止,如果小于,则会立刻停止。
            4、new SynchronousQueue():线程等待队列。同步队列,按序排队,先来先服务
            5、Util.threadFactory("OkHttp Dispatcher", false):线程工厂,直接创建一个名为OkHttp Dispatcher的非守护线程。
            */  
            executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
                    new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
        }
        return executorService;
    }

(4)其中executorService().execute(call)方法会执行NamedRunnable.run()方法,
NamedRunnable.run()又会执行AsyncCall.execute()方法,AsyncCall.execute方法如下

        protected void execute() {
            boolean signalledCallback = false;
            try {
                //发起请求,并返回结果,和同步请求执行逻辑一致。
                Response response = getResponseWithInterceptorChain();
                if (retryAndFollowUpInterceptor.isCanceled()) {
                    signalledCallback = true;
                    //请求失败,回调CallBack.onFailure方法,
                    responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
                } else {
                    signalledCallback = true;
                    //请求成功,回调CallBack.onResponse方法
                    responseCallback.onResponse(RealCall.this, response);
                }
            } catch (IOException e) {

            } finally {
                //结束请求
                client.dispatcher().finished(this);
            }
        }

执行getResponseWithInterceptorChain()方法并返回结果,根据response来执行callback,所以我们得到了OKHTTP的大体流程,如下图


OkHttp源码解析_第5张图片
OKHttp大体流程.png

最后OKHttp整体流程如下所示:


OkHttp源码解析_第6张图片
OKHttp整体流程.png

参考资料:

OKHttp源码解析系列文章
Android:手把手带你深入剖析 Retrofit 2.0 源码
OkHttp源码学习随笔
OKHTTP
最后,感谢本文内容所参考文章的作者;

你可能感兴趣的:(OkHttp源码解析)