OKHttp源码分析(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011394071/article/details/52903860

OKHttp源码分析(一)

本文章基于OkHttp3.4.1的源码
  • 首先来看okhttp的使用,使用有两种方法,即同步请求和异步回调,先来看异步回调的使用方式

    //创建Okhttp客户端对象
    OkHttpClient okHttpClient = new OkHttpClient();//[1]
    //创建请求对象
    Request request = new Request.Builder()//[2]
            .get()
            .url(url)
            .build();
    //创建回调对象
    Call call= okHttpClient.newCall(request);//[3]
    //发起请求
    call.enqueue(new Callback() {//[4]
        @Override
        public void onFailure(Call call, IOException e) {
            Message msg = Message.obtain();
            msg.what = FAIL;
            msg.obj = e.getMessage();
            mHandler.sendMessage(msg);
            Log.e("result", "onResponse: 错误返回" );
        }
    
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            Message msg = Message.obtain();
            msg.what = SUCCESS;
            msg.obj = response.body().string();
            mHandler.sendMessage(msg);
            Log.e("result", "onResponse: 成功返回" );
        }
    });
    }
    
  • 由上面,我们可以将发起一个请求分成四个步骤,

    • 创建okhttp客户端对象
    • 创建请求对象
    • 创建回调对象
    • 发起请求

[1]

  • 好下面来一步一步看,先看上面代码中标注[1]的语句,创建客户端对象做了哪些事情

    OkHttpClient okHttpClient = new OkHttpClient();
    
  • 实际就是调用构造方法

    public OkHttpClient() {
        this(new Builder());//构造方法中创建了Builder对象
      }
    
  • 那就来看看Builder构造又干了什么事

    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
    }
    
  • 有点吓人,做了一大堆初始化的工作,暂时不关心具体干了啥事,知道他就是创建了一个Builder对象并做很多变量初始化就可以。

[2]

  • 我们接着看[2]

    //创建请求对象
    Request request = new Request.Builder()//[2]
            .get()
            .url(url)
            .build();
    
  • 来看Request.Builder()

    public static class Builder {
        private HttpUrl url;
        private String method;
        private Headers.Builder headers;
        private RequestBody body;
        private Object tag;
    
        public Builder() {
          this.method = "GET";
          this.headers = new Headers.Builder();//这里又是Headers内部类的Builder
        }
    
  • 注意,这个Builder于之前okhttpclient的那个Builder是不一样的,之前那个是OKHttpClient的内部类,同样,这里的Builder是Request的内部类,这两个之间没有关系,只是在内部为外部类服务。

  • 实际上new Request.Builder()这个语句就是创建了一个Builder对象
  • 接着通过这个Builder对象调用get()方法

    public Builder get() {
          return method("GET", null);
        }
    
    public Builder method(String method, RequestBody body) {
      //这里删除了一些代码,这些代码主要是判断传进来的method和body的一些逻辑,先不关注,
    //这个方法最后做一下初始化成员变量,就返回调用者本身
      this.method = method;
      this.body = body;
      return this;
    }
    
  • 所以调用get方法后还是得到Builder对象,接着继续调用url()方法

    public Builder url(String url) { if (url == null) throw new NullPointerException("url == null");

      // Silently replace websocket URLs with HTTP URLs.
      if (url.regionMatches(true, 0, "ws:", 0, 3)) {
        url = "http:" + url.substring(3);
      } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
        url = "https:" + url.substring(4);
      }
    HttpUrl parsed = HttpUrl.parse(url);
          if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
          return url(parsed);
        }
    
    //对应的url(pased)方法
         public Builder url(HttpUrl url) {
      if (url == null) throw new NullPointerException("url == null");
      this.url = url;
      return this;
    }
    
  • 在url中都是判断url是否合法,不合法就抛出异常,合法就返回调用者本身,就是Builder对象
  • 最后调用build()方法

    public Request build() {
          if (url == null) throw new IllegalStateException("url == null");
          return new Request(this);
        }
    
    //调用Request构造方法做一些初始化
    private Request(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers.build();
        this.body = builder.body;
        this.tag = builder.tag != null ? builder.tag : this;
      }
    

[3]

  • 看到这里,创建请求对象完成,发现上面基本上都是做一些初始化的工作,我们接着看[3]

    //创建回调对象
    Call call= okHttpClient.newCall(request);//[3]
    
  • 这个newCall是Call的内部接口方法的实现,有必要看一下OKHttpClient的类声明

    public class OkHttpClient implements Cloneable, Call.Factory {
    //这样就能明白了
    
    @Override public Call newCall(Request request) {
        return new RealCall(this, request);
      }
    
  • 里面有调用了RealCall构造方法,只是做了一些初始化,为后面做准备

     protected RealCall(OkHttpClient client, Request originalRequest) {
        this.client = client;
        this.originalRequest = originalRequest;
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
      }
    
  • 看RealCall类声明

    final class RealCall implements Call {
    
    //这个类实现了Call接口,所以实现了Call中的所有方法,
    
  • 我们看到最后返回的其实就是Call的一个子类对象,所以下一步可以直接用这个返回的对象调用Call里声明的方法

[4]

  • 好了,看关键的步骤来了,

     call.enqueue(new Callback() {//[4]
    
  • 因为返回的这个call其实就是一个RealCall对象,所以调用的这个enqueue方法应该在这个里去看他是怎么实现的,后面我们会发现okhttp中,在不同类中有一些同名的方法或类,如果没有看好实现的关系,很容易弄混。

    @Override public void enqueue(Callback responseCallback) {
        synchronized (this) {
        //这里判断是否已经被执行过,由此可见在请求过程enqueue()方法不能被多次执行
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
      }
    
  • 来看后面的语句

     client.dispatcher()
    //client就是步骤[3]中做初始化时传进来的,其实就是第一步创建的okhttpclient
    //所以我们要回到OKHttpClient类中去看这个方法怎么实现的
    
  • 回到OKHttpClient类

    public Dispatcher dispatcher() {
        return dispatcher;
      }
    
    //其实就是在创建客户端对象的时候被初始化了
    public Builder() {
        dispatcher = new Dispatcher();//看构造方法没做别的事情,只是提供一个对象方便调用其内部资源
    
  • 接着看client.dispatcher().enqueue(new AsyncCall(responseCallback))后半部分

    enqueue(new AsyncCall(responseCallback))
    //看这里的enqueue应该去Dispatcher类中去看了,因为是他的对象调用
    
  • 好,来到Dispatcher类

    synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    
  • 有得一个个拆分看,否则很难看懂,runningAsyncCalls.size()这个什么东西

    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    
     public ArrayDeque() {//这里就是初始化了一个双端队列,初始容量为16
            elements = new Object[16];
        }
    

runningAsyncCalls.size() < maxRequests 这句话就是判断当前的队列是否小于最大的请求数量,最大允许的请求数量为64

runningCallsForHost(call) < maxRequestsPerHost),正在执行的于call对象访问同一个服务器主机的请求数量是否小于最大允许的值,最大允许的值为5

  • 只有以上两个条件同时满足才允许添加到队列中并立即执行,否则先添加到队列中
  • 好接下来看是怎么执行这个请求的任务的

    executorService().execute(call);
    
  • executorService()这个方法实际上是创建一个线程池来执行提交的网络请求,重点看后面的execute()

  • 我们知道线程池执行时,是开启线程来执行提交的任务,所以我们要弄明白提交的这个任务是什么,毫无疑问就是传进来的call,但是,想象,我们最开始执行call.execute(new Callback())传进来的是一个普通的接口子类对象,这个子类对象可不是什么任务,只是作为回调,接收请求返回的结果,那中途肯定是被转换为可执行的对象了

  • 回到client.dispatcher().enqueue(new AsyncCall(responseCallback)),发现我们最开始传进来的callback对象被封装了

  • 跟踪一下AsyncCall,发现AsyncCall就是RealCall的一个内部类而已

    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;
    
        private AsyncCall(Callback responseCallback) {
        //这里只是把callback对象赋值给成员变量
          super("OkHttp %s", redactedUrl().toString());
          this.responseCallback = responseCallback;
        }
    
  • 再看NamedRunnable

    //这里可以看到在上面的那一步,把enqueue方法的参数变成了一个可执行的对象
    public abstract class NamedRunnable implements Runnable {
    
  • 要看任务执行的实现,就要看任务的run方法是怎么实现的,发现在AsyncCall中没有重写run方法,到父类看看

    @Override public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
          execute();//这是个抽象类,看AsyncCall子类怎么实现
        } finally {
          Thread.currentThread().setName(oldName);
        }
      }
    
  • 看看子类中的实现

    @Override protected void execute() {
          boolean signalledCallback = false;
          try {
            Response response = getResponseWithInterceptorChain();
            if (retryAndFollowUpInterceptor.isCanceled()) {
              signalledCallback = true;
            //注意看,这里就看到了我们最初传进去的callbacke对象回调失败方法
              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);
          }
        }
      }
    
  • 到这里,你应该明白了我们传进去的callback是什么时候被回调的

  • 至于网络请求是怎么实现的,关键在这句

    Response response = getResponseWithInterceptorChain();
    
  • 这里先暂告一段,后面有空再继续分解getResponseWithInterceptorChain()

  • 下面来小结一下我们上面执行的过程中主要涉及到的一些类及他们的关系,当然这只是上面涉及到的一些类,还有一些还没涉及到的没有画上去

猜你喜欢

转载自blog.csdn.net/u011394071/article/details/52903860