OkHttp源码分析

OkHttp是Square公司开发的用于网络请求的第三方框架,这几年非常流行,也是很多面试会问到的东西,今天我们来分析一下部分实现。

一、使用

一般使用方式是新建一个OkHttpClient对象,通过Builder来构建一个Request对象,最后通过OkHttpClient的newCall方法来实现构建Call的实现类,最后通过enqueue方法来进行请求:

		OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "fail");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, "response : " + response.toString());
            }
        });

在Callback里面进行回调,如果请求成功并且没有异常,则在onResponse里面输出response的主体,反之调用onFailure方法。

二、流程分析

2.1 执行过程

OkHttpClient只是new了一下,这里先不分析,Request类我们可以看到构造方法是不可见的,只能通过内部的Builder方式来构建实例,通过建造者模式传入url等参数。Call实例的构建是通过OkHttpClient来进行:

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

可以看到是直接返回了一个RealCall,Call是一个借口,RealCall是他的实现,从字面意义我们也可以看出来这个类是“真正的”Call。接着enqueue方法也是由Realcall来执行:

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

先进行是否执行过的判断,如果已经执行过enqueue或者execute方法,executed被标记为true,再次调用这两个方法就会抛出异常。captureCallStackTrace是添加相应的信息记录。然后是通过OkHttpClient的Dispatcher.enqueue,Dispatcher是通过OkHttpClient的Builder来创建的,字面意思是一个调度器。Dispatcher的文档是:

/**
 * Policy on when async requests are executed.
 * //异步请求执行策略,每个Dispatcher对应一个线程池
 * 

Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your * own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number * of calls concurrently. */

是通过线程池来执行具体的Call,从这里我们也可以看出传进来的Call应该继承了Runnable接口:

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    //条件判断,正在请求的个数小于最大限制,默认最大限制64,也就是最大线程数量限制
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
    //大于限制,加到另外一个队列中先做存储
      readyAsyncCalls.add(call);
    }
  }

通过线程池及一些策略来执行Call接口,这里的Call指的是AsyncCall:

final class AsyncCall extends NamedRunnable {//最终继承自Runnable
    private final Callback responseCallback;

    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();
        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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

Callback通过构造函数传入,它的execute方法也是线程池执行到会调用的方法,最重要的也就是Response的获取,也就是这一句:

Response response = getResponseWithInterceptorChain();

后面都是对于Response的解析和callback的回调,以及一些善后工作。

2.2责任链以及网络请求

getResponseWithInterceptorChain方法如下:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

创建了一堆拦截器来执行各种策略,先是client我们自己添加的拦截器(默认没有),然后是retryAndFollowUpInterceptor,这个是RealCall的构造函数中新建的,等等,最后创建了一个RealInterceptorChain对象并将之前添加的拦截器和request传进去,然后调用proceed方法:

@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

其实调用的是另一个重载的方法:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;//保证proceed方法只被调用一次
    
    //省去部分代码
    
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

	//省去部分代码

    return response;
  }

这里的streamAllocation,httpCodec和Connection在构造函数里面有一次初始化,但是我们发现上面构造的时候传入的都是null,只有一个index作为初始下标,但是RealInterceptorChain还分别有三个方法来修改这三个成员:connection(),streamAllocation(),httpStream(),但是找了一圈也没有找到这些方法的调用地方,然后发现是在不同的Interceptor中会调用proceed方法,而在这个方法里面,RealInterceptorChain会被重新初始化,也就是每个interceptor对应一个RealInterceptorChain。

这里使用了责任链模式,在proceed的时候会重新构建RealInterceptorChain,然后每次interceptor获取之前保存的下一个,最后调用intercept方法,而在每个interceptor的intercept方法中,又会重新调用proceed方法,但是每次传入的参数不同。在RetryAndFollowUpInterceptor中会传入StreamAllocation,而在ConnectInterceptor中会传入httpCodec和connection,其他interceptor只是调用chain.proceed(request),也就是只传入一个request,这里难以理解的一点是,每个interceptor都是在intercept方法中给下一个interceptor准备RealInterceptorChain,ConnectInterceptor就是给CallServerInterceptor准备这些参数,而CallServerInterceptor也是关键的向服务器进行请求的操作:

/** This is the last interceptor in the chain. It makes a network call to the server. */
public final class CallServerInterceptor implements Interceptor {

大体流程到这里分析完毕了 但是每个拦截器还有很多细节,比如缓存方式等,具体的向服务器请求的细节这些。希望后面可以有时间再深入了解

你可能感兴趣的:(android)