博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
11:okhttp原理浅析
阅读量:5935 次
发布时间:2019-06-19

本文共 7951 字,大约阅读时间需要 26 分钟。

1、基本用法

GET请求

OkHttpClient client = new OkHttpClient();  Request request = new Request.Builder()      .url(url)      .build();  Response response = client.newCall(request).execute();  return response.body().string();}复制代码

POST请求

public static final MediaType JSON= MediaType.parse("application/json; charset=utf-8");  OkHttpClient client = new OkHttpClient();  RequestBody body = RequestBody.create(JSON, json);  Request request = new Request.Builder()      .url(url)      .post(body)      .build();  Response response = client.newCall(request).execute();复制代码

2、核心类、方法:

  • OkHttpClient
  • Request、Response
  • RealCall、AsyncCall
  • Dispatcher
  • Interceptor、RealInterceptorChain
/**   * 添加拦截器    * 通过RealInterceptorChain#proceed调用Interceptor集合中某个index拦截器的interceptor方法,   * 此方法内部会再new一个RealInterceptorChain对象,index++,再再调用其proceed方法,   * 形成递归调用。   * @return   * @throws IOException   */  Response getResponseWithInterceptorChain() throws IOException {    // Build a full stack of interceptors.    //TODO 责任链 倒序调用    List
interceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); //TODO 5、处理重试与重定向 interceptors.add(retryAndFollowUpInterceptor); //TODO 4、处理 配置请求头等信息 interceptors.add(new BridgeInterceptor(client.cookieJar())); //TODO 3、处理 缓存配置 根据条件(存在响应缓存并被设置为不变的或者响应在有效期内)返回缓存响应 //TODO 设置请求头(If-None-Match、If-Modified-Since等) 服务器可能返回304(未修改) interceptors.add(new CacheInterceptor(client.internalCache())); //TODO 2、连接服务器 interceptors.add(new ConnectInterceptor(client)); if (!forWebSocket) { interceptors.addAll(client.networkInterceptors()); } //TODO 1、执行流操作(写出请求体、获得响应数据) //TODO 进行http请求报文的封装与请求报文的解析 interceptors.add(new CallServerInterceptor(forWebSocket)); Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); return chain.proceed(originalRequest); }复制代码
// RealInterceptorChain.java  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,      RealConnection connection) throws IOException {    if (index >= interceptors.size()) throw new AssertionError();    calls++;    // If we already have a stream, confirm that the incoming request will use it.    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)          + " must retain the same host and port");    }    // If we already have a stream, confirm that this is the only call to chain.proceed().    if (this.httpCodec != null && calls > 1) {      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)          + " must call proceed() exactly once");    }    //创建新的拦截链,链中的拦截器集合index+1    // Call the next interceptor in the chain.    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,        writeTimeout);    //执行当前的拦截器 默认是:retryAndFollowUpInterceptor    Interceptor interceptor = interceptors.get(index);        //intercept方法中,又会调用chain.proceed()    Response response = interceptor.intercept(next);    // Confirm that the next interceptor made its required call to chain.proceed().    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {      throw new IllegalStateException("network interceptor " + interceptor          + " must call proceed() exactly once");    }    // Confirm that the intercepted response isn't null.    if (response == null) {      throw new NullPointerException("interceptor " + interceptor + " returned null");    }    if (response.body() == null) {      throw new IllegalStateException(          "interceptor " + interceptor + " returned a response with no body");    }    return response;  }复制代码

Dispatcher调度器内维持一个线程池,和三个队列。

// Ready async calls in the order they'll be run.   private final Deque
readyAsyncCalls = new ArrayDeque<>(); // Running asynchronous calls. Includes canceled calls that haven't finished yet. private final Deque
runningAsyncCalls = new ArrayDeque<>(); // Running synchronous calls. Includes canceled calls that haven't finished yet. private final Deque
runningSyncCalls = new ArrayDeque<>();复制代码
3、RealCall同步请求:execute方法

在Dispatcher#executed方法中将此RealCall对象加入runningSyncCalls队列;

下一步执行getResponseWithInterceptorChain()。

@Override public Response execute() throws IOException {    synchronized (this) {      if (executed) throw new IllegalStateException("Already Executed");      executed = true;    }    captureCallStackTrace();    eventListener.callStart(this);    try {      client.dispatcher().executed(this);      Response result = getResponseWithInterceptorChain();      if (result == null) throw new IOException("Canceled");      return result;    } catch (IOException e) {      eventListener.callFailed(this, e);      throw e;    } finally {      client.dispatcher().finished(this);    }  }复制代码
4、RealCall的异步请求:enqueue方法

创建AsyncCall对象,传入Dispatcher中

//RealCall.java  @Override public void enqueue(Callback responseCallback) {    synchronized (this) {      if (executed) throw new IllegalStateException("Already Executed");      executed = true;    }    captureCallStackTrace();    client.dispatcher().enqueue(new AsyncCall(responseCallback));  }复制代码

在Dispatcher中判断是将任务添加到执行队列并执行、还是将任务添加到等待队列

//Dispatcher.java  private int maxRequests = 64;  private int maxRequestsPerHost = 5;  synchronized void enqueue(AsyncCall call) {  //如果正在执行的请求小于设定值即64,并且请求同一个主机的request小于设定值即5    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {      //添加到执行队列,开始执行请求      runningAsyncCalls.add(call);      //获得当前线程池,没有则创建一个      executorService().execute(call);    } else {      //添加到等待队列中      readyAsyncCalls.add(call);    }  }复制代码

线程池执行AsyncCall#execute()方法,同样调用getResponseWithInterceptorChain()。

@Override protected void execute() {      boolean signalledCallback = false;      try {        //TODO 责任链模式        //TODO 拦截器链  执行请求        Response response = getResponseWithInterceptorChain();        //回调结果        if (retryAndFollowUpInterceptor.isCanceled()) {          signalledCallback = true;          responseCallback.onFailure(RealCall.this, new IOException("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 {          eventListener.callFailed(RealCall.this, e);          responseCallback.onFailure(RealCall.this, e);        }      } finally {        //TODO 移除队列        client.dispatcher().finished(this);      }    }  }复制代码

Dispatcher#finished将调用promoteCalls(),从等待队列中提取可执行的队列执行到线程池中执行,将其移到runningAsyncCalls队列。

private void promoteCalls() {        //TODO 检查 运行队列 与 等待队列        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.        for (Iterator
i = readyAsyncCalls.iterator(); i.hasNext(); ) { AsyncCall call = i.next(); //TODO 相同host的请求没有达到最大 if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningAsyncCalls.add(call); executorService().execute(call); } if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } }复制代码

OKHttp调度的"优雅'之处:

1、采用Dispacher作为调度,与线程池配合实现了高并发,低阻塞的的运行

2、采用Deque作为集合,按照入队的顺序先进先出

3、最精彩的就是在try/catch/finally中调用finished函数,可以主动控制队列的移动。避免了使用锁而wait/notify操作。

转载地址:http://lnjtx.baihongyu.com/

你可能感兴趣的文章
LeetCode - Subsets II
查看>>
Ubuntu 14.04 配置VNC服务 配置Xfce4桌面
查看>>
(转)supertable像excel那样固定table的表头和第一列
查看>>
R语言中的标准输入,输出, 错误流
查看>>
引用作形參--输入三个整数,採用地址的方法按从大到小排序
查看>>
西川善司【神秘海域(Uncharted)】的图形分析
查看>>
灵活定义神经网络结构
查看>>
WebRTC开发基础(WebRTC入门系列2:RTCPeerConnection)
查看>>
sql 2008 R2添加对MySql的远程服务器链接
查看>>
配置rhel 6.4(64位)安装使用syslog-ng 3.5
查看>>
Adrnoid开发系列(二十五):使用AlertDialog创建各种类型的对话框
查看>>
javascript深入理解js闭包
查看>>
博客园首行缩进问题
查看>>
iOS: ios视频播放(MPMediaPlayerController,AVPlayer,AVPlayerViewcontroller、ffmpeg-AVPlayer)...
查看>>
[Android Pro] 将你的安卓手机屏幕共享到PC或Mac上
查看>>
excel同时冻结首行和首列怎么操作
查看>>
[Cycle.js] Generalizing run() function for more types of sources
查看>>
phpmyadmin出现空password登录被禁止
查看>>
IOS开发基础知识--碎片34
查看>>
如何把Eclipse工程导入到Android Studio
查看>>