okhttp源码解析(一)

一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献,用于替代HttpUrlConnection和Apache HttpClient。

我将通过一系列的文章来分析okhttp的源码:

如何引入

在app的build.gradle文件中

dependencies {
       compile ‘com.squareup.okhttp3:okhttp:3.10.0’
}
这里引入的3.10.0版本,该系列文章分析的源码均基于这个版本。

如何使用

  • 同步请求
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url("http://xxx").get().build();
Call call  = client.newCall(request);
try {
    call.execute();
} catch (IOException e) {
    e.printStackTrace();
}
  • 异步请求
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url("http://xxx").get().build();
Call call  = client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException{
    }
});

创建过程分析

通过上面同步和异步get请求的使用方法,大致可以发现okhttp暴露给上层开发者使用的主要类OkHttpClient、Request和Call,当然不仅仅只是这些接下来我们就对这3个类进行分析

OkhttpClient的创建
OkHttpClient client = new OkHttpClient.Builder().build();

OkHttpClient.Builder, OkHttpClient的内部类,相信见到这个Builder大家都不会陌生,这种构建对象的方式就是设计模式中的建造者模式,当构建一个对象参数比较多,并且根据不同的参数实现不同的对象特征的时候就会使用这种模式。创建过程不是我们分析的重点,感兴趣的小伙伴可以去源码里看一下。看过我文章的都知道我的源码分析都是配合我的灵魂画技来展示的,下面就用UML类图的方式来表现OkhttpClient类中比较重要的部分
这里写图片描述
OkHttpClient中比较重要的属性和方法都在这个图中了,其中重要属性Dispatcher,重要方法Call newCall(Request request)我们会在稍后的部分进行分析。

Request的创建
Request request = new Request.Builder().url("http://xxxx").get().build();

Request对象的创建跟OkHttpClient一样都是建造者模式。Request对象内部就简单的多了,就是封装了请求需要的url、方法等。
这里写图片描述

Call的创建
Call call = client.newCall(request);

Call对象的创建是调用的OkHttpClient对象的newCall方法,传入一个Request对象参数。接下来我们看一下OkHttpClient中的newCall方法。

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

newCall方法的返回值类型是一个Call,需要的入参是一个Request对象。
这里调用了RealCall类中静态方法newRealCall(),newRealCall()的返回类型是一个Call对象。接下来我们到RealCall类中看一看具体实现

RealClall

final class RealCall implements Call {
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  /**
 1. There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
 2. This will be set after we create the call instance then create the event listener instance.
   */
  private EventListener eventListener;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

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

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
}

RealCall类中静态方法newRealCall()的返回值类型是一个RealCall,内部通过构造函数构建了一个RealCall对象,并且我们发现Call原来是一个接口,而RealCall对象是Call的实现类。OkhttpClient对象调用newCall(request)方法实际获得的是Call接口的实现类RealCall的对象。到这里我们的3个类的创建过程都清晰了。

接下来我们简单看一下RealCall类。

RealCall.AsyncCall
RealCall中有一个内部类AsyncCall,AsyncCall从名字就可以看出来和异步相关,而AsyncCall中就有一个很重要的属性responseCallback,就是我们网络请求的回调接口,AsyncCall继承自NamedRunnable,而NamedRunnable是抽象类,并且是Runnable接口的实现类,那么我从这层关系就可以知道RealCall的内部类AsyncCall是一个线程类。好了暂时先看到这里,这个AsyncCall后面会再次提到,当然RealCall中还有一些很重要的属性和方法,这些都交给我这个灵魂画手吧~!
这里写图片描述
以上就是RealCall。

OK到此我们把OkhttpClient、Request和Call都分析了一下,这里对于创建过程做一个总结:
1. OkHttpClient Builder方式创建。
2. Request Builder方式创建。
3. Call是一个接口,实现类是RealCall,RealCall是通过OkHttpClient 对象调用。
newCall方法传入Request 对象,内部调用的RealCall的静态方法newRealCall方法通过构造函数创建。

下面就是灵魂画手最激动人心的时刻,将这三者的关系通过UML类图表现出来。出来吧~叼毛兽~!
这里写图片描述

得到了Call对象我们就可以用这个对象来执行同步请求或者异步请求了,从上面的分析来看:

同步请求实际就调用了RealCall的execute();
异步请求实际就调用了RealCall的enqueue(Callback responseCallback);

接下来就来分别分析上面的提到的两个方法。

  • execute()
@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);
    }
  }

以上就是execute()方法的内部逻辑,我们这里只分析主干线,第一个if判断

if (executed) throw new IllegalStateException("Already Executed");
      executed = true;

表示请求只能执行一次,不然我分分钟钟抛个异常给你看。然后我们看try{}部分

try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    }

代码也很简单,try的逻辑也很简单,主要的就是上面两行代码,这里分别执行了client.dispatcher().executed(this)方法和getResponseWithInterceptorChain()方法。

getResponseWithInterceptorChain()这个方法我们后续篇章会单独去讲解它,这里面是okhttp中很重要的一个概念【拦截器】。

我们再来看finally{}部分。

finally {
      client.dispatcher().finished(this);
}

可以发现无论是try部分还是最终的finally部分都出现了client.dispatcher(),都是调用client.dispatcher()所返回的对象的响应方法来进行处理,client我们自然不陌生就是OkHttpClient的对象。那么它的dispatcher()方法返回了什么呢? 还记得我们OkHttpClient的UML类图吗?不记得了我们可以翻回前面看一下,上文有提到OkHttpClient中的重要属性和方法,newCall我们已经分析过了,剩下的就是重要属性Dispatcher,而client.dispatcher()返回的就是Dispatcher在OkHttpClient中的属性dispatcher。

public Dispatcher dispatcher() {
    return dispatcher;
}

到这里我们的同步请求就把逻辑引入到Dispatcher类中。我们先不去看这个类,再回过头来看看Call的异步请求!

  • enqueue(Callback responseCallback)
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

于同步请求相同的if判断,这里不在赘述,关键代码就一行

client.dispatcher().enqueue(new AsyncCall(responseCallback));

同样的异步请求也将逻辑引入到Dispatcher类中。那么接下来我们就分析Dispatcher类,看看这个类在okhttp中是充当了什么样的角色,同步和异步的请求都在这个类中做了些什么。

Dispatcher的创建

这次我们先上图
这里写图片描述
Dispatcher的创建离不开OkHttpClient,Dispatcher是作为OkHttpClient对象的属性存在的,OkHttpClient在通过Builder()方式创建对象的时候就构建了Dispatcher对象,为属性dispatcher赋值。这里我们截取部分OkHttpClient的创建过程。

public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      ......//省略部分代码
}

接下来深入Dispathcer类,其中重要的属性和方法都在上面的图中体现了,可以看到Dispathcer中有线程池对象,还有3个队列,以及同步请求和异步请求在RealCall中出现的方法execute()和enqueue(Callback responseCallback)的真正逻辑部分。

其实从属性上我们大致可以看出这个类的重要性,线程池、队列等等,我能想到的就是这个类维护了线程池,管理队列,然后调度执行请求任务。那么是不是这样的呢,我们从Dispathcer源码中结合UML图来找答案。

Dispathcer成员变量

//最大并发请求数为64
private int maxRequests = 64;
//每个主机最大请求数为5
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;

/** Executes calls. Created lazily. */
//线程池
private @Nullable 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 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;
  }

同步请求:

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
}

同步请求逻辑很简单,就是将RealCall 加入到同步请求队列。

异步请求:

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

异步比同步复杂一些,判断了队列的长度是否大于maxRequests (64) 并且当前主机执行请求的数是否大于maxRequestsPerHost(5)。
true:
加入到正在执行的异步请求队列,调用线程池执行AsyncCall ,还记得AsyncCall 吧,它是RealCall的内部类,是一个线程类。
false:
加入到准备执行的异步请求队列中

因为线程池调用的是AsyncCall这个线程类,这里我们就要跳到RealCall的内部类AsyncCall中去看相应的方法。我们知道AsyncCall既然是线程类一定有run方法,我们在讲RealCall的时候,通过UML类图可以发现AsyncCall继承自NamedRunnable这个抽象类,NamedRunnable的run方法中执行了execute()方法,由于execute()方法是一个抽象方法,那么转了一圈我们又回到了AsyncCall类中来看具体实现。

@Override protected void execute() {
     boolean signalledCallback = false;
     try {
       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 {
       client.dispatcher().finished(this);
     }
}

同样的看try{}部分

try {
     Response response = getResponseWithInterceptorChain();
      if (retryAndFollowUpInterceptor.isCanceled()) {
        signalledCallback = true;
        responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
      } else {
        signalledCallback = true;
        responseCallback.onResponse(RealCall.this, response);
      }
} 

getResponseWithInterceptorChain()这个重要的方法也出现在了异步中
finally部分

finally {
    client.dispatcher().finished(this);
}

可以发现同步和异步其实finally 部分都涉及到了Dispatcher类,都是调用了它的finished方法

其他重要方法

/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
   finished(runningAsyncCalls, call, true);
}

/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
   finished(runningSyncCalls, call, false);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
   int runningCallsCount;
   Runnable idleCallback;
   synchronized (this) {
     if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
     if (promoteCalls) promoteCalls();
     runningCallsCount = runningCallsCount();
     idleCallback = this.idleCallback;
   }

   if (runningCallsCount == 0 && idleCallback != null) {
     idleCallback.run();
   }
}

public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
}

无论是同步还是异步请求,请求完成后调用了finished从队列中移除call请求任务。

到这里我们就可以对Dispatcher进行一个分析总结了,Dispatcher就像它的英文翻译一样调度器,无论是同步还是异步都是通过RealCall调用Dispatcher这个okhttp的分发调度器通过线程池来执行具体的请求任务。请求完成后又将任务从队列中清除。

Dispatcher总结:
发送的同步和异步请求都会在Dispatcher中进行状态的管理,维护请求状态,并且内部维护一个线程池用于执行请求。

到此我们的这一篇源码分析就基本完事了,而在同步和异步请求中都出现的代码

Response response = getResponseWithInterceptorChain();

我们将在下一篇中进行分析,来讲一讲okhttp的拦截器。敬请期待

猜你喜欢

转载自blog.csdn.net/omyrobin/article/details/81773804