OkHttp认识与使用

一、HTTP协议回顾:

1. HTTP协议概述

WEB浏览器与WEB服务器之间的一问一答的交互过程必须遵循一定的规则,这个规则就是HTTP协议。

2. HTTP是 Hyper Text Transfer Protocol(超文本传输协议)

  • 它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据 的过程及数据本身的格式。
  • Http协议是一种应用层协议,它通过TCP实现了可靠的数据传输,能够保证数据的完整性、正确性
  • 而TCP对于数据传输控制的优点也能够体现在Http协议上,使得Http的数据传输吞吐量、效率得到保证

3. 请求格式(用火狐看源码)

  • 请求:

请求行 : 请求方式 请求路径 版本
请求头 : 以key-value形式组成,K:V。。。
空行
请求体 : 用于数据传递:get方式没有请求体(参数地址传递) post方式有请求体

  • 响应:

响应行 :版本 响应码 响应信息
响应头 :以key-value形式组成,K:V。。。
空行
响应体 :响应正文

4. 常用请求头

  • Host: www.baidu.com
  • User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
    (User Agent用户代理,是Http协议中的一部分,属于头域的组成部分,User Agent也简称UA。它是一个特殊字符串头,是一种向访问网站提供你所使用的浏览器类型及版本、操作系统及版本、浏览器内核、等信息的标识。通过这个标识,用户所访问的网站可以显示不同的排版从而为用户提供更好的体验或者进行信息统计;例如用手机访问谷歌和电脑访问是不一样的,这些是谷歌根据访问者的UA来判断的。UA可以进行伪装。
  • Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
  • Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
  • Accept-Encoding: gzip, deflate, br
  • Connection: keep-alive
  • Cache-Control: max-age=0
  • Content-Type: text/html
  • Content-Length:120

5. 请求方式

Get:请求获取Request-URI所标识的资源
POST:在Request-URI所标识的资源后附加新的数据
HEAD: 请求获取由Request-URI所标识的资源的响应信息报头
PUT:请求服务器存储一个资源,并用Request-URI作为其标识
DELETE:请求服务器删除Request-URI所标识的资源
TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT:保留将来使用
OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项
Patch:是对PUT方法的补充,用来对已知资源进行局部更新

注意:

  1. GET方式(以在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔,通常传送的数据不超过1kb),
    通过请求URI得到资源。一般用于获取/查询资源信息
  2. POST方式(在请求的正文内容中向服务器发送数据,传送的数据无限制),
    用于向服务器提交新的内容。一般用于更新资源信息

6. 状态码

200(正常),206请求部分数据
302/307(临时重定向),
304(未修改),
404(找不到),
500(服务器内部错误)

7. Http协议的特点

①支持客户/服务器模式。
②简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、 HEAD、POST。每种方法规定了客户与服务器联系的类型不同。 由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
③灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
④无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求, 并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
⑤无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。 缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每 次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快

8. Http 1.0 与 Http1.1的区别

1.0协议,客户端与web服务器建立连接后,只能获得一个web资源! 而1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源!

二、网络通信

1. 网络三要素

IP:主机的唯一表示 (http://202.108.22.5/)
端口号:正在运行的程序(0~65535)
协议:通信规则,TCP以及UDP

2. 网络模型

2.1 简介

  • 定义:计算机网络的各层 + 其协议的集合
  • 作用:定义该计算机网络的所能完成的功能

2.2 结构介绍

  • OSI体系结构:概念清楚 & 理念完整,但复杂 & 不实用

  • TCP / IP体系结构:含了一系列构成互联网基础的网络协议,是Internet的核心协议 & 被广泛应用于局域网 和 广域网

  • 五层体系结构:融合了OSI 与 TCP / IP的体系结构,目的是为了学习 & 讲解计算机原理

网络模型

网络参考模型OSI概述:(物联网传话试用)
1. 物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。 指两台电脑进行传输数据的网线。
2. 数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
3. 网络层:主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。 将发送端电脑发送过来的数据, 通过路由器进行分配给指定IP的电脑上。
4. 传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。 主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。 定义两台电脑数据传输方式,称作协议,常用的协议有:TCP、UDP、HTTP。
5. 会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名) 要设置目标电脑的IP地址和端口号,才可以找到接收端的电脑和应用。
6. 表示层:主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等)。 将文字或者图片转换成电脑识别的二进制的过程.。
7. 应用层: 主要是一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西.就是终端应用)。

3. TCP与UDP区别:

  • TCP:
    建立连接
    安全可靠协议
    以流进行数据传递,无大小限制
    三次握手协议,四次挥手

  • UDP:
    不建立连接
    不可靠协议
    以数据包传递,有大小限制64K

04. Socket以及Http:

  • Socket:长连接,理论上客户端和服务端一旦建立连接,则不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该链接已释放网络资源。所以当一个socket连接中没有数据的传输,那么为了位置连续的连接需要发送心跳消息,具体心跳消息格式是开发者自己定义的。

  • Http:短连接,即客户端向服务器发送一次请求,服务器端响应后连接即会断掉。

三、异步和同步的区别

同步是阻塞模式,异步是非阻塞模式

  • 同步请求:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
  • 异步请求:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式

四、HttpURlconnection发送网络请求:

1. get请求

//子线程中执行请求
URL url = new URL(path);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
    urlConnection.setRequestMethod("GET");
    urlConnection.setConnectTimeout(5000);
    if (urlConnection.getResponseCode() == 200){
        InputStream inputStream = urlConnection.getInputStream();
        
    }

2. post请求

//子线程中执行请求
URL url = new URL(path);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setConnectTimeout(5000);
urlConnection.setReadTimeout(5000);
String content = "name="+URLEncoder.encode(name)+"&pass="+URLEncoder.encode(pass);//数据编解码
urlConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");//设置请求头
urlConnection.setRequestProperty("Content-Length",content.length()+"");
urlConnection.setDoOutput(true);//以后就可以使用conn.getOutputStream().write()  
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(content.getBytes());
if (urlConnection.getResponseCode() == 200){ }

3. json解析:

  • json解析
      bean:
        JSONObject jsonObject = new JSONObject(text);
        String code = jsonObject.getString("resultCode");

    array:
        ArrayList<StuClzInfo> list = new ArrayList<>();
        JSONArray array = new JSONArray(text);
        for (int i = 0; i < array.length(); i++) {
                StuClzInfo info = new StuClzInfo();
                JSONObject object = array.getJSONObject(i);
                info.setCode(object.getString("code"));
                info.setCount(object.getInt("count"));
                list.add(info);
        }

        
    bean中带array:
        JSONObject object = new JSONObject(text);
            String resultCode = object.getString("resultCode");
            if ("1".equals(resultCode)) {
                tv_name.setText(object.getString("school"));
                JSONArray clazz = object.getJSONArray("clazz");
                List<StuClzInfo> list = new ArrayList<>();
                for (int i = 0; i < clazz.length(); i++) {
                    JSONObject o = clazz.getJSONObject(i);
                    StuClzInfo info = new StuClzInfo();
                    info.setCode(o.getString("code"));
                    info.setCount(o.getInt("count"));
                    list.add(info);
                }
            }
  • gson解析(bean,array,bean中带array)
    bean:
        Gson gson = new Gson();
        LoginInfo loginInfo = gson.fromJson(text, LoginInfo.class);//bean
        
    array:
        Gson gson = new Gson();
        Type type = new TypeToken<List<StuClzInfo>>() {}.getType();
        List<StuClzInfo> list = gson.fromJson(text, type);//list<bean>
        
    bean中带array:
        SchoolInfo info = gson.fromJson(text, SchoolInfo.class);
        list = info.getClazz();//bean.list<bean>
  • fastjson解析(bean,array,bean中带array)
bean:
        LoginInfo loginInfo1 = JSON.parseObject(text, LoginInfo.class);

    array:
        List<StuClzInfo> list = JSON.parseArray(text, StuClzInfo.class);
        
    bean中带array:
        SchoolInfo info = JSON.parseObject(text, SchoolInfo.class);
        List<StuClzInfo> list = info.getClazz();

五、OkHttp3讲解

1. OkHttp3简介

1.支持http和https协议,api相同,易用;
2.http使用线程池,https使用多路复用;
3.okhttp支持同步和异步调用;
4.支持普通form和文件上传form;
5.操作请求和响应(日志,请求头,body等);
6.okhttp可以设置缓存;
7.支持透明的gzip压缩响应体

2. OkHttp3 配置

①依赖:implementation 'com.squareup.okhttp3:okhttp:3.12.0'
②添加网络权限:<uses-permission android:name="android.permission.INTERNET"/>

3. OkHttp3 使用思路

get请求思路

1.获取okHttpClient对象
2.构建Request对象
3.构建Call对象
4.通过Call.enqueue(callback)方法来提交异步请求;execute()方法实现同步请求

post请求思路

1.获取okHttpClient对象
2.创建RequestBody
3.构建Request对象
4.构建Call对象
5.通过Call.enqueue(callback)方法来提交异步请求;execute()方法实现同步请求

4. OkHttp3 发送异步请求(GET)

//第一步获取okHttpClient对象
    OkHttpClient client = new OkHttpClient.Builder()
            .build();
    //第二步构建Request对象
    Request request = new Request.Builder()
            .url(url)
            .get()
            .build();
    //第三步构建Call对象
    Call call = client.newCall(request);
    //第四步:异步get请求
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.i("onFailure", e.getMessage());
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            //得到的子线程
            String result = response.body().string();
            Log.i("result", result);
        }
    });

4. OkHttp3 发送同步请求(GET)

//第一步获取okHttpClient对象
    OkHttpClient client = new OkHttpClient.Builder()
            .build();
    //第二步构建Request对象
    Request request = new Request.Builder()
            .url(url)
            .get()
            .build();
    //第三步构建Call对象
    final Call call = client.newCall(request);
    //第四步:同步get请求
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Response response = call.execute();//必须子线程执行
                String result = response.body().string();
                Log.i("response", result);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();

5. OkHttp3 发送异步请求(POST)

    //第一步创建OKHttpClient
    OkHttpClient client = new OkHttpClient.Builder()
            .build();
    //第二步创建RequestBody(Form表达)
    RequestBody body = new FormBody.Builder()
            .add("username", "admin")
            .add("password", "123456")
            .build();
    //第三步创建Rquest
    Request request = new Request.Builder()
            .url(url)
            .post(body)
            .build();
    //第四步创建call回调对象
    final Call call = client.newCall(request);
    //第五步发起请求
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.i("onFailure", e.getMessage());
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            String result = response.body().string();
            Log.i("result", result);
        }
    });

5. OkHttp3 发送同步请求(POST)

    //第一步创建OKHttpClient
    OkHttpClient client = new OkHttpClient.Builder()
            .build();
    //第二步创建RequestBody
    RequestBody body = new FormBody.Builder()
            .add("username", "admin")
            .add("password", "123456")
            .build();
    //第三步创建Rquest
    Request request = new Request.Builder()
            .url(url)
            .post(body)
            .build();
    //第四步创建call回调对象
    final Call call = client.newCall(request);
    //第五步发起请求
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Response response = call.execute();
                String result = response.body().string();
                Log.i("response", result);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start(); 

6. 请求头处理(Header)以及超时和缓冲处理以及响应处理

    //超时设置
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(5,TimeUnit.SECONDS)//设置连接时间
            .readTimeout(5,TimeUnit.SECONDS)//设置读取时间
            .writeTimeout(5,TimeUnit.SECONDS)//设置写出时间
    .cache(new Cache(new File(getCacheDir(), "Cache"),10*1024*1024))
            .build();

    //表单提交
    RequestBody requestBody = new FormBody.Builder()
            .add("pno", "1")
            .add("ps","50")
            .add("dtype","son")
            .add("key","4a7cf244fd7efbd17ecbf0cb8e4d1c85")
            .build();

    //请求头设置
    Request request = new Request.Builder()
            .url(url)
    .addHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
    .header("User-Agent", "OkHttp Example")
            .post(body)
            .build();
  
    //响应处理
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        //响应行
        Log.d("ok", response.protocol() + " " +response.code() + " " + response.message());
        //响应头
        Headers headers = response.headers();
        for (int i = 0; i < headers.size(); i++) {
            Log.d("ok", headers.name(i) + ":" + headers.value(i));
        }
        //响应体
        final String string = response.body().string();

        Log.d("ok", "onResponse: " + string);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tx.setText(string);
            }
        });
    }

7. 请求体处理(Form表单,String字符串,流,文件)

①POST方式提交String/JSON application/json;json串

//POST方式提交String
MediaType mediaType1 = MediaType.parse("application/x-www-form-urlencoded;charset=utf-8");
String requestBody = "pno=1&ps=50&dtype=son&key=4a7cf244fd7efbd17ecbf0cb8e4d1c85";
        RequestBody requestBody1 = RequestBody.create(mediaType1, requestBody);

//POST方式提交JSON:传递JSON同时设置son类型头(接口找一下)
RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), "{}");
request.addHeader("Content-Type", "application/json")//必须加json类型头
            
//POST方式提交无参
RequestBody requestBody1 = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=utf-8"), "");

② POST方式提交流

RequestBody requestBody2 = new RequestBody() {
            @Nullable
            @Override
            public MediaType contentType() {
                return MediaType.parse("application/x-www-form-urlencoded;charset=utf-8");
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                sink.writeUtf8("pno=1&ps=50&dtype=son&key=4a7cf244fd7efbd17ecbf0cb8e4d1c85");
            }
        };

③POST方式提交表单

RequestBody requestBody4 = new FormBody.Builder()
                .add("pno", "1")
                .add("ps","50")
                .add("dtype","son")
                .add("key","4a7cf244fd7efbd17ecbf0cb8e4d1c85")
                .build();

④POST提交文件

MediaType mediaType3 = MediaType.parse("text/x-markdown; charset=utf-8");
        File file = new File("test.txt");
        RequestBody requestBody3 = RequestBody.create(mediaType3, file);

        //5.POST方式提交分块请求
        MultipartBody body = new MultipartBody.Builder("AaB03x")
                .setType(MultipartBody.FORM)
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"title\""),
                        RequestBody.create(null, "Square Logo"))
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"image\""),
                        RequestBody.create(MediaType.parse("image/png"), new File("website/static/logo-square.png")))
                .build();

8. 拦截器

拦截器.png

 拦截器是OkHttp 执行网络请求中的重要角色,贯穿了整个请求执行的过程,是一个强有力的机制,能够监控,重写以及重试(请求的)调用,用户可传入的 interceptor 分为两类:Application Intercetor和NetworkInterceptor

  1. Application interceptors 应用拦截器
     okClient.addInterceptor(new LoggingInterceptor())
    Application Interceptor 是第一个 Interceptor 因此它会被第一个执行,因此这里的 request 还是最原始的。而对于 response 而言呢,因为整个过程是递归的调用过程,因此它会在 CallServerInterceptor 执行完毕之后才会将 Response 进行返回,因此在 Application Interceptor 这里得到的 response 就是最终的响应,虽然中间有重定向,但是这里只关心最终的 response
     ①不需要去关心中发生的重定向和重试操作。因为它处于第一个拦截器,会获取到最终的响应
     ②只会被调用一次,即使这个响应是从缓存中获取的。
     ③只关注最原始的请求,不去关系请求的资源是否发生了改变,我只关注最后的 response 结果而已。
     ④因为是第一个被执行的拦截器,因此它有权决定了是否要调用其他拦截,也就是 Chain.proceed() 方法是否要被执行。
     ⑤因为是第一个被执行的拦截器,因此它有可以多次调用 Chain.proceed() 方法,其实也就是相当与重新请求的作用了。

  2. Network Interceptors 网络拦截器
     OKClient.addNetworkInterceptor(new LoggingInterceptor())
    NetwrokInterceptor 处于第 6 个拦截器中,它会经过 RetryAndFollowIntercptor 进行重定向并且也会通过 BridgeInterceptor 进行 request 请求头和 响应 resposne 的处理,因此这里可以得到的是更多的信息。在打印结果可以看到它内部是发生了一次重定向操作,所以NetworkInterceptor 可以比 Application Interceptor 得到更多的信息了
     ①因为 NetworkInterceptor 是排在第 6 个拦截器中,因此可以操作经过 RetryAndFollowup 进行失败重试或者重定向之后得到的resposne。
     ②为响应直接从 CacheInterceptor 返回了。
     ③观察数据在网络中的传输。
     ④可以获得装载请求的连接。

二、拦截器的应用

1.日志拦截器

在 LoggingInterceptor 中主要做了 3 件事:
①请求前-打印请求信息;
②网络请求-递归去调用其他拦截器发生网络请求;
③网络响应后-打印响应信息

代码:

    OkHttpClient client = builder
                .addInterceptor(new LoggingInterceptor())//应用拦截器
                .addNetworkInterceptor(new LoggingInterceptor())//网络拦截器
                .readTimeout(5, TimeUnit.SECONDS)
                .connectTimeout(5, TimeUnit.SECONDS)
                .writeTimeout(5, TimeUnit.SECONDS)
                .build();

    class LoggingInterceptor implements Interceptor {

        private static final String TAG = "LoggingInterceptor";

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

            //1.请求前--打印请求信息
            long startTime = System.nanoTime();
            Log.d(TAG, String.format("Sending request %s on %s%n%s",
                    request.url(), chain.connection(), request.headers()));

            //2.网络请求
            Response response =  chain.proceed(request);

            //3.网络响应后--打印响应信息
            long endTime = System.nanoTime();
            Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",
                    response.request().url(), (endTime - startTime) / 1e6d, response.headers()));

            return response;
        }
    }
2.缓存拦截器(通过adb命令查看data数据)

在 MyCacheinterceptor 中主要做了(post方式无法缓存)
①设置缓存位置
②无网时:设置缓存协议
③有网:加载网络数据;无网:加载缓存数据

代码:

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(myCacheinterceptor)//应用拦截器
                .addNetworkInterceptor(myCacheinterceptor)//网络拦截器
                .connectTimeout(5, TimeUnit.SECONDS)
                .cache(new Cache(new File(getCacheDir(), "Cache"), 1024 * 1024 * 10))
                .build();
       
    class MyCacheinterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
             //1.获取请求数据
            Request request = chain.request();
             //2.判断如果无网时,设置缓存协议
            if (!isNetworkAvailable(MainActivity.this)) {
                request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();

            }
            //3.开始请求网络,获取响应数据
            Response originalResponse = chain.proceed(request);
            //4.判断是否有网
            if (isNetworkAvailable(MainActivity.this)) {、
                //有网,获取网络数据
                int maxAge = 0;
                return originalResponse.newBuilder()
                        .removeHeader("Pragma")
                        .header("Cache-Control", "public ,max-age=" + maxAge)
                        .build();
            } else {
                //没有网络,获取缓存数据
                int maxStale = 15*60;
                return originalResponse.newBuilder()
                        .removeHeader("Pragma")
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                        .build();
            }

        }
    }

    /**
        检测是否有网
     */
    public static boolean isNetworkAvailable(Context context) {
        if (context != null) {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo info = cm.getActiveNetworkInfo();
            if (info != null) {
                return info.isAvailable();
            }
        }
        return false;
    }

9. 注意事项

①推荐让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。
②response.body().string()只调用一次
③每一个Call(其实现是RealCall)只能执行一次,否则会报异常
⑤子线程加载数据后,主线程刷新数据

10. HttpURLconnection及OkHttp3的对比分析

1.HttpUrlConnection,google官方提供的用来访问网络,但是实现的比较简单,只支持1.0/1.1
2.并没有多路复用,如果碰到app大量网络请求的时候,性能比较差,
3.HttpUrlConnection底层也是用Socket来实现的
4.OkHttp像HttpUrlConnection一样,实现了一个网络连接的过程。
5.OkHttp和HttpUrlConnection是一级的,用socket实现了网络连接,OkHttp更强大,
6.HttpUrlConnection在IO方面用到的是InputStream和OutputStream,但OkHttp用的是sink和source,这两个是在Okio这个开源库里的, feredSink(支持缓冲)、GzipSink(支持Gzip压缩)、ForwardingSink和InflaterSink(后面这两者服务于GzipSink)
7.多个相同host和port的stream可以共同使用一个socket,而RealConnection就是处理连接的,那也就是说一个RealConnection上可能有很多个Stream
8.OkHttp代码比HttpURLConnection精简的多

11. OkHttp源码分析(设计模式,线程池的使用)

ok.png

dispatcher:调度器
proxy:代理
protocols:协议
interceptors:拦截器集合
cache:

okHttpClient.newCall

RealCall.newRealCall

四、单例、多线程安全问题

/**
  饿汉式
 */
public class Person {

    //1.构造函数私有化
    private Person(){}

    //2.创建单个私有对象对象
    private static  Person person = new Person();

    //3.提供对外公开访问方法
    public static Person getPerson() {
        return person;
    }
}

/**
 * 懒汉式
 *
 * 多线程安全问题:
 *      1.多个线程同时操作
 *      2.共用同一个资源
 *      3.分步执行操作同一个资源
 *
 * 多线程安全解决方式:
 *      1.同步方法
 *      2.同步代码块
 *      3.锁机制
 */
public class Student {

    //1.构造函数私有化
    private Student(){}

    //2.创建单个私有对象对象
    private static  Student student;

    //3.提供对外公开访问方法
    public static Student getInstance(){

        //解决线程安全问题
        if (student == null){
            synchronized (Student.class){
                if (student == null) {
                    student = new Student();
                }
            }
        }

        return student;
    }
}

五、OkHttpUtils工具类抽取

public class OkHttpUtils {

    private static OkHttpUtils okHttpUtils;
    private static OkHttpClient okHttpClient;
    private static Handler mHandler;

    /**
     * 构造初始化
     */
    private OkHttpUtils(){
        /**
         * 构建OkHttpClient
         */
        okHttpClient = new OkHttpClient.Builder()
        /**
         * 请求的超时时间
         */
        .readTimeout(5000, TimeUnit.MILLISECONDS)
        /**
         * 设置响应的超时时间
         */
        .writeTimeout(5000, TimeUnit.MILLISECONDS)
        /**
         * 设置连接的超时时间
         */
        .connectTimeout(5000, TimeUnit.MILLISECONDS)
        /**
         * 构建
         */
        .build();


        /**
         * 获取主线程的handler
         */
        mHandler = new Handler(Looper.getMainLooper());
    }

    /**
     * 通过单例模式构造对象
     * @return
     */
    public static OkHttpUtils getInstance(){
        if (OkHttpUtils == null){
            synchronized (OkHttpUtils.class){
                if (okHttpUtils == null){
                    okHttpUtils = new OkHttpUtils();
                }
            }
        }
        return okHttpUtils;
    }

    /**
     * 构造Get请求,封装对用的Request请求,实现方法
     * @param url  访问路径
     * @param realCallback  接口回调
     */
    private void getRequest(String url, final RealCallback realCallback){

        Request request = new Request.Builder().url(url).get().build();
        deliveryResult(realCallback, okHttpClient.newCall(request));
    }

    /**
     * 构造post 请求,封装对用的Request请求,实现方法
     * @param url 请求的url
     * @param requestBody 请求参数
     * @param realCallback 结果回调的方法
     */
    private void postRequest(String url, RequestBody requestBody, final RealCallback realCallback){

        Request request = new Request.Builder().url(url).post(requestBody).build();
        deliveryResult(realCallback, okHttpClient.newCall(request));
    }

    /**
     * 处理请求结果的回调:主线程切换
     * @param realCallback
     * @param call
     */
    private void deliveryResult(final RealCallback realCallback, Call call) {
        call.enqueue(new Callback() {
            @Override
            public void onFailure(final Call call, final IOException e) {
                sendFailureThread(call, e, realCallback);
            }

            @Override
            public void onResponse(final Call call, final Response response) throws IOException {
                sendSuccessThread(call, response, realCallback);
            }
        });
    }

    /**
     * 发送成功的回调
     * @param call
     * @param response
     * @param realCallback
     */
    private void sendSuccessThread(final Call call, final Response response, final RealCallback
            realCallback) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                realCallback.onResponse(call,response);
            }
        });
    }

    /**
     * 发送失败的回调
     * @param call
     * @param e
     * @param realCallback
     */
    private void sendFailureThread(final Call call, final IOException e, final RealCallback realCallback) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                realCallback.onFailure(call,e);
            }
        });
    }

    //-----------对外公共访问的get/post方法-----------
    /**
     * get请求
     * @param url  请求url
     * @param realCallback  请求回调
     */
    public void get(String url, final RealCallback realCallback){
        getRequest(url,realCallback);
    }

    /**
     * post请求
     * @param url       请求url
     * @param realCallback  请求回调
     * @param requestBody    请求参数
     */
    public void post(String url, RequestBody requestBody, final RealCallback realCallback){
        postRequest(url,requestBody,realCallback);
    }

    /**
     * http请求回调类,回调方法在UI线程中执行
     */
    public static abstract class RealCallback {
        /**
         * 请求成功回调
         * @param response
         */
        public abstract void onResponse(Call call,Response response);
        /**
         * 请求失败回调
         * @param e
         */
        public abstract void onFailure(Call call,IOException e);
    }
}

六、RecyclerView+SmartRefresh

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容