OkHttp的学习

  一、OkHttp的全面用法:

    1. Get的请求:  例子 请求百度

      OkHttpClient client = new OkHttpClient();
      
HttpUrl httpUrl = HttpUrl.parse("www/baidu.com").newBuilder()
      .addQueryParameter("city","beijing")
      .build();
      Request request = new Request.Builder()
      .url(httpUrl)
      .build();

      Response response = client.newCall(request).execute();

    
    2.  异步POST请求 FormBody

      OkHttpClient client = new OkHttpClient();
      FormBody body = new FormBody.Builder()
        .add("name","xiaoming")
        .add("age","18")
        .build();
      Request request = new Request.Builder()
        .url("www/baidu.com")
        .post(body)
        .build();
      Response response = client.newCall(request).execute();

  3. 异步上传文件 MultipartBody

  MediaType MEDIA_TYPE = MediaType.parse("text/x-markdown; charset=utf-8");
  String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/nihao.txt";
  File file = new File(path);
  RequestBody requestBody = RequestBody.create(file,MEDIA_TYPE);
  MultipartBody body = new MultipartBody.Builder()
     
 ----->上传字符时候需要加上setType特殊类型,否则无法上传成功
.addFormDataPart("filename", "filename", requestBody)
.build();
  OkHttpClient client = new OkHttpClient();
  Request request = new Request.Builder()
.url("www.baidu.com")
.post(body)
.build();


 4.异步下载文件
  String url = "https://img-my.csdn.net/uploads/201603/26/1458988468_5804.jpg";
  final Request request = new Request.Builder().url(url).build();
  Call call = mOkHttpClient.newCall(request);
  call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: 失败");
}

@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = response.body().byteStream();
String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/shu.jpg";
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file);
try{
byte[] buffer = new byte[1024];
int len = 0;
while((len = is.read(buffer)) != -1) {
fos.write(buffer,0,len);
}
fos.flush(); //文件的读写,这个需要多看看和学习
}catch (Exception e){
e.printStackTrace();
}
Log.d(TAG, "onResponse: --->下载完成");
}
});


  二、同步和异步的区别

   里面有个Dispatcher这个类来实现,它主要作用是维护请求的状态(同步和异步),并且维护一个线程池,用于执行请求

   同步请求

   同步请求发送请求之后,就会进入阻塞状态,Dispatcher(分发器)主要做两个事情:保存同步请求、移除同步请求,直到收到响应。   

   异步请求

   异步请求放到线程池里面,Dispatcher类主要做三件事情:正在执行异步队列、等待执行异步队列、维护一个还有一个线程池

   里面之所以有两个异步请求队列:当前运行的异步队列小于64时候,它就会被放入到正在运行的异步队列(runningAsyncCalls)中,然后去运行线程池,否则就是加入就绪缓冲队列(readyAsyncCalls)当中作为等待

  

  三、缓存的原理以及实现

   1.缓存原理:

    

   2.缓存相关的字段

    Expirse : 缓存失效的时间

    Cactce-Contral :  控制缓存  缓存类型

      private :      客户端可以去缓存

      public   :      客户端和代理服务器都可以缓存

      max-age  :   表示多少秒后失效(在服务端用的较多)

·      no-cache  : 通过服务验证码(304)是否能使用缓存

      no-store   :  代理和本地都不可以使用缓存,只能从服务器去拿数据

    Last-Modified :  最后一次修改时间(比如只是添加一个空格,时间就不一样,所以就出现ETag这个关键字段)

    ETag :   通过返回的Response返回数据比较里面两个内容是否一直

    If-Modified-Since :  与Last-Modified成对存在的

    If-None-Match :   与ETag成对存在的

   3.缓存的机制

    int maxSize = 10 * 1024 * 1024;
    Cache cache = new Cache(new File("sd"), maxSize);
    OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
    Request request = new Request.Builder()
      .url("http://www.qq.com")
      .build();
    Response response = client.newCall(request).execute();
    String string = response.body().toString();     --------->如果你不去读取返回的Response的请求体,

                           下次在请求也还是网络请求,并不是缓存请求
    if(response.isSuccessful()){
    Log.i(TAG, "run: = " + response.networkResponse().toString());
    Log.i(TAG, "run: = " + response.cacheResponse().toString());
    Log.i(TAG, "run-------------------------------");
    Response response1 = client.newCall(request).execute();
    String string = response1.body().toString();   ------>这次再次请求就是缓存请求
    if(response1.isSuccessful()){
    Log.i(TAG, "run: = " + response1.networkResponse().toString());
    Log.i(TAG, "run: = " + response1.cacheResponse().toString());

  4. 缓存实现

    

    CacheInterceptor : 缓存拦截器

    CasheStrategy : 缓存策略(是用本地缓存还是网络缓存),networkResponse为空的时候执行cacheResponse,否则需要取网络请求

    Cache : 缓存目录,这里面就有put  get remove等方法进行对缓存的操控

  四、多线程下载功能

    1.关键的字段  

     Transer-Encoding : chunked  :  无法拿到整个文件的长度

     content-length :   访问文件的总长度的大小

     Range :   可以访问服务器某一个字段的长度大小

    2. 下载文件遇到的问题

      文件存储的路径 :  检查是否有SD卡的路径

      文件是否受损 : 通过拿到服务器的文件的MD5,然后去和你本地的MD5做对比观察是否相等,如果不相等说明文件已经受损

     文件空间的大小 :  下载文件是否比剩余空间小,做一个判断

     进度条的更新 :   

     数据的保存 :  

   五、Https使用

    1.https加密的常见知识

      对称加密: 秘钥是相同

      非对称加密: 一个是公钥,一个是私钥,公钥只能用私钥去解,私钥只能用公钥去解

      数字证书: 

    2.SSL/TLS的运行机制 :

      a.客户端会告诉服务端证书(公钥)、数据加密算法、以及生成一个随机数

      b.服务端接受之后对证书进行私钥解密,验证是否可信,如果成功,服务端会确认是使用加密算法、以及生成一个随机数给客户端

      c.客户端再对服务端返回数据进行验证,验证通过之后就发送对称加密对数据进行加密处理,发送给服务端

      d.服务端就会对称加密进行解析拿到数据

      证书采用的是非对称加密,而数据传输采用的是对称加密

    3.DNS讲解 :  域名解析

    比如我去直接访问IP地址,它会抛出校验抛出的异常,connect请求连接时候添加HostnameVerifier这个接口里面的verify,直接返回为true,就可以跳过校验

    class MyHostVerify  implements HostnameVerifier() {

    @Override

    public boolean verify(){

      手动可以去写校验方法

      return true;

    }

  }

  六、自定义拦截器

   1.重写Interceptor这个类

    public interface Interceptor {
      Response intercept(Chain chain) throws IOException;
      interface Chain {
        Request request();
        Response proceed(Request request) throws IOException;
        Connection connection();
      }
     }

    /**
     * 重试拦截器
   */
  
 
public class RetryIntercepter implements Interceptor {
    public int maxRetry;//最大重试次数
    private int retryNum = 0;//假如设置为3次重试的话,则最大可能请求4次(默认1次+3次重试)
    public RetryIntercepter(int maxRetry) {
      this.maxRetry = maxRetry;
    }
    @Override
    public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    System.out.println("retryNum=" + retryNum);
     Response response = chain.proceed(request);
    while (!response.isSuccessful() && retryNum < maxRetry) {
      retryNum++;
       System.out.println("retryNum=" + retryNum);
       response = chain.proceed(request);
    }
    return response;
    }
   }

  2.添加自定义拦截器

   mClient=new OkHttpClient.Builder()
      .addInterceptor(new RetryIntercepter(3))//重试
      .addInterceptor(logging)//网络日志
      .addInterceptor(new TestInterceptor())//模拟网络请求
      .build();
  

  3.addInterceptor(添加应用拦截器和addNetworkInterceptor()的区别

   

      addInterceptor :  总是只调用一次,即使HTTP响应是从缓存中获取,不是从网络获取的

      addNetworkInterceptor能够操作中间过程的响应,可以观察网络上传的数据

     

  六、拦截器总结:

    1.应用拦截器和网络拦截器需要学习

    2.拦截器核心原理 : 

     a. 在发送请求前对Request进行处理

     b.调用下一个拦截器获取response (通过调用realChain.proceed这个方法可以执行下一个拦截器)

     c.对response进行处理,返回给上一个拦截器

  七、OkHttp的面试点

     1.Socket是什么?

      a.是对TCP/IP协议进行的封的编程接口,类属于传输层

      b.成对出现一对套接字 : 包括ip地址和端口号

     2. TCP和UDP的区别:

       TCP :  基于字节流的方式作为传输

       UDP : 基于数据报文格式,打包之后作为传输

     3.http和Socket的区别

      Http : 采用 请求--响应 方式 。 HTPP协议属于应用层,客户端有需要才请求

      Socket :  基于TCP/IP协议进行传输,采用 服务器主动发送数据  的方式,Socket属于传输层    

     5. 断点续传的原理?(还需要多看看,补充一下)

     断点续传 : 从文件已经下载的地方开始接着下载

     Okhttp中如何实现    

      Request request = new Request.Builder()
          .addHeader("RANGE", "bytes=" + downloadLength + "-")
          .url("111111")
          .build();
      Response response = client.newCall(request).execute();
      if(response != null ) {
       InputStream is = response.body().byteStream();
       saveFile = new RandomAccessFile(file, "rw");
       saveFile.seek(downloadLength); //跳过已经下载的字节
        byte[] b = new byte[24];
        int total = 0;
        int len;
        while((len = is.read(b)) != -1) {
         total += len;
        saveFile.write(b, 0, len);
        }
      }

    6.多线程下载原理 okhttp是如何实现的(还需要多看看怎么使用)

    7.json数据如何解析?okhttp如何解析json类型数据

    9.okhttp如何处理https

     a.https : 是一种基于SSL/TLS的htpp的协议

     b.https : 所有传输内容都是经过加密的(对称+不对称)

      对称加密 : 加密和解密用的是同一个秘钥(对于真正传输数据过程)

      非对称加密  : 加密和解密是偶用的秘钥不是同一把秘钥(只是处于握手协议阶段)

      对称加密的过程:首先客户端生成随机数作为对称秘钥,然后去服务端请求一个公钥,有服务端返回给客户端,客户端接收到公钥后,使用公钥对自己生成的对称秘钥进行加密,

      然后把对称加密秘钥加密后的秘钥在发给服务端,接受到秘钥它会自己解密,这个时候就可以数据通信了。

一、参数的讲解

  1. OkHttpClient : OkHttp的客户端,初始化OkHttp的一些重要配置

  2. Request : 请求数据封装

  3. Call : 可执行异步或者同步请求

  4. Interceptor : OkHttp 中的重要成分,拦截器,基于责任链设计模式

  5.RetryAndFollowUpInterceptor : 负责错误重试以及重定向

  6.BridgeInterceptor : 负责组装请求以及解析数据

  7.CacheInterceptor: 负责读取缓存和更新缓存

  8.ConnectInterceptor: 负责和服务器连接      Okio对接

  9.CallServerInterceptor: 负责发送请求以及接受数据  核心功能

  10.拦截器的说明:在RealCall内部维护一个interceptors的集合,通过getResponseWithInterceptorChain去获得originalRequest(原始请求)进行处理,

          内部会通过 chain.procee(originalRequest) 最终得到一个 Response 对象,而 Intercaptor.Chain 内部会封装当前需要处理的 interceptor 对象。

          其内部会调用 interceptor.intercept() 方法去处理这个 request 请求最终得到 response

  

  ·               OkHttp同异步大致流程

    

           OkHttp大体流程

  二、CacheInterceptor  : 负责读取缓存和更新缓存

    1. 案例的使用 :      getResponseWithInterceptorChain里面调用interceptors.add(new CacheInterceptor(client.internalCache()));

     Cache cache = new Cache(new File("/sdcard/test/"),100*100*1024);

     OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache).build();

     Request request = new Request.Builder().url("http://www.imooc.com/courseimg/s/cover005_s.jpg").

                  cacheControl(new CacheControl.Builder().noCache().build()).build();

     Call call = okHttpClient.newCall(request);

     call.enqueue(new Callback() {

     @Override

     public void onFailure(Call call, IOException e) {

      Log.e("info","----onFailure");

    }

     @Override public void onResponse(Call call, Response response) throws IOException {

      Log.e("info","----onResponse"+response.body().toString()); } });

    

    2. okhttp&http缓存策略

     1) .强制缓存 : Exprises 、Cache-Control  

           Exprises :Exprises的值为服务端返回的到期时间      http1.0用的

           Cache-Control  :  是服务端返回的Response中添加的头信息

      Cache-Control 的缓存类型 :

      private :      客户端可以去缓存

      public   :      客户端和代理服务器都可以缓存

      max-age  :   表示多少秒后失效(在服务端用的较多)

·        no-cache  : 通过服务验证码(304)是否能使用本地缓存

      no-store   :  代理和本地都不可以使用缓存,只能从服务器去拿数据

     2) . 对比缓存

       a. 首先需要进行比较判断是否可以使用缓存

       b.服务器会将缓存标识与数据一起返回给客户端

     

    Etag : 服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识

    If-None-Match :  再次请求服务器时,通过此字段通知服务器客户端缓存数据的唯一标识

    Last-Modified :  服务器在响应请求时,告诉浏览器资源的最后修改时间

    If-modified-Since : 再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间

    标识发送给服务端,服务端根据缓存标识进行判断,假如返回304,则表示缓存可用,假如返回200,标识缓存不可用,使用最新返回的数据。

  三、BridgeInterceptor :负责组装请求以及解析数据  interceptors.add(new BridgeInterceptor(client.cookieJar()));

    列举的是添加的头信息

    Content-Type 定义网络文件的类型和网页的编码,如果未指定 ContentType,默认为[TEXT]/[HTML],具体的类型,参考这个文档 HTTP Content-type

    Content-Length 表示的是请求体内容的长度。它和 Transfer-Encoding 是互斥的,主要根据 Content-Length 是否为 -1 进行判读使用哪一个请求头。

    Transfer-Encoding 值为 chunked 表示请求体的内容大小是未知的。

    Host 请求的 url 的主机

    Connection 默认就是 "Keep-Alive",就是一个 TCP 连接之后不会关闭,保持连接状态

    Accept-Encoding 默认是 "gzip" 告诉服务器客户端支持 gzip 编码的响应。

    Cookie 当请求设置了 Cookie 那么就是添加 Cookie 这个请求头。

    User-Agent "okhttp/3.4.1" 这个值根据 OKHTTP 的版本不一样而不一样,它表示客户端 的信息。

  

  四、retryAndFollowUpInterceptor : 负责错误重试以及重定向  interceptors.add(retryAndFollowUpInterceptor)

    1.网络请求异常的“重连机制” : 多次请求,不超过20次,超过20次就不做重复连接

    2.异常的判断

      a. IOException 异常的重连机制

      b.RouteException(路由) 异常的重连机制

      c.recover(恢复) 方法异常检测

      d.isRecoverable 方法异常检测

  

  五、CallServerinterceptor :  负责发送请求以及接受数据  interceptors.add(new CallServerInterceptor(retryAndFollowUpInterceptor.isForWebSocket()));

    1. 写入请求头信息和请求体信息

    2.获得请求头和请求体的信息  

 

  ConnectInterceptor :负责和服务器连接   interceptors.add(new ConnectInterceptor(client));

    interceptors.addAll(client.interceptors());

  七、自定义拦截器

    class LoggingInterceptor implements Interceptor {
      @Override public Response intercept(Interceptor.Chain chain) throws IOException {
          Request request = chain.request();
          //1.请求前--打印请求信息
          long t1 = System.nanoTime();
          logger.info(String.format("Sending request %s on %s%n%s",
             request.url(), chain.connection(), request.headers()));

          //2.网络请求          Response response = chain.proceed(request);          //3.网络响应后--打印响应信息          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. Application Intercetor和NetworkInterceptor的区别

     

    2.添加Application Interceptor的使用

      代码案例:

      OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new LoggingInterceptor()) .build(); 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();OkHttpClient client = new OkHttpClient.Builder()
          .addInterceptor(new LoggingInterceptor())          .build();      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();   

    3. NetworkInterceptor 的使用

     代码案例

     OkHttpClient client = new OkHttpClient.Builder()
        .addNetworkInterceptor(new LoggingInterceptor())        .build();     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();      

  4. Application Interceptor 适用于在请求前统一添加一些公共参数,例如在添加 APP 的版本号,用户 ID ,手机版本号,运营商类型等参数。或者对响应体的数据进行 json 转化等操作。

  5.    NetwrokInterceptor 在这一层拦截器中可以获取到最终发送请求的 request ,也可以获取到真正发生网络请求回来的 response 响应,从而修改对应的请求或者响应数据。

  6. 两种拦截器的区别

    Application interceptors
       Don't need to worry about intermediate responses like redirects and retries.
       不需要去关心中发生的重定向和重试操作。因为它处于第一个拦截器,会获取到最终的响应 response 。
       Are always invoked once, even if the HTTP response is served from the cache.
        只会被调用一次,即使这个响应是从缓存中获取的。
        Observe the application's original intent. Unconcerned with -OkHttp-injected headers like If-None-Match.
        只关注最原始的请求,不去关系请求的资源是否发生了改变,我只关注最后的 response 结果而已。
        Permitted to short-circuit and not call Chain.proceed().
        因为是第一个被执行的拦截器,因此它有权决定了是否要调用其他拦截,也就是 Chain.proceed() 方法是否要被执行。
        Permitted to retry and make multiple calls to Chain.proceed()
        因为是第一个被执行的拦截器,因此它有可以多次调用 Chain.proceed() 方法,其实也就是相当与重新请求的作用了。

    Network Interceptors
        Able to operate on intermediate responses like redirects and retries.
        因为 NetworkInterceptor 是排在第 6 个拦截器中,因此可以操作经过 RetryAndFollowup 进行失败重试或者重定向之后得到的resposne。

        Not invoked for cached responses that short-circuit the network.
        对于从缓存获取的 response 则不会去触发 NetworkInterceptor 。因为响应直接从 CacheInterceptor 返回了。

        Observe the data just as it will be transmitted over the network.
        观察数据在网络中的传输。

        Access to the Connection that carries the request.
        可以获得装载请求的连接。

三次握手的实例 : https://www.cnblogs.com/tiwlin/archive/2011/12/25/2301305.html

OkHttp 网址值得去学习 : https://www.jianshu.com/p/ede34ab5b776

 

      

原文地址:https://www.cnblogs.com/liunx1109/p/11437218.html