OkHttp Source Code Analyse (Part one)

OkHttp 

An HTTP & HTTP/2 client for Android and Java applications

HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth.

OkHttp is an HTTP client that’s efficient by default:

  • HTTP/2 support allows all requests to the same host to share a socket.
  • Connection pooling reduces request latency (if HTTP/2 isn’t available).
  • Transparent GZIP shrinks download sizes.
  • Response caching avoids the network completely for repeat requests.

OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and for services hosted in redundant data centers. OkHttp initiates new connections with modern TLS features (SNI, ALPN), and falls back to TLS 1.0 if the handshake fails.

Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks.

OkHttp supports Android 2.3 and above. For Java, the minimum requirement is 1.7.

From:http://square.github.io/okhttp/



Example:

OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("www.baidu.com")
                .build();
        
        //sync method
        Response response = client.newCall(request).execute();

        //async method
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });


主要分析一下client.newCall(request).enqueue(Callback callback)方法:

(1)client.newCall(Request request);

OkHttpClient.java

/**
 * Prepares the {@code request} to be executed at some point in the future.
 */
@Override public Call newCall(Request request) {
  return new RealCall(this, request);
}
protected RealCall(OkHttpClient client, Request originalRequest) {
  this.client = client;
  this.originalRequest = originalRequest;
}


(2)client.newCall(request).enqueue(Callback responesCallback);

RealCall.java

@Override public void enqueue(Callback responseCallback) {
  enqueue(responseCallback, false);
}

void enqueue(Callback responseCallback, boolean forWebSocket) {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
(2-1)client.dispatcher();

Dispatcher.java

private int maxRequests = 64;
private int maxRequestsPerHost = 5;

/** Executes calls. Created lazily. */
private ExecutorService executorService;

/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

public Dispatcher(ExecutorService executorService) {
  this.executorService = executorService;
}

public Dispatcher() {
}

public synchronized ExecutorService executorService() {
  if (executorService == null) {
    executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
  }
  return executorService;
}

可以看出Dispatcher内包含一个TheadPoolExecutor(线程池),负责管理并执行加入其中的任务,

重点区分下面这三个Deque:

readyAsyncCalls:等待执行的任务队列;

runningAsyncCalls:正在执行的异步任务队列;

runningSyncCalls:正在执行的同步阻塞任务队列;


(2-2) client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));

Dispatcher.java

synchronized void enqueue(AsyncCall call) {
  if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    runningAsyncCalls.add(call);
    executorService().execute(call);
  } else {
    readyAsyncCalls.add(call);
  }
}

可以看出:当向Dispather中加入新的异步任务时,如果当前正在执行的异步任务数小于64并且在所有正在执行的异步任务中与该新任务拥有相同主机名的任务小于5时,直接将该任务加入正在执行的任务队列中,并开始执行它,否则加入等待队列。

(3)线程池执行一个异步任务AsycCall(实际就是一个Runnable),会调用该类的execute()方法;

RealCall.java

@Override protected void execute() {
  boolean signalledCallback = false;
  try {
    Response response = getResponseWithInterceptorChain(forWebSocket);
    if (canceled) {
      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 {
      responseCallback.onFailure(RealCall.this, e);
    }
  } finally {
    client.dispatcher().finished(this);
  }
}

(3-1) Response response = getResponseWithInterceptorChain(forWebSocket);

发送网络请求,并获取响应,后续会详细介绍,获取响应或出现异常 通过Callback responseCallback回调给发送请求方。

(3-2)最后会执行 client.dispatcher().finished(this);

Dispatcher.java

/** Used by {@code AsyncCall#run} to signal completion. */
synchronized void finished(AsyncCall call) {
  if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
  promoteCalls();
}

private void promoteCalls() {
  if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
  if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

  for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
    AsyncCall call = i.next();

    if (runningCallsForHost(call) < maxRequestsPerHost) {
      i.remove();
      runningAsyncCalls.add(call);
      executorService().execute(call);
    }

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

可以看出,在finished(AsyncCall call)中将刚刚完成的AsyncCall从runningAsyncCalls中移除,然后调用promoteCalls()方法;

在promoteCalls()方法中,如果当前正在执行的任务个数大于等于64,表示线程池已经达到最大并发量,不能再加入新任务,退出。如果当前等待队列为空,表示新任务可执行,退出。

否则就遍历readyAsyncCalls,如果某个任务的主机名在正在执行的任务队列的个数小于5 ,则将其从等待队列中移除并加入到runningAsyncCalls中并放入线程池执行,直至线程池达到最大并发量64。


然后,当线程池中的某任务执行完后,再调用Dispather的finished 方法,再从等待任务队列中取出新任务加入到线程池中执行。与此同时,也会有任务不断的加入Dispather中,当线程池中有任务执行完毕后,就会从等待队列中取出任务加入到线程池中执行。


每当有任务执行完毕后,都会主动检测
等待任务队列是否有可执行的任务,而没有使用互斥锁等逻辑,避免发生死锁。


 
 
  • OkHttp采用Dispatcher技术,类似于Nginx,与线程池配合实现了高并发,低阻塞的运行
  • Okhttp采用Deque作为缓存,按照入队的顺序先进先出
  • OkHttp最出彩的地方就是在try/finally中调用了finished函数,可以主动控制等待队列的移动,而不是采用锁或者wait/notify,极大减少了编码复杂性
作者:BlackSwift 链接:http://www.jianshu.com/p/6637369d02e7 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


Reference:http://www.jianshu.com/p/6637369d02e7


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是 okhttp-eventsource 的使用示例: 1. 添加依赖 在项目的 build.gradle 文件中添加以下依赖: ```groovy implementation 'com.squareup.okhttp3:okhttp:版本号' implementation 'com.github.morihacky:okhttp-eventsource:版本号' ``` 2. 创建 EventSource ```java OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://example.com/events") .build(); EventSource eventSource = new EventSource.Builder(callback, request) .build(); ``` 3. 实现回调 ```java EventSource.Listener callback = new EventSource.Listener() { @Override public void onOpen(EventSource eventSource, Response response) { // 连接成功 } @Override public void onMessage(EventSource eventSource, String id, String type, String data) { // 处理消息 } @Override public void onComment(EventSource eventSource, String comment) { // 处理注释 } @Override public boolean onRetryTime(EventSource eventSource, long milliseconds) { // 确定是否重新连接 return true; } @Override public boolean onRetryError(EventSource eventSource, Throwable throwable, Response response) { // 确定是否重新连接 return true; } @Override public void onClosed(EventSource eventSource) { // 连接关闭 } @Override public void onFailure(EventSource eventSource, Throwable t, Response response) { // 连接失败 } }; ``` 4. 连接和关闭 ```java // 连接 eventSource.start(); // 关闭 eventSource.close(); ``` 以上就是 okhttp-eventsource 的使用示例。通过这个库,你可以轻松地实现 SSE(Server-Sent Events)协议,从服务器实时接收信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值