OkHttp3 源码解读

转载请注明本文出自maplejaw的博客(http://blog.csdn.net/maplejaw_

开源库地址:https://github.com/square/okhttp
解读版本:3.4.1

OkHttp是目前非常流行的网络请求库,出自Square公司。对于该库的使用,相信大家已经比较熟悉了。今天,我将从源码角度对OkHttp3进行剖析。

基本使用

Okhttp的使用可以分为四步:

  1. 初始化客户端(实际应用中应当保持单例)

     //获取一个客户端
     OkHttpClient client = new OkHttpClient.Builder()
                      ...
                     .build();
  2. 构建Request

    //构建一个Request
    Request request = new Request.Builder()
      .url(url)
      .build();
  3. 获取Call对象

    //获取Call对象
    Call call=client.newCall(request);
  4. 发送请求(execute同步/enqueue异步)

    //同步调用
    Response response = call.execute();
    
    //异步调用
    call.enqueue(new Callback() {
       @Override
     public void onFailure(Call call, IOException e) {
    
     }
    
       @Override
     public void onResponse(Call call, Response response) throws IOException {
    
     }
    });

当然,在使用类似POST等可以设置请求体的请求方法时,我们还可以如下构建请求体。

构建字符串,字节,文件请求体:

public static final MediaType TEXT = MediaType.parse("text/plain; charset=utf-8");
public static final MediaType STREAM = MediaType.parse("application/octet-stream");


 //构建字符串请求体
 RequestBody body1 = RequestBody.create(TEXT, string);

 //构建字节请求体
 RequestBody body2 = RequestBody.create(STREAM, byte);

 //构建文件请求体
 RequestBody body3 = RequestBody.create(STREAM, file);


 //将请求体设置给请求方法内
 Request request = new Request.Builder()
      .url(url)
      .post(xx)// xx表示body1,body2,body3中的某一个
      .build();

构建表单请求体:

 //构建表单RequestBody
 RequestBody formBody=new FormBody.Builder()
                 .add("name","maplejaw")
                 .add("age","18")
                  ...     
                 .build();

构建分块表单请求体:

  public static final MediaType STREAM = MediaType.parse("application/octet-stream");
 //构建表单RequestBody
 RequestBody multipartBody=new MultipartBody.Builder()
                .setType(MultipartBody.FORM)//指明为 multipart/form-data 类型
                .addFormDataPart("name","maplejaw") //添加表单数据
                .addFormDataPart("age","20") //添加表单数据
                .addFormDataPart("avatar","111.jpg",RequestBody.create(STREAM,file)) //添加文件,其中avatar为表单名,111.jpg为文件名。
                .addPart(..)//该方法用于添加自定义Part,一般来说以上已经够用
                .build();

关于Okhttp的基本使用已经介绍完毕,在大多数情况下,只要掌握以上使用方法,就足以应付关于网络请求的日常使用。
接下来,将从源码角度剖析OkHttp这个网络框架,如果你到目前为止还弄不清请求行、状态行、请求头、响应头、请求体和响应体这些基本概念的话,建议先阅读你应该知道的HTTP基础知识这篇文章。

源码解读

初始化OkHttpClient

所谓初始化OkHttpClient,无非就是对其进行相关配置,在了解OkHttpClient相关配置前,先认识一下以下一些基本的类。
Proxy
代理类,默认有三种代理模式DIRECT(直连),HTTP(http代理),SOCKS(socks代理),这三种模式,折腾过科学上网的或多或少都了解一点吧。
ProxySelector
代理选择类,默认不使用代理,即使用直连方式,当然,我们可以自定义配置,以指定URI使用某种代理,类似代理软件的PAC功能。
Protocol
协议类,用来表示使用的协议版本,比如http/1.0,http/1.1,spdy/3.1,h2
Dns
DNS这里就不用介绍了,用于根据主机名来查询对应的IP。
Cache
缓存类,内部使用了DiskLruCache来进行管理缓存,匹配缓存的机制不仅仅是根据url,而且会根据请求方法和请求头来验证是否可以响应缓存。此外,仅支持GET请求的缓存。
ConnectionSpec
连接规范,用于配置Socket连接层。对于HTTPS,还能配置安全传输层协议(TLS)版本和密码套件(CipherSuite)
Interceptor
拦截器,该类的功能还是比较强大的,通过拦截器可以监视、重写和重试请求。拦截器的源码如下:

public interface Interceptor {
   
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

拦截器的使用也非常简单,如果你只是想修改Request,那么就通过chain.request()获取原始的Request然后进行修改,比如添加cookie,代理等请求头,甚至还能修改请求方法和请求体。同理如果需要修改Response,则可以通过chain.proceed来获取Response后进行修改。此外我们还可以在其中进行打印日志等其他监视行为。
关于拦截器的使用例子如下:

   //通过addInterceptor添加拦截器
   OkHttpClient client = new OkHttpClient.Builder()
                ...
                .addInterceptor(new MyInterceptor())
                .build();


  //自定义拦截器              
 class MyInterceptor implements Interceptor {

        @Override 
       public Response intercept(Interceptor.Chain chain) throws IOException {
            //获取原始Request
            Request request = chain.request(); 

            //构建新的Request
            Request newRequest=request.newBuilder()//使用newBuilder,在原来request基础上修改,当然如果暴力点,可以完全重写Request。
                    .header("User-Agent", "OkHttp Example")
                    ...
                    .build();

            //获取Response
            Response response = chain.proceed(newRequest);

            //构建新的Response
            Response newResponse=response.newBuilder()
                     .header("Cache-Control", "max-age=60")
                      ....
                     .build();

            return newResponse;
        }
    }                

CookieJar
用来管理cookie,可以根据url保存cookie,也可以通过url取出相应cookie。默认的不做cookie管理。该接口中有两个抽象方法,用户可以自己实现该接口以对cookie进行管理。

  //保存cookie
  void saveFromResponse(HttpUrl url, List<Cookie> cookies);

  //根据Url导入保存的Cookie
  List<Cookie> loadForRequest(HttpUrl url);

SocketFactory
Socket工厂,通过createSocket来创建Socket。
SSLSocketFactory
安全套接层工厂,HTTPS相关,用于创建SSLSocket。一般配置HTTPS证书信任问题都需要从这里着手。对于不受信任的证书一般会提示javax.net.ssl.SSLHandshakeException异常。配置信任所有证书的源码如下:


      OkHttpClient client = new OkHttpClient.Builder()
                .sslSocketFactory(getTrustAllSSLSocketFactory())//配置SSL工厂
                .build();

      //获取信任所有证书的SSLSocketFactory
      public static SSLSocketFactory getTrustAllSSLSocketFactory() {
        // 信任所有证书
        TrustManager[] trustAllCerts = new TrustManager[]{
  new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[]{};
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        }};

        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, null);
            return sslContext.getSocketFactory();
        } catch (Throwable ex) {

        }

        return null;
    }

对于信任自证书的配置问题,可以参考Android Https相关完全解析 当OkHttp遇到Https。这篇文章。
CertificateChainCleaner
证书链清洁器,HTTPS相关,用于从Java的TLS API构建的原始数组中统计有效的证书链,然后清除跟TLS握手不相关的证书,提取可信任的证书以便可以受益于证书锁机制。
HostnameVerifier
主机名验证器,与HTTPS中的SSL相关,当握手时如果URL的主机名不是可识别的主机,就会要求进行主机名验证。

public interface HostnameVerifier {
   

     //通过session验证指定的主机名是否被允许
    boolean verify(String hostname, SSLSession session);
}

CertificatePinner
证书锁

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值