Android—OkHttp同步异步请求源码分析与区别

OkHttp同步请求步骤:

  1. 创建OkHttpClient,客户对象
  2. 创建Request,请求主体,在请求主体设置请求的url,超时时间等
  3. 用newCall(request)将Reuqest对象封装成Call对象,然后用Call对象的execute()发起同步请求。
  4. execute()返回的是Response对象。可以用execute().body().toString()得到请求所返回的主体内容。
val client = OkHttpClient()
val request = Request.Builder()
    .url("https://www.baidu.com")
    .build()
val response = client.newCall(request).execute().body().toString()

注意:发送请求后,就会进入阻塞状态,直到收到响应。

OkHttp异步请求步骤:

  1. 创建OkHttpClient,客户对象
  2. 创建Request,请求主体,在请求主体设置请求的url,超时时间等
  3. 用newCall(request)将Reuqest对象封装成Call对象,然后用Call对象的enqueue()发起异步请求。
  4. enqueue(object: Callback{重写onFailure、onResponse方法}) 在onResponse方法中获取申请数据内容。
val client = OkHttpClient()
val request = Request.Builder()
    .url("https://www.baidu.com")
    .build()
val response = client.newCall(request).enqueue(object: Callback {
    override fun onFailure(call: Call, e: IOException) {
        TODO("Not yet implemented")
    }

    override fun onResponse(call: Call, response: Response) {
        response.body().toString()
    }

})

源码分析:

注意:下面的源代码段可能来自不同一个类文件,只是将他们放一起,容易观察,主要放一些关键代码,其他会有...代替。

1.关于创建OkHttpClient对象,下面源码:

public OkHttpClient() {
    this(new Builder());
  }

public Builder() {
      dispatcher = new Dispatcher();   
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;   
      ......
      connectionPool = new ConnectionPool();
      .....
    }

可以看到OkHttp采用了建造者模式,在Builder()里面封装各种需要的属性,关键的主要有dispatcher分发器,connectionSpecs决定是异步还是同步,connectionPool 连接池。每个连接都会放入连接池中,由它进行管理。 

总结新建Client对象时,新建了一个分发器和一个连接池,还有一些属性的初始化。

2.创建Request对象时,源码:

public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tags = Util.immutableMap(builder.tags);
  }

可以看到Request里面也有一个Builder类,Builder构造函数默认请求方式为Get,还有对请求头部的封装。

总结:新建一个Request对象里面主要封装了请求路径,头部信息等。

3.用newCall(request)将Reuqest对象封装成Call对象时,源码:

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

//可以看到newCall方法里面是调用了RealCall类的newRealCall方法,下面到RealCall类里看看。

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// 调用RealCall的构造函数
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

//下面是RealCall类构造函数

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

总结:newCall方法实际生成RealCall对象,对象里面包含了Client客户对象和Request的请求对象,还新建了一个RetryAndFollowUpInterceptor 重定向拦截器。

4.execute()同步请求方法,源码:

@Override public Response execute() throws IOException {
   .....
//开启事件监听
   eventListener.callStart(this);   
   try {
//分发器用executed方法将Call对象添加进同步运行队列
     client.dispatcher().executed(this); 
//结果是从拦截器链方法中获取的
     Response result = getResponseWithInterceptorChain();  
     ......
   } finally {
//finish方法里将Call对象从Calls队列中移出
     client.dispatcher().finished(this);  
   }
}

//下面进到client.dispatcher().executed(this)的excuted方法里面

 synchronized void executed(RealCall call) {
//runningSyncCalls是正在运行的同步队列
    runningSyncCalls.add(call);  
 }

总结:excute()同步申请方法,分发器将Call对象添加到同步运行队列,当然其中会有一个promoteAndExecute()方法来计算队列中请求数量,有限定最大申请数量,上面代码没列出来,最后结果是经过一系列拦截器的方法后的数据。

5.enqueue异步请求方法,源码:

@Override public void enqueue(Callback responseCallback) {
//判断是否请求过这个Call对象
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
//异常检测
    captureCallStackTrace();
//事件监听
    eventListener.callStart(this);
//调用分发器的enqueue方法,分发器在client创建时新建的。
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

可以看到enqueue方法里面又调用了分发器的enqueue方法,在enqueue方法里新建了一个AsyncCall对象,

AsyncCall对象传入我们上一层传入enqueue方法的CallBack对象。

接下来看看上面的AsyncCall类是什么东西。

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
    ...........
}

看到AsyncCall继承自NamedRunnable,再来看看NamedRunnable是什么东西

public abstract class NamedRunnable implements Runnable {
  .....
  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }
  .....
}

可以看到NamedRunnable实现了Runnable接口,里面最核心的就是在run方法里面运行了execute()方法,这个方法的具体实现在AsyncCall类里,我们来看看。

@Override protected void execute() {
      .......
      Response response = getResponseWithInterceptorChain(); 
      .....
  }

我把大部分代码都省了,最重要的就上面那句,可以看到跟同步请求一样,最后结果都是经过一系列拦截器的方法后的数据。

那么同步跟异步有什么区别呢?

异步传入enqueue方法的CallBack的对象实现了Runnable接口,让它在子线程中运行。

还有,接下来回到开头看看client.dispatcher().enqueue(new AsyncCall(responseCallback));这句,分发器类里的变量和它的enqueue方法(刚刚看的是AsyncCall类)。

public final class Dispatcher {
  //默认的最大并发请求量 
  private int maxRequests = 64;
  //单个host支持的最大并发量
  private int maxRequestsPerHost = 5;  
  .........
  //异步等待队列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  //异步运行队列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  //同步运行队列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  void enqueue(AsyncCall call) {
      synchronized (this) {
  //把Call对象添加进readyAsyncCalls异步等待队列
        readyAsyncCalls.add(call);
      }
  //计算队列内请求数量的方法,如果异步请求满足不超过64,5的条件则进行请求操作。
      promoteAndExecute();
  }

可以看到分发器把Call对象添加进readyAsyncCalls异步等待队列,而在同步请求时分发器是把Call对象直接添加到同步运行队列,promoteAndExecute()方法就是前面说到的计算队列内请求数量的方法,这个方法里面会进行判断,如果请求数量符合上面两个最大值的要求就会把请求放入队列等操作。

总结:enqueue方法传入CallBack对象,CallBack对象被封装为AsyncCall,AsyncCall内部实现了Runnable接口,分发器把AsyncCall传入了异步等待对列,最后运行promoteAndExecute()方法,如果请求数量符合条件进行异步请求,在子线程获取数据。

篇幅有点长了,关于Response response = getResponseWithInterceptorChain(); 拦截器链也是重点,在下一篇再写。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章