前言

现在最流行的网络框架是什么?那肯定是okhttp,它开源已经有很长一段时间了,一直都是开发者们的最爱,例如目前最流行的:Rxjava + Retrofit + okhttp,都是以okhttp作为网络层实现的基础,图片加载框架Fresco,也支持设置okhttp为网络层,为什么大家都为okhttp一路绿灯?不用怀疑,肯定是okhttp过硬的技术实力,现在我们就来了解一下okhttp的源码。

正文

先来回顾一下okhttp的基本用法:

public class MainActivity extends AppCompatActivity {

    public static final MediaType JSON = MediaType.parse("application/text; charset=utf-8");

    private OkHttpClient okHttpClient;

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.text_view);
        okHttpClient = new OkHttpClient.Builder().build();
        findViewById(R.id.get_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                get();
            }
        });
        findViewById(R.id.post_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                post();
            }
        });
    }

    private void get() {
        new Thread() {
            @Override
            public void run() {
                super.run();
                Request request = new Request.Builder()
                        .url("http://www.baidu.com")
                        .build();
                try {
                    // execute为同步方法,不会新起线程,所以这里手动创建了Thread
                    final Response response = okHttpClient.newCall(request).execute();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                textView.setText(response.body().string());
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();

    }

    private void post() {
        RequestBody body = RequestBody.create(JSON, "test");
        Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .post(body)
                .build();

        okHttpClient.newCall(request)
                // enqueue为异步方法,在线程池中运行
                .enqueue(new Callback() {
                    @Override
                    public void onFailure(@NonNull Call call, @NonNull IOException e) {

                    }

                    @Override
                    public void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException {
                        final String result = response.body().string();
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                textView.setText(result);
                            }
                        });
                    }
                });
    }
}

代码里实现了get请求和post请求,这两种请求没有差别,只是多调用了post方法,明确了请求的方式,然后通过request对象生成Call对象,执行网络请求:excute是同步方法,不会创建新的线程,所以我们需要手动创建线程,enqueue是异步方法,会运行在okhttp内部的线程池中,网络的结果就在response中。

今天的内容就是分析从创建Request到得到Response的过程。

首先我们看看Request这个对象:

public final class Request {
  final HttpUrl url; // url地址信息
  final String method;  // 方法get、post、put等等
  final Headers headers; // header信息
  final @Nullable RequestBody body; // 请求体,包括参数信息等
  final Object tag;  // 这个请求的标签,可以根据标签取消网络请求
  // 同步
  private volatile CacheControl cacheControl; // Lazily initialized.

  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;
  }
  // setter and getter
  public HttpUrl url() {
    return url;
  }
  
  // 相当于clone方法,复制当前Request的所有配置,返回新的Request
  public Builder newBuilder() {
    return new Builder(this);
  }

  /**
   * Returns the cache control directives for this response. This is never null, even if this
   * response contains no {@code Cache-Control} header.
   */
  public CacheControl cacheControl() {
    CacheControl result = cacheControl;
    return result != null ? result : (cacheControl = CacheControl.parse(headers));
  }

  public boolean isHttps() {
    return url.isHttps();
  }

  public static class Builder {
   ...

    public Builder() {
      // 默认是get方法
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

    Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      this.body = request.body;
      this.tag = request.tag;
      this.headers = request.headers.newBuilder();
    }

    public Builder url(HttpUrl url) {
      if (url == null) throw new NullPointerException("url == null");
      this.url = url;
      return this;
    }

    // setter and getter
}

Request中主要是记录的请求的一些信息,没有什么复杂的逻辑,接下来调用了

final Response response = okHttpClient.newCall(request).execute();

先看看newCall方法:

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

内部调用了RealCall.newRealCall,进入到RealCall这个类中:

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

  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;
  }

看到这里就明白了,okhttpClient.newCall就是创建了一个RealCall对象,里面有OkhttpClient和Request信息,关键还是在excute方法上:

@Override public Response execute() throws IOException {
    synchronized (this) { // 如果任务已经开始了,不会执行并抛出异常
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this); // okhttpclient的eventlistener,在builde中设置,这里不做介绍
    ...
      client.dispatcher().executed(this);    // 把RealCall保存到正在执行的任务列表中
      // 这是最关键的代码,通过Interceptors的连锁处理,最终得到Response并返回
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
   ...
  }
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors()); // 这个是我们在okhttpclient中自己设置的intceptors,因为优先级最高,所以我们可以完全自定义返回的Response
    interceptors.add(retryAndFollowUpInterceptor); // 自动重试的Interceptor,刚才已经看到了创建的代码
    interceptors.add(new BridgeInterceptor(client.cookieJar())); // 桥接的Interceptor,主要处理header中的信息
    interceptors.add(new CacheInterceptor(client.internalCache())); // 缓存Interceptor,判断是否要使用缓存
    interceptors.add(new ConnectInterceptor(client));  // 网络连接Interceptor,读取网络数据
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors()); // 这里是okhttpclient设置的networkInterceptors
    }
    interceptors.add(new CallServerInterceptor(forWebSocket)); // 读取网络数据Interceptor

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

这里把我们设置的Intercepters和内置的Interceptors组合起来,封装成RealInterceptorChain对象,这里明确了各种Interceptor的优先级,这对于如何设置Interceptor非常重要,我们分析的终点就在proceed方法中了:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError(); // 判断递归是否已经越界

    calls++;

    // 一些验证,这里省略

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout); // 创建一下Chain,并得到之前的Chain的各种配置信息
    Interceptor interceptor = interceptors.get(index); // 得到当前index的interceptor
    Response response = interceptor.intercept(next); // 当前Interceptor执行下一个chain

    // 一些验证,这里省略
    return response;
  }

这里应该是最难理解的地方了,到这里我们没有看到for循环,那么Interceptors是怎么按照顺序响应的呢?关键就在与我们如何实现Interceptor接口:

new Interceptor() {
     @Override
     public Response intercept(Chain chain) throws IOException {
          return null;
     }
}

在当前的Interceptor中,调用执行第一个Chain.proceed()方法;

第一个Chain.proceed()中又执行了第二个Interceptor;

第二个Interceptor执行第二个Chain.proceed();

而在第二个Chain.proceed()中又执行了第三个Interceptor;

....

这样就形成了递归,我们知道okhttpclient中设置的Interceptor优先级最高,如果我们直接返回null会怎么样呢?因为我们打断了递归,所以直接就返回了null,后面的Interceptor都不会再执行了,所以对于定义Interceptor要十分小心。

看完了excute,再看看enqueue方法:

@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)); // 这里是重点,看看dispatcher里做了什么
  }
synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {// 请求书是否应到最大
      runningAsyncCalls.add(call); // 放入到正在运行任务的队列中
      executorService().execute(call); // 线程池运行call,那么这个call一定实现了Runnable
    } else {
      readyAsyncCalls.add(call); // 加载到等待运行的队列中
    }
  }

看一下AynCall的代码:

final class AsyncCall extends NamedRunnable { // NamedRunnable继承Runnable,为每一个Runnable命名
    private final Callback responseCallback; // 这是在enqueue传入的callback参数

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

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain(); // 熟悉的代码,跟excute中的一样
        if (retryAndFollowUpInterceptor.isCanceled()) { // 任务是否被取消
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled")); // 被取消,回调onFailed
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response); // 返回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);   // okhttpclient设置的eventlistener
          responseCallback.onFailure(RealCall.this, e); // 回调onFailed
        }
      } finally {
        client.dispatcher().finished(this); // 从队列中结束任务
      }
    }
  }

从服务端读取数据是在CallServerInterceptor中,虽然只是简单的读写操作,但是okhttp封装的却很仔细,所以这里暂时省略,之后我们会单独把Response的读取作为很重要的一部分仔细分析。

总结

是不是觉得很简单?现在我们还只是弄清楚okhttp的主线操作,其他的功能例如缓存,线程池等等还需要研究一下具体的细节。作为研究okhttp源码的开篇,已经为我们之后的研究奠定了非常坚实的基础。

最后祝大家周末愉快!!!


Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐