OkHttp( 3.9.0-SNAPSHOT)源码解析

OkHttp源码的samples的简单使用的示例:

public static void main(String... args) throws Exception{

OkHttpClient client=new OkHttpClient();

// Create request for remote resource.

Request request=new Request.Builder()

.url(ENDPOINT)

.build();

// Execute the request and retrieve the response.

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

// Deserialize HTTP response to concrete type.

ResponseBody body=response.body();

Listcontributors=CONTRIBUTORS_JSON_ADAPTER.fromJson(body.source());

body.close();

......

}

静态常量

OkHttpClient开始就定义了两个静态常量,这两个常量是与协议相关分别是DEFAULT_CONNECTION_SPECS和DEFAULT_PROTOCOLS。

静态代码块

接下来看OkHttpClient的static代码块主要就是创建了一个Internal的内部类对象。

接口

OkHttpClient实现了三个接口,分别是Cloneable,Call.Factory,WebSocket.Factory

重点在Call.Factory接口上,Call该接口的实例就是我么去执行请求网络数据的对象,

Call中的主要方法

/** 返回该Call创建的Request请求对象 */

Request request();

/** 同步执行请求网络数据,返回服务端响应结果*/

Response execute()throwsIOException;

/** 异步执行请求网络数据的回调方法*/

void enqueue(Callback responseCallback);

/** 取消网络请求*/

void cancel();

/** 判断网络请求是否被执行了*/

boolean isExecuted();

/** 判断网络请求是否被取消了*/

boolean isCanceled();

/**clone该方法是Cloneable接口中的方法,意义在于创建一个相同的请求对象*/

Call clone();

/**Factory内部接口就是创建Call的工厂接口,该接口定义的方法newCall需要传入Request对象*/

interface Factory{

Call newCall(Request request);

}

成员变量

最重要的就是OkHttpClient,该类的成员变量中6个成员变量进行重点说明的

final Dispatcher dispatcher;

final List interceptors;

final List networkInterceptors;

final EventListener.Factory eventListenerFactory;

final @Nullable Cache cache; 

final @Nullable InternalCache internalCache;

1、同步请求过程解析

Response response=client.newCall(request).execute(); 这句代码执行过程:

(1)client.newCall(request)

第一步:上面提到OkHttpClient的三个接口中有个Factory内部接口(创建Call的工厂接口),而Call对象有个实现类RealCall,其实newCall方法是RealCall执行自己的newRealCall方法创建了一个RealCall实例,该方法三个参数如下:

*@param client OkHttpClient对象

*@param originalRequest 之前传入的那个原始请求对象

*@param forWebSocket 是否是WebSocket

RealCall call=newRealCall(client,originalRequest,forWebSocket);

第二步:上面提到OkHttpClient的6个成员变量中有个EventListener.Factory eventListenerFactory对象(也是个工厂接口,内部定义了一个create方法用于创建EventListener对象),上一步传入了OkHttpClient对象,该对象已经创建完了eventListenerFactory实例,所以拿到该实例传入call对象创建EventListener对象。

抽象类EventListener中有一大堆的网络连接的监听方法

call.eventListener=client.eventListenerFactory().create(call);

最后返回当前的Call对象

(2)call.execute()

@Override

publicResponse execute()throwsIOException{

    synchronized(this) {

    if(executed)throw newIllegalStateException("Already Executed");

     executed=true;

}

   captureCallStackTrace();

 try{

       client.dispatcher().executed(this);

      Response result=getResponseWithInterceptorChain();

      if(result==null)throw newIOException("Canceled");

       returnresult;

      }finally{

       client.dispatcher().finished(this);

   }

}

2.1)call中的execute方法中的client.dispatcher().executed(this);

由于execute是同步执行网络请求所以要用关键字synchronized,

synchronized(this) {

    if(executed)throw newIllegalStateException("Already Executed");

   executed=true;

}

之后再执行captureCallStackTrace()方法,放入堆栈进行追踪捕捉。

client.dispatcher()是获得上面OkHttpClient的成员变量Dispatcher dispatcher,注意啦Dispatcher中的方法基本都是同步,用的Synchronized修饰的方法。为了分析清楚这里插一段Dispatcher的说明。

Dispatcher中维护着执行发送请求的线程池,所有的请求都是放在请求队列中的总共有三个队列readyAsyncCalls(准备的异步请求队列),runningAsyncCalls(正在在执行的异步请求还包括被中断的),runningSyncCalls(正在在执行的异步请求还包括被中断的)

private final Deque<AsyncCall> readyAsyncCalls=newArrayDeque<>();

private final Deque<AsyncCall> runningAsyncCalls=newArrayDeque<>();

private final Deque<RealCall> runningSyncCalls=newArrayDeque<>();

RealCall和AsyncCall的关系通过Dispatcher类的导入代码可以看出

import okhttp3.RealCall.AsyncCall;   // AsyncCall其实就是RealCall的内部类

具体实现如下:

final class AsyncCall extends NamedRunnable{

private final Callback responseCallback;

// 构造时需要传入一个Callback接口,也就是返回给我们成功失败的回调方法

AsyncCall(Callback responseCallback) {  

   super("OkHttp %s",redactedUrl());

   this.responseCallback=responseCallback;

}

String host() {

    return originalRequest.url().host();

}

Request request() {

     return originalRequest;

}

RealCall  get() {

    return RealCall.this;

}

@Override

protected void execute() {

          boolean signalledCallback=false;

   try{

          Response response=getResponseWithInterceptorChain();

          if(retryAndFollowUpInterceptor.isCanceled()) {

          signalledCallback=true;

          responseCallback.onFailure(RealCall.this,newIOException("Canceled"));

      }else{

          signalledCallback=true;

          responseCallback.onResponse(RealCall.this,response);

     }

  }catch(IOException e) {

       if(signalledCallback) {

       // Do not signal the callback twice!

        Platform.get().log(INFO,"Callback failure for "+toLoggableString(),e);

    }else{

       responseCallback.onFailure(RealCall.this,e);

   }

      }finally{

        client.dispatcher().finished(this);

      }

   }

}

dispatcher中erexecuted方法的具体实现

//  加入同步执行队列

synchronized void executed(RealCall call) {

  runningSyncCalls.add(call);

}

2.2)Response result=getResponseWithInterceptorChain();

下面回过头去看RealCall的execute方法中client.dispatcher().executed(this);执行结束后的代码Response result=getResponseWithInterceptorChain();

为了分析清楚这里插一段Interceptor的说明

Response getResponseWithInterceptorChain()throwsIOException{

//创建一个存放拦截器的集合,最后传入 newRealInterceptorChain对象中

   List<Intercepter>  interceptors=newArrayList<>();

// client.interceptors() (自定义的拦截器, 在call api的前的拦截) - > //retryAndFollowUpInterceptor (实现请求重试)

   interceptors.addAll(client.interceptors());

// 重定向拦截器

   interceptors.add(retryAndFollowUpInterceptor);

// 桥接拦截器(处理header 、cookie 等)

   interceptors.add(newBridgeInterceptor(client.cookieJar()));

// 缓存拦截器(处理 cache)

   interceptors.add(newCacheInterceptor(client.internalCache()));

// 连接拦截器(负责建立连接)

   interceptors.add(newConnectInterceptor(client));

   if(!forWebSocket) {

// 自定义网络拦截器(此时已建立连接)

     interceptors.addAll(client.networkInterceptors());

  }

// 服务请求拦截器(发起请求、接收响应)

   interceptors.add(newCallServerInterceptor(forWebSocket));

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

上面的RealInterceptorChain方法具体实现。在RealCall里的getResponseWithInterceptorChain方法里,创建了一个RealInterceptorChain对象,调用proceed(),在interceptor的intercept()方法里又调用proceed(),明显形成了一个递归,像链表一下一个一个递归传递并且做相应的拦截处理。

RealInterceptorChain next=newRealInterceptorChain(interceptors,streamAllocation,httpCodec,

connection,index+1,request,call,eventListener);

Interceptor interceptor=interceptors.get(index);

Response response=interceptor.intercept(next);

最后一个拦截器返回处理的Response,也就是服务器端返回的结果,并且在代码中加了许多的判断如:this.httpCodec!=null 意思就是还没有拦截链执行完。interceptor必须执行一次proceed()方法,否则会抛异常。

RealInterceptorChain + Interceptor实现了装饰器模式,实现了请求/响应的串式或流式处理。只不过内层装饰器不是外层装饰器的成员变量,而是接口方法中创建的临时变量。

在ConnectInterceptor之后的拦截器必须满足:request的url要一致,interceptor必须执行一次proceed()。这样子做是为了保证递推的正常运作。而对与client.interceptors是在ConnectInterceptor之前的拦截器,可以不用必须执行一次proceed()。可以实现直接返回虚拟的response用于是测试等功能。

这几个Interceptor的职责:

RetryAndFollowUpInterceptor --->创建StreamAllocation对象,处理http的redirect,出错重试。对后续Interceptor的执行的影响:修改request及StreamAllocation。

BridgeInterceptor-------------->补全缺失的一些http header。对后续Interceptor的执行的影响:修改request。

CacheInterceptor-------------->处理http缓存。对后续Interceptor的执行的影响:若缓存中有所需请求的响应,则后续Interceptor不再执行。

ConnectInterceptor------------>借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响:创建了httpStream和connection。

CallServerInterceptor----------->处理IO,与服务器进行数据交换。对后续Interceptor的执行的影响:为Interceptor链中的最后一个Interceptor,没有后续Interceptor

2.3client.dispatcher().finished(this); 

dispatcher中finish方法的具体实现(注意第三个参数的差别)

/** Used by {@codeCall #execute} to signal completion.同步方法 */

void finished(RealCall call) {

    finished(runningSyncCalls,call,false);

}

/** Used by {@codeAsyncCall#run} to signal completion. 异步方法*/

void  finished(AsyncCall call) {

    finished(runningAsyncCalls,call,true);

}

上面的finished(runningAsyncCalls,call,true);方法实现如下:

private void finished(Dequecalls,T call,boolean promoteCalls) {

   int  runningCallsCount;

   Runnable idleCallback;

  synchronized(this) {

    if (!calls.remove(call) ) throw newAssertionError("Call wasn't in-flight!");

      if(promoteCalls)  promoteCalls(); // 异步Call数量多时需要维护队列,而不是立即执.行,所以要加入到runningAsyncCalls中

       runningCallsCount = runningCallsCount();

       idleCallback = this.idleCallback;

  }

  if(runningCallsCount==0&&idleCallback!=null) {

    idleCallback.run();

  }

}

上面promoteCalls方法的具体实现如下:

private int maxRequests=64;

private int maxRequestsPerHost=5;

private void promoteCalls() {

    if(runningAsyncCalls.size()>=maxRequests) return;// 已经达到最大容器.

     if ( readyAsyncCalls.isEmpty() ) return; // 队列为空

        for(Iterator i=readyAsyncCalls.iterator();i.hasNext();) {  // 遍历取出

          AsyncCall call=i.next();  

 // 当正在执行的任务总数及相同host下的任务数小于最大值时,直接执行当前请求,而任务数超过限定时,将其加入等待队列。

          if ( runningCallsForHost(call) < maxRequestsPerHost ){ 

             i.remove();

            runningAsyncCalls.add(call);

            executorService().execute(call);

          }

     }

    if ( runningAsyncCalls.size()>=maxRequests ) return;// Reached max capacity.

 }

}

还可以通setMaxRequests()设置同时允许执行的最大请求数,以及setMaxRequestsPerHost()设置相同host下最多运行的请求数。从源码中看出OkHttpClient用了许多的Budiler设计模式,几个重要的类Response、Request、OkHttpClient。

2、异步请求过程解析

RealCall的enqueue方法,其中遇到的大部分方法在同步请求中有分析就不赘述了。主要讲一下异步网络请求的流程

@Override

public voidenqueue(Callback responseCallback) {

  synchronized(this) {

      if(executed)throw newIllegalStateException("Already Executed");  

     executed=true;

 }

    captureCallStackTrace();

    client.dispatcher().enqueue(newAsyncCall(responseCallback));

}

上面client.dispatcher().enqueue(newAsyncCall(responseCallback));中Dispatcher的enqueue方法

synchronized void enqueue(AsyncCall call) {

// 如果正在执行异步请求的队列没有超过最大请求数量,并且有没有超过每个主机允许的最大访问量

// 就将请求加入到正在执行异步请求的队列中,否则就加入到准备异步请求的队列中去

  if(runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost

     runningAsyncCalls.add(call); 

     executorService().execute(call);

   }else{

      readyAsyncCalls.add(call);

   }


异步请求返回是通过接口回调的具体实现如下:

final classAsyncCallextendsNamedRunnable{

private finalCallback responseCallback;

AsyncCall(Callback responseCallback) {

    super("OkHttp %s",redactedUrl());

    this.responseCallback=responseCallback;

}

String host() {

    return  originalRequest.url().host();

}

Request request() {

  return originalRequest;

}

RealCall get() {

    return  RealCall.this;

}

@Override

protected voidexecute() {

   booleansignalledCallback=false;

try{

    Response response=getResponseWithInterceptorChain();

    if (retryAndFollowUpInterceptor.isCanceled() ) {

       signalledCallback=true;

       responseCallback.onFailure(RealCall.this,newIOException("Canceled"));

    }else{

      signalledCallback=true;

      responseCallback.onResponse(RealCall.this,response);

}

  }catch(IOException e) {

        if(signalledCallback) {

          // Do not signal the callback twice!

          Platform.get().log(INFO,"Callback failure for "+toLoggableString(),e);

      }else{

          responseCallback.onFailure(RealCall.this,e);

       }

   }finally{

     client.dispatcher().finished(this);

   }

 }

}

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

推荐阅读更多精彩内容