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

//Call接口的实现类
final class RealCall implements Call {
  //OkHttpClient对象
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
  private EventListener eventListener;
  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);
  }
  
  //静态方法,内部通过构造函数创建RealCall对象
  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,就是我们网络请求的回调接口Callback,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对象我们就可以用这个对象来执行同步请求或者异步请求了,从上面的分析来看Call是一个接口,那么我们就去它的实现类RealCall中去看:

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

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

  • execute() ------Call的同步请求方法
@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。

OkhttpClient中的 **dispatcher()**方法

public Dispatcher dispatcher() {
    return dispatcher;
}

方法很简单,就是返回Dispatcher类的实例对象,到这里我们的同步请求就把逻辑引入到Dispatcher类中。我们先不去看这个类,再回过头来看看Call的异步请求!

  • enqueue(Callback responseCallback) ------Call的异步请求方法
@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对象的成员变量dispatcher 存在的,OkHttpClient在通过Builder()方式创建对象的时候就构建了Dispatcher对象,为成员变量dispatcher赋值。这里我们截取部分OkHttpClient的创建过程。

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

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

其实从成员变量上我们大致可以看出这个类的重要性,线程池、队列等等,我们都知道Android中网络请求都是在子线程中进行,频繁的创建新线程是很耗费资源的事情,一般需要频繁创建线程的情况,我们都是使用线程池来管理线程的创建和销毁,所以我能想到的就是Dispathcer类内部维护了线程池,管理请求任务队列,调度执行请求任务。那么是不是这样的呢?我们从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方法,下面我将同步和异步中调用的finished方法一起列出来,方便观看和比较。

同步和异步finally中的重要方法


//异步请求最终调用
/** 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!");
     //根据传入的promoteCalls判断是否执行promoteCalls()方法,
     //同步传入的false,异步传入的true
     if (promoteCalls) promoteCalls();
     runningCallsCount = runningCallsCount();
     idleCallback = this.idleCallback;
   }

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

//异步请求的promoteCalls为true才会调用到该方法
private void promoteCalls() {
	//已经达到最大并发数直接return
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    //没有准备执行的任务直接return
    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.
    }
  }

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

无论是同步还是异步请求,请求完成后调用了finished从队列中移除call请求任务。异步方法还会执行promoteCalls()方法,循环从readyAsyncCalls准备执行任务队列中获取Call对象加入到runningAsyncCalls正在执行任务队列中。

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

接下来又到的激动人心的时刻,叼毛兽升级~!

在这里插入图片描述

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

到此我们的这一篇源码分析就基本完事了,到现在为止我们只是分析了一部分的执行过程,真正的网络请求部分我还没有涉及到,而这些都是在同步和异步请求中都出现的代码

Response response = getResponseWithInterceptorChain();

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

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值