OkhttpClient学习

OkHttpClient 学习和使用

先通过一个非常简单的例子来看怎样使用OKHttpClient发送一个请求,然后根据这个简单例子来详细的分析每个步骤。

        OkHttpClient client = new OkHttpClient().newBuilder().build();
        Request request = new Request.Builder().url("http://www.baidu.com").build();
        Response response = client.newCall(request).execute();
        ResponseBody responseBody = response.body();

这是一个通过OkHttpClient发送一个简单的get请求百度首页的过程。

  1. 创建一个OkHttpClient实例
  2. 创建一个Request实例
  3. 使用 OkHttpClient 创建一个Call并执行 execute()方法,得到一个Response对象。
  4. Response 进行相关操作。

以上就是整个简单HTTP请求的发送和接收过程。

OkHttpClient实现了CloneableCall.Factory接口,Cloneable接口表示okhttpclient支持Object的clone方法。Call.Factory接口是通过Request创建一个Call。同时也要实现Call.FactorynewCall方法。
OkHttpClient通过内部的Builder类来创建。生成器模式,new OkHttpClient(){ this(new Builder())} 或者new OkHttpClient.Builder().build().创建OkHttpClient的方式比较多,这样都是使用默认的OkHttpClient的配置。
相关可以配置的和Client相关的还有

  private static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
      Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
    //默认支持的协议
  private static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
      ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT);
  //使用https的版本和密码套件
  final Dispatcher dispatcher;//调度,异步请求
  final Proxy proxy;//代理 java.net
  final List<Protocol> protocols;
  final List<ConnectionSpec> connectionSpecs;
  final List<Interceptor> interceptors;//拦截器
  final List<Interceptor> networkInterceptors;//网络拦截器
  final ProxySelector proxySelector;//代理选择器
  final CookieJar cookieJar;//http cookies 管理方案和 持久化
  final Cache cache;//缓存
  final InternalCache internalCache;//内部缓存
  final SocketFactory socketFactory;//socket 工厂 java.net
  final SSLSocketFactory sslSocketFactory; //SSLSocket 工厂 java.net.ssl 
  final CertificateChainCleaner certificateChainCleaner;//证书方案
  final HostnameVerifier hostnameVerifier;//hostName 校验 OkHostnameVerifier
  final CertificatePinner certificatePinner;//固定的证书
  final Authenticator proxyAuthenticator;//代理的身份认证
  final Authenticator authenticator;// web的身份认证
  final ConnectionPool connectionPool;//连接池
  final Dns dns;//DNS
  final boolean followSslRedirects;//是否 执行 ssl redirect
  final boolean followRedirects;//是否执行 redirect
  final boolean retryOnConnectionFailure; //连接失败后是否重试
  final int connectTimeout;//连接超时时间
  final int readTimeout;// 读取超时
  final int writeTimeout;// 写入超时

Request和Client一样也是使用生成器模式完成的。
Request中包含了

  private final HttpUrl url;
  private final String method;
  private final Headers headers;
  private final RequestBody body;
  private final Object tag;

  private volatile CacheControl cacheControl; // Lazily initialized.

Request 中包含的内容都是比较清晰的,一个是HttpUrl对象。主要内容就是请求地址,实例化的时候可以指定,协议,端口等。method就是指请求方法,Headers是请求头的封装。RequestBody就是请求主体。

HttpUrl 的使用

    HttpUrl url = new HttpUrl.Builder()
        .scheme("https")
        .host("www.baidu.com")
        .addPathSegment("s")
        .addQueryParameter("wd", "dota2")
        .build();
    System.out.println(url);
 //  https://www.baidu.com/s?wd=dota2

得到了一个HttpUrl。还有很多其他的方法:
queryParameterName()queryParameterValue 获取get请求参数 等。

RequestBody是一个抽象类。还有两个子类FormBodyMultipartBody。主要差别在于MediaType.
回到例子中的Request对象,使用了默认的Request.Builder()来构建。默认的是get方法,默认的Header.Builder。tag默认是自身。

OkHttpClient 实现了Call.Factory接口,创建一个Call

public Call newCall(Request request) {
    return new RealCall(this, request);
  }

实际上接下来的工作都是由RealCall在完成的。
根据client和request创建一个RealCall实例,client被final修饰。默认会加上RetryAndFollowUpInterceptor参数。

  protected RealCall(OkHttpClient client, Request originalRequest) {
   this.client = client;
   this.originalRequest = originalRequest;
   this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
 }

然后就是execute()

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }
  1. 检查这个Call是否被执行过
  2. 利用client.dispatcher.execute(this)来完成实际的请求过程。Dispatcher在client文档中的表示的是异步请求的调度。实际上同步请求也是通过他们来调度完成的
  3. Reqsponse是通过getResponseWithInterceptorChain()方法获得的。从方法名可知,会通过一系列的拦截器。
  4. 最后通知dispatcher执行完成。

具体的Dispatcher稍后看,先关注发送请求解析返回结果的getResponseWithInterceptorChain()

  private Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

这个方法把网络请求,重试,缓存,压缩等操作都统一成一个个Interceptor。形成一个Interceptor.Chain,最后完成一次请求的过程。

通过getResponseWithInterceptorChain()可以看出Interceptor.Chain的组成部分:

  1. client.interceptors 构造client的时候设置的Interceptors
  2. retryAndFollowUpInterceptor负责失败重试和重定向。this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);在构造RealCall时创建。
  3. 桥接,将请求转换成发送到服务器的请求、通过网络响应构造一个服务器的返回响应d的BridgeInterceptor
  4. 读取缓存返回,更新缓存的CacheInterceptor
  5. 和服务器创建连接的ConnectInterceptor
  6. CallServerInterceptor makes a network call to the server 最后一个Interceptor.
    然后创建一个Interceptor.Chain 实例,执行proceed方法。得到Response.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • OkHttp源码的samples的简单使用的示例: public static void main(String....
    _warren阅读 690评论 0 1
  • 这篇文章主要讲 Android 网络请求时所使用到的各个请求库的关系,以及 OkHttp3 的介绍。(如理解有误,...
    小庄bb阅读 1,084评论 0 4
  • OkHttp解析系列 OkHttp解析(一)从用法看清原理OkHttp解析(二)网络连接OkHttp解析(三)关于...
    Hohohong阅读 20,851评论 4 58
  • OkHttp源码分析-同步篇 很早就想拿okhttp开刀了,这次就记一次使用OKhttp的网络请求。首先需要说明的...
    埃赛尔阅读 944评论 1 2
  • 小翠是我一个办公室的同事,最近被她父亲气的要死。 小翠父亲六十出头,身体硬朗,和老伴一起经营几亩地,过着自给自足的...
    家里真好阅读 2,550评论 21 76