Android---网络编程之OkHttp3整体结构了解以及使用

相关

Android—网络编程之Http协议

Android—网络编程之OkHttp3整体结构了解以及使用

Android—网络编程之Okhttp3源码解析

Android—网络编程之Retrofit2整体结构了解以及+Okhttp3+rxjava2使用

Android—网络编程之Retrofit2源码分析

Android—网络编程之Socket编程(实例)

在这里为大家提供一种思路,看一款开源项目的时候,可以先看一下,这个开源框架包括那些类,哪些接口,根据注释大体了解一下各个类和接口的作用,这样有助于我们把握一个项目的全局架构,更好的理解一个好的开源框架。

OKHttp功能

大家应该都知道接口和抽象类的作用

  • 抽象类:用于模板设计,抽离共性,实现多态
  • 接口:用于功能的定制
那么okhttp中的接口都是些什么,定制了一些什么样的功能,可以一起来看一些(基于3.10.0版本)
  • Authenticator:响应服务器的身份验证,其实现类需要返回一个包含授权头的请求,也可以返回null,出发这种情况的调用者会接受这个响应!
  • Call:同步或异步执行一个请求,该请求能被取消,不能被执行两次!
  • Call.Factory:Call的工厂接口,以前的版本是Call的内部接口!
  • Callback:请求失败或成功的回调接口,只有两个方法:失败:onFailure(Call call, IOException e);成功:onResponse(Call call, Response response)。
  • Connection:返回HTTP、HTTPS或HTTPS+HTTP/2连接的套接字和流。连接可以直接指向源服务器或通过代理。这个类的典型实例是由HTTP客户端自动创建、连接和执行的。应用程序可以使用这个类来监视作为连接池成员的HTTP连接。
  • CookieJar:为HTTP cookie提供策略和持久性。策略(loadForRequest(HttpUrl url)):接受或拒绝cookie。持久性(saveFromResponse(HttpUrl url,List cookies)):保存cookie,内存,文件,数据库…
  • Dns:把主机名解析为IP地址的域名服务。大多数应用程序将使用默认的系统DNS服务,当然也可以自己实现来使用不同的DNS服务器,这个接口的实现必须是安全的并发使用。
  • EventListener.Factory:EventListener的工厂接口,将在3.11或 3.12版本完善这一系列的API,所有的启动/连接/获取事件最终都会收到一个匹配的结束/发布事件,或者成功的(非空参数),或者失败(非空的可转换的)的事件。
  • Interceptor:这就是OKHttp至关重要的拦截器接口了,观察、修改和潜在的短路请求,以及相应的响应。拦截器通常在在请求或响应中添加、删除或转换。
  • WebSocket:WebSocket的非阻塞式接口,管理web套接字的请求,发送,队列消息字节大小,取消和关闭。由其工厂类创建对象。
  • WebSocket.Factory:WebSocket的工厂接口。
通过以上接口的解析,我们了解到了OKHttp的大体功能,依附于这些功能接口,OKHttp是怎样实现的呢,下面我把OKHttp的类按照义务即他们在整体中担任的责任划分出了几个功能组:

网络客户端的功能搭建

通过上一篇HTTP的基础了解,我们知道利用HTTP通信需要设置请求头,对okhttp来说就是配置对象的属性,或者通过拦截器处理请求头和相应消息。而这些属性可以有以下类来实现。

  • Address:连接到源服务器的规范。主机名和端口。代理信息。对于安全连接,地址还包括SSL套接字工厂、主机名验证器和证书pinner。共享同一地址的HTTP请求也可以共享相同的连接。
  • Cache:对缓存的管理。
  • CacheControl:设置缓存策略。
  • CacheControl.Builder:构建一个Cache-Control请求头。builder模式,设置属性。
  • CertificatePinner:构建可信的证书。
  • CertificatePinner.Builder:构建一个已配置的证书
  • Challenge:配置一个安全的身份验证
  • CipherSuite:TLS密码组件。
  • ConnectionPool:连接池,管理HTTP和HTTP/2的链接以及重用的策略。
  • ConnectionSpec:指定在HTTP传输时的套接字连接的配置。对于https,这包括在协商安全连接时使用的TLS版本和加密组件。
  • ConnectionSpec.Builder:创建ConnectionSpec的对象,并设置其属性!
  • Cookie:cookie管理类
  • Cookie.Builder:实例化Cookie对象,但是Cookie的name, value, 和domain values必须在build()方法之前设置。
  • Credentials:创建HTTTP授权凭证的工厂
  • Dispatcher:执行异步请求时的策略。
  • EventListener:事件侦听器。扩展这个类以监视应用程序的HTTP调用的数量、大小和持续时间。(非最终API,将在3.11或3.12版本确定)

  • RFC:是一系列以编号排定的文件。文件收集了有关互联网相关信息,以及UNIX和互联网社区的软件文件。几乎所有的因特网标准都收录在RFC文件之中。

以上属性的配置都可以用OkHttpClient,OkHttpClient.Builder配置

  • OkHttpClient:实现的接口:Cloneable, Call.Factory, WebSocket.Factory。可以用来发送HTTP请求和读取响应的调用工厂。他适合创建单列,当创建单个OkHttpClient实例并将其用于所有的HTTP调用时,OkHttp执行得最好。这是因为每个客户机都有自己的连接池和线程池。重用连接和线程可以减少延迟并节省内存。相反,为每个请求创建客户端会浪费空闲池上的资源。OkHttp还使用了HTTP/2连接的守护线程。如果它们保持空闲,它们将自动退出。
  • OkHttpClient.Builder:创建OkHttpClient,设置OkHttpClient的一些属性。属性如下:
属性 类型 引用变量 设置方法
异步请求时的策略 Dispatcher dispatcher 构造里面默认初始化;也可以dispatcher(Dispatcher dispatcher) 设置
HTTP代理 Proxy proxy proxy(@Nullable Proxy proxy)方法设置
客户端配置的协议集合 List protocols 构造默认为http/1.1和h2的集合;protocols(List protocols)方法设置,目前支持以上两种协议。
套接字配置集合 List connectionSpecs 默认包含一个TLS链接和一个未加密的链接;connectionSpecs(List connectionSpecs)方法设置。
配置的拦截器集合 List interceptors addInterceptor(Interceptor interceptor)方法设置拦截器,可以调用多次。
单个网络请求和响应的拦截器列表 List networkInterceptors addNetworkInterceptor(Interceptor interceptor)配置使用 。
事件侦听器工厂对象 EventListener.Factory eventListenerFactory eventListener(EventListener eventListener)方法转换设置;eventListenerFactory(EventListener.Factory eventListenerFactory)方法直接设置。
设置指定的代理选择策略 ProxySelector proxySelector 默认使用系统范围的代理选择器;proxySelector(ProxySelector proxySelector)方法设置。
接收传入HTTP响应的cookie并提供其处理程序 CookieJar cookieJar 默认为NULL;cookieJar(CookieJar cookieJar) 设置。
设置用于读取和写入缓存响应的响应缓存 Cache;InternalCache cache;internalCache cache(@Nullable Cache cache)方法设置。setInternalCache(@Nullable InternalCache internalCache)。(Cache类内部实现了InternalCache接口)
创建连接的套接字工厂 SocketFactory socketFactory 默认使用系统的套接字工厂;socketFactory(SocketFactory socketFactory)方法设置。
安全HTTPS连接的套接字工厂 SSLSocketFactory sslSocketFactory 默认使用系统自带的;sslSocketFactory(SSLSocketFactory sslSocketFactory)或sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)方法设置,最好使用后者。
从Java构建的TLS api中返回的原始数组计算有效的证书链。 CertificateChainCleaner certificateChainCleaner 在设置Https的套接字工厂时被设置。
HTTPS链接请求主机名与响应证书的验证器 HostnameVerifier hostnameVerifier 默认设置OkHostnameVerifier的对象;hostnameVerifier(HostnameVerifier hostnameVerifier)方法设置。
约束哪些证书可信任的证书pinner CertificatePinner certificatePinner 构造器中初始化了一个pinne;certificatePinner(CertificatePinner certificatePinner)设置。
响应代理服务器身份验证器 Authenticator proxyAuthenticator proxyAuthenticator(Authenticator proxyAuthenticator)
响应源服务器的身份验证器。 Authenticator authenticator authenticator(Authenticator authenticator)
回收HTTP和HTTPS连接的连接池 ConnectionPool connectionPool 默认使用新的连接池;也可以用connectionPool(ConnectionPool connectionPool)方法设置。
查找主机名IP地址的DNS服务 Dns dns 不设置使用一个默认的DNS;用dns(Dns dns)设置自定义dns。

例如:下面就是一个基本的客户端配置

 OkHttpClient okHttpClient = new OkHttpClient.Builder()
                        //缓存配置
                        .cache(new Cache(new File(MainActivity.this.getCacheDir(),"test_cache"),1024*1024*1024))
                        .addInterceptor(new Interceptor() {
                            @Override
                            public Response intercept(Chain chain) throws IOException {
                                return null;
                            }
                        })//设置拦截器 可以对请求和响应做处理,比如缓存配置,打印响应流
//                        .addNetworkInterceptor()同上
                        .retryOnConnectionFailure(true)//开启失败重连
//                        .authenticator()响应服务器的身份验证,可以为NULL
//                        .certificatePinner()  配置证书
//                        .connectionPool() 配置连接复用池 , 默认5个 5分钟
//                        .connectionSpecs() 链接策略。默认包含一个TLS链接和一个未加密的链接
//                        .cookieJar() cookie 管理 默认为null
//                        .dispatcher()异步请求时的策略 默认已经初始化
//                        .dns()查找主机名IP地址的DNS服务 默认使用框架自带的DNS,也可以自己实现
//                        .followRedirects() 设置是否可以重定向 默认开启
//                        .followSslRedirects()从HTTPS到HTTP和从HTTP到HTTPS的重定向,默认遵循协议重定向
//                        .hostnameVerifier()HTTPS链接请求主机名与响应证书的验证器
//                        .pingInterval()设置此客户机发起的HTTP/2和web套接字ping之间的间隔。用它来自动发送ping帧,
//                                        直到连接失败或关闭。这使连接存在,并可检测连接性故障。
//                        .protocols()客户端配置的协议集合 默认为http/1.1和h2的集合
//                        .proxy()  HTTP代理
//                        .readTimeout()设置新连接的默认读取超时。值0表示没有超时,否则。当转换为毫秒时,值必须在1到Integer.MAX_VALUE之间。
//                        .socketFactory() 默认使用系统的套接字工厂
//                        .sslSocketFactory() 同上
//                        .writeTimeout(10, TimeUnit.SECONDS)设置新连接的默认写入超时。值0表示没有超时,否则。当转换为毫秒时,值必须在1到Integer.MAX_VALUE之间。
                        .build();

基本的属性已经完成配置,就到了真正发起网络请求了,下面的就属于请求功能类了

  • Request:一个HTTP请求。可以使用get或post方法,可以设置标记取消请求,可以设置此请求的cache-control头,已替换任何缓存控制头。
  • FormBody:继承RequestBody,表单请求封装。
  • FormBody.Builder:FormBody的内部类,创建FormBody对象并设置属性!
  • Handshake:TLS握手的记录。这个值对象描述了一个完成的握手。使用ConnectionSpec来设置新握手的策略。
  • Headers:单个HTTP消息的头字段。使用请求和对解释头的响应。这个类维护HTTP消息中的头字段的顺序。
  • Headers.Builder:Headers的内部类!创建Headers对象。
  • HttpUrl:一个统一资源定位器(URL),带有http或https的方案。使用这个类来编写和分解Internet地址。
  • HttpUrl.Builde:HttpUrl的内部类,创建HttpUrl对象并设置属性!
  • MediaType:RFC 2045媒体类型,用于描述HTTP请求或响应主体的内容类型。
  • MultipartBody:符合RFC 2387(1998年发布MIME Multipart /相关的内容类型)的请求体。
  • MultipartBody.Part:请求头(Headers)和请求体(RequestBody)的封装内部类。
  • ResponseBody:从源服务器到客户端应用程序的使用响应主体原始字节的一次性流。响应体必须被关闭:每个响应主体由一个有限的资源支持,比如套接字(实时网络响应)或一个打开的文件(用于缓存的响应)。如果未能关闭响应体,将会泄漏资源,并可能最终导致应用程序卡顿或崩溃。响应体只能使用一次。
  • Response:一个HTTP响应。响应体是一次性的。这个类实现Closeable。关闭它只是关闭它的响应主体。
Request request = new Request.Builder()
                        .url("http://c.3g.163.com/")//可以是string字符串,也可以是URL 或是一个HttpUrl
//                        .addHeader()添加请求头
//                        .header() 同上 方式不一样
//                        .headers()同上 方式不一样
//                        .cacheControl()配置请求时的缓存指令
//                        .delete() 请求时的方法
//                        .delete(new RequestBody() {
//                            @Nullable
//                            @Override
//                            public MediaType contentType() {
//                                return null;
//                            }
//
//                            @Override
//                            public void writeTo(BufferedSink sink) throws IOException {
//
//                            }
//                        })  同上
                        .get() //同上
//                        .head() //同上
//                        .method() //同上 自己配置请求方法 和 请求体
//                        .patch()同上
//                        .post()同上
//                        .put()同上
//                        .removeHeader() 移除请求头
//                        .tag() 给此请求配置标记,通过此标记可以取消此请求
                        .build();
                        okHttpClient.newCall(request)
//                        .execute()//同步请求
                        .enqueue(new Callback() { //异步请求  有回调
                            @Override
                            public void onFailure(Call call, IOException e) {
                                //请求失败回调方法
                            }

                            @Override
                            public void onResponse(Call call, Response response) throws IOException {
                                //请求成功回掉方法
                            }
                        });
  • Route:连接到抽象源服务器的具体路由。在创建连接时,可以有多个选择:
    1.HTTP代理:可以为客户端显式地配置代理服务器。否则将使用代理选择器。它可以返回多个代理来尝试。
    2.IP地址:是否直接连接到源服务器或代理,打开套接字需要一个IP地址。DNS服务器可能会返回多个IP地址来尝试。
    每个路径都是这些选项的特定选择。

  • WebSocketListener:WebSocket连接时的一些监听。

至此,我们结合框架中的类和功能接口,对okhttp框架的使用完整的梳理了一遍,这样我也有了深刻的印象,当我们需要怎样配置就可以怎样配置了。一个简单的网络请求(例子)。

 //缓存目录
        cache = new Cache(new File(this.getCacheDir(),"test_cache"),1024*1024*1024);
        //配置缓存策略
        cacheInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                //request = request.newBuilder().header("Cache-Control","public, max-age=3600").build(); 可以在这个地方配置 ,也可以在下面request配置
                if(!isNetworkAvailable(MainActivity.this)){
                    request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
                }
                Response response = chain.proceed(request);
                String cacheControl = request.cacheControl().toString();
                if(isNetworkAvailable(MainActivity.this)){
                    return response.newBuilder()
                            .header("Cache-Control",cacheControl)
                            .header("User-Agent","oktest")
                            .removeHeader("Pragma")
                            .build();
                }else {
                    return response.newBuilder()
                            .header("Cache-Control","public, " + CACHE_CONTROL_CACHE)
                            .header("User-Agent","oktest")
                            .removeHeader("Pragma")
                            .build();
                }
            }
        };
        //打印请求头 url;打印响应头 响应时间 响应体
        logInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                long t1 = System.nanoTime();
                Log.e("Sending request",request.url().toString());
                Log.e("request connection",chain.connection()+"");
                Log.e("request headers",request.headers().toString());
                Response response = chain.proceed(request);

                long t2 = System.nanoTime();
                Log.e("Received response for",response.request().url().toString());
                Log.e("response time",(t2 - t1) / 1e6d+"毫秒");
                Log.e("response headers",response.headers().toString());
                Log.e("response body",response.body().toString());
                //在这里也可以对response.body()操作处理
                return response;
            }
        };
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                        //缓存配置
                        .cache(cache)
                        .addInterceptor(cacheInterceptor)//设置拦截器 可以对请求和响应做处理,比如缓存配置,打印响应流
                        .addNetworkInterceptor(cacheInterceptor)//同上
                        .addInterceptor(logInterceptor)
                        .retryOnConnectionFailure(true)//开启失败重连
                        .build();
                Request request = new Request.Builder()
                        .url("https://blog.csdn.net/zcpHappy/article/details/79719141")//可以是string字符串,也可以是URL 或是一个HttpUrl
                        .header("User-Agent", "oktest") //同上 方式不一样
                        .get() //同上
                        .build();
                okHttpClient.newCall(request)
//                        .execute()//同步请求
                        .enqueue(new Callback() { //异步请求  有回调
                            @Override
                            public void onFailure(Call call, IOException e) {
                                //请求失败回调方法
                                tvShow.setText(e.getMessage());

                            }

                            @Override
                            public void onResponse(Call call, Response response) throws IOException {
                                //请求成功回掉方法
//                                String responseString = response.body().string();
                                InputStream inputStream = response.body().byteStream();
                                final StringBuilder stringBuilder = new StringBuilder();
                                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                                String  line;
                                try{
                                    while ((line = bufferedReader.readLine())!=null){
                                        stringBuilder.append(line+"\n");
                                    }

                                }catch (Exception e){
                                    e.printStackTrace();
                                }finally {
                                    try {
                                        inputStream.close();
                                    }catch (Exception eInput){
                                        eInput.printStackTrace();
                                    }
                                }
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        tvShow.setText(stringBuilder.toString());
                                    }
                                });
                            }
                        });

下面是有网络的情况下 请求头 响应头 输出情况

04-12 13:18:11.649 5065-5156/com.example.zcp.ok3test E/Sending request: https://blog.csdn.net/zcpHappy/article/details/79719141
04-12 13:18:11.649 5065-5156/com.example.zcp.ok3test E/request connection: null
04-12 13:18:11.649 5065-5156/com.example.zcp.ok3test E/request headers: User-Agent: oktest
                                                                        Cache-Control: public, max-age=3600
04-12 13:18:12.299 5065-5156/com.example.zcp.ok3test E/Received response for: https://blog.csdn.net/zcpHappy/article/details/79719141
04-12 13:18:12.299 5065-5156/com.example.zcp.ok3test E/response time: 651.625836毫秒
04-12 13:18:12.299 5065-5156/com.example.zcp.ok3test E/response headers: Server: openresty
                                                                         Date: Thu, 12 Apr 2018 05:18:12 GMT
                                                                         Content-Type: text/html; charset=UTF-8
                                                                         Transfer-Encoding: chunked
                                                                         Connection: keep-alive
                                                                         Keep-Alive: timeout=20
                                                                         Set-Cookie: uuid_tt_dd=10_9767942580-1523510292191-844897; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
                                                                         Set-Cookie: dc_session_id=10_1523510292191.777942; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
                                                                         Vary: Accept-Encoding
                                                                         Strict-Transport-Security: max-age= 31536000
                                                                         Cache-Control: public, max-age=3600
                                                                         User-Agent: oktest
04-12 13:18:12.299 5065-5156/com.example.zcp.ok3test E/response body: okhttp3.internal.http.RealResponseBody@4a7dcc80

有网络响应体截图:

这部分是无网络情况下使用缓存,请求头 响应头输出情况

04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/Sending request: https://blog.csdn.net/zcpHappy/article/details/79719141
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/request connection: null
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/request headers: User-Agent: oktest
                                                                         Cache-Control: public, max-age=3600
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/Received response for: https://blog.csdn.net/zcpHappy/article/details/79719141
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/response time: 2.990045毫秒
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/response headers: Server: openresty
                                                                          Date: Thu, 12 Apr 2018 05:18:12 GMT
                                                                          Content-Type: text/html; charset=UTF-8
                                                                          Transfer-Encoding: chunked
                                                                          Connection: keep-alive
                                                                          Keep-Alive: timeout=20
                                                                          Set-Cookie: uuid_tt_dd=10_9767942580-1523510292191-844897; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
                                                                          Set-Cookie: dc_session_id=10_1523510292191.777942; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
                                                                          Vary: Accept-Encoding
                                                                          Strict-Transport-Security: max-age= 31536000
                                                                          Cache-Control: public, max-age=3600
                                                                          User-Agent: oktest
04-12 13:25:13.174 5065-11442/com.example.zcp.ok3test E/response body: okhttp3.internal.http.RealResponseBody@4a854060

无网络,缓存中的响应输出

可以看到,配置缓存以后 请求到响应从651.625836毫秒下降到2.990045毫秒;当然配置缓存以后,在有网络的情况下也是使用的缓存

拼搏在技术道路上的一只小白And成长之路

你可能感兴趣的:(Android---网络编程)