OKHttp请求流程概述及涉及的设计模式

通过前两篇文章,大家对HTTP的许多概念及OkHttp的类结构及使用已经有了解,这篇主要讲一下OKHttp的整个请求流程以及这中间涉及到的一些设计模式,过程中会忽略掉一些局部比较细节的问题,这些忽略的部分会在后面的文章中继续进行深入讲解。

发起请求

我们先看一下OKHttp最外层使用的形式:

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
        .url(url)
        .build();
Call call = client.newCall(request);
//同步请求
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 {
        //请求成功
      }
    });

如上代码既是一般的使用流程,总结为4步:
1 创建OkHttpClient实例
2 创建Request实例
3 用client实例的newCall方法创建一个Call实例
4 调用Call的同步或异步请求方法,得到Response实例
对外部使用来说,这就完成了一次请求。
这4步涉及4个对象:OkHttpClient、Request、Call、Response。

Requst

首先看Requst对象,这个对象主要携带一些我们发出请求所需的数据,比如请求地址、请求头信息等,以下是它所有属性:

  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Object tag;
  private volatile CacheControl cacheControl;

这几个属性都很简单:url即我们请求的地址;method即请求方法,默认是GET,我们可以设置成POST等;headers是一些头信息;body是RequestBody对象,这个对象可以帮我们向服务端传送各种格式的数据,后面会简单介绍一下;tag是这个Request对象的标记,用来找到这个请求对象,以便在后续流程中进行取消请求等操作;cacheControl用来控制缓存操作,设置此次请求是否需要缓存以及缓存时间等,这里不加详述,后续文章中会有专门针对缓存的介绍。
再简单介绍一下RequestBody对象,这是一个抽象类,只有下面3个方法需要实现:

  public abstract @Nullable MediaType contentType();

  public long contentLength() throws IOException {
    return -1;
  }

  public abstract void writeTo(BufferedSink sink) throws IOException;

这个类还提供了5个快速创建实例的create方法,只需传入MediaType和相应的内容数据即可创建实例,如下面代码:

MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String json = "{name:abc,age:18}";
RequestBody body = RequestBody.create(JSON, json);

OkHttpClient

有了Request里的请求信息,就可以用OkHttpClient创建Call对象了,OkHttpClient
对象有很多属性,这里不再一一列举,可以理解它就是整个系统的上下文,收集了请求流程中会用到的各种数据,这个对象本身也会在请求发起时向下传递。这里只关注一下例子中用到的newCall函数,源码如下:

@Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

可见这个函数实际返回了一共RealCall对象,一个Call的子类。RealCall构造函数的第一个参数就是OkHttpClient的this,即把这个对象向下传递了,第二个参数是Request,就把请求信息传递了下去,第三个参数暂时不用关心,是关于web socket的。

Call & RealCall

Call是个接口,RealCall是它唯一的实现子类。通过其构造函数我们知道RealCall里保存了OkHttpClient和Requst,对于外部调用来说当执行execute或enqueue方法时才真正是网络请求的开始。
先看execute方法:

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      //将这个请求交给dispatcher管理
      client.dispatcher().executed(this);
      //真正请求开始
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

其中比较关键的是这两句:
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
第一句将这次请求加入到分发器的管理之中,分发器即Dispatcher类,这个类中有几个保存请求的队列,我们发出的每个请求都会添加到相应的队列,我们可以在请求流程中对每个请求进行状态查询或取消等操作。这个类就介绍这么多吧,这个类会在后续文章详细介绍。
第二句是真正的发起请求,我们看到调用了getResponseWithInterceptorChain方法,返回值是Response,说明调用完这个方法,我们获得了服务器发回的请求结果。下面详细介绍这个方法。

getResponseWithInterceptorChain方法

方法源码:

  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 (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest, this, eventListener);
    return chain.proceed(originalRequest);
  }
}

从源码看,这个方法很简单,就是在一个列表里添加一系列的拦截器(xxxInterceptor),然后创建了一个RealInterceptorChain对象,并调用其proceed方法,整个方法就走完了。但这个方法在整个请求流程中是非常重要的,而且这里还涉及一个责任链模式。
先说一下责任链模式,GOF是这样定义的:

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。

2012082915050182.jpg

可以简单理解为,当有一个请求需要处理,这时候有一条处理链,链上每个环都有可能处理这个请求,但是最终是哪些环参与了处理,是不一定的,只知道最终这个请求会被处理。因为最终会被哪些环节处理以及顺序是怎样的都不一定,这样动态性很强。

对应到OkHttp,我们发出一个请求,这个请求也会经历一系列的处理,上面代码中添加的每个拦截器都有可能进行处理

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

推荐阅读更多精彩内容