美文网首页
OkHttp3 之旅

OkHttp3 之旅

作者: gdutkyle | 来源:发表于2018-03-20 01:09 被阅读70次

    一 为什么推荐使用Okhttp3?

    首先,我并不觉得OkHttp是一个网络框架。okhttp对标的,应该是HttpClient或者HttpURLConnection,okhttp应该是一种新的网络请求方法,而网络框架,应该是基于上面几个网络访问方式进行封装的。像volley(基于httpClient和httpURLConnection)或者retrofit2(基于OkHttp3)。
    好吧,扯远了~~
    我们为什么要使用OkHttp3作为我们新的网络请求方式?我认为有以下几点:

    1 OkHttp支持SPDY、http2.0和https

    2 OkHttp3内置ConnectionPool连接池,可以实现多路复用(在spdy不可用的情况下使用)

    3 利用GZip压缩内容

    4 具备超时重连机制,调用方不用自己去进行自定义连接动作

    5 具备缓存机制,可以避免重复的网络请求

    6 使用简单,封装的很好,方便调用(这个吧,不知道算不算,哈哈哈)

    二 OKHttp3.0集成

    集成方式还是比较简单的,我们只需要在我们的app的build.gradle中添加依赖

    compile 'com.squareup.okhttp3:okhttp:3.10.0'
    

    同步调用:

    private void sendRequestSync() throws IOException {
        OkHttpClient mHttpClient=new OkHttpClient();
        Request request= new Request.Builder().url(ENDPOINT).build();
        mHttpClient.newCall(request).execute();
    }
    

    异步调用:

     private void sendRequestAsyn(){
        OkHttpClient mHttpClient=new OkHttpClient();
        Request request= new Request.Builder().url(ENDPOINT).build();
        mHttpClient.newCall(request).enqueue(new Callback() {
            @Override 
            public void onFailure(Call call, IOException e) {
            }
    
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json=response.body().string();
            }
        });
    }
    

    三 OkHttp3请求流程分析

    由于同步执行的代码比较简单直观,我们这里用异步请求的方式进行分析。首先我们把整个网络请求分为4个部分

    3.1 OkHttpClient的初始化

     OkHttpClient mHttpClient=new OkHttpClient();   
    

    OKHttpClient其实是call的一个工厂类,OkHttpClient是用来发起http请求,并拿到http的response的处理类。因为每一个HttpClient都维护有自己的连接池和线程池,所以不建议在每一次请求的时候都去初始化一个OkHttpcliet,而是在我们的项目中,只维护一个单例类就可以。
    我们有两种方法去初始化一个OkHttpClient:

    方法一:直接new OkHttpClient(),创建一个默认的OkHttpClient;

    方法二:通过builder去创建:

    public final OkHttpClient client = new OkHttpClient.Builder()
    
    .addInterceptor(new HttpLoggingInterceptor())
    
    .cache(new Cache(cacheDir, cacheSize))
    
    .build();  
    

    3.2 异步方法执行

    mHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
    
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json=response.body().string();
            }
        });
    

    在这里,我们首先去查看newcall(request)方法

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

    所以,我们知道,最终的enqueue方法是在realCall类中调用的。我们前去查看源码

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

    从这段代码的最后一个client.dispatcher(),好了到这里,我们就需要去看看Dispatcher类了。

    3.3:Dispatcher类

    Dispatcher是OkHttp的异步请求策略类,在这里,OkHttp3定义了多个重要的参数,分别是

    public final class Dispatcher {
        //最大的请求数量
        private int maxRequests = 64;
        
        //每一个host的最大请求数
        private int maxRequestsPerHost = 5;
    
        private @Nullable Runnable idleCallback; 
        private @Nullable ExecutorService executorService;
    
        //当前准备执行的队列
        private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
    
        //当前正在执行的队列,包括那些尚未结束的请求
        private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
    
        //同步执行的队列,包括还没有结束的请求
        private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
    
        public Dispatcher(ExecutorService executorService) {
            this.executorService = executorService;
         }
    
        public Dispatcher() {
         }
    }
    

    dispatch中查看enqueue的处理方式

    synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
         runningAsyncCalls.add(call);
         executorService().execute(call);
        } else {
         readyAsyncCalls.add(call);
       }
     }
    

    也就是,当前的运行队列小于最大值maxRequest并且当前运行的每个host的请求小于host最大请求数的时候,我们就把当前的call加入到执行队列中,否则就加入到等待队列中,等待okHttp执行。那么,我们是在哪里执行这些队列中的请求的呢?答案是RealCall

    3.4:RealCall

    RealCall里面的内部类AsyncCall封装了异步执行下的execute()方法。话不多少,我们直接上源码

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

    这段代码,我们只关注两个核心的方法,一个是getResponseWithInterceptorChain(),另一个是client.dispatcher().finished(this);

    3.4.1: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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    
    return chain.proceed(originalRequest);
    }
    

    我们从execute()方法可以知道,我们真正发出网络请求,获取response方法就是在这个方法中。我们一步一步分析:

    第一:多拦截器的构造。

    getResponseWithInterceptorChain()方法首先定义了一个拦截器list,从代码中我们可以看到,添加的顺序分别为:

    1 移动端自定义的拦截器:client.interceptors()(为什么自定义的拦截器要第一个添加,后面有介绍)

    2 retryAndFollowUpInterceptor 失败重试的拦截器

    3 BridgeInterceptor:官方解释,这是一个连接我们应用和网络的桥梁。首先通过用户请求创建一个网咯请求,接着把这个请求发出处理,最后把网络返回的值转换成用户所需要的值

    4 CacheInterceptor:缓存拦截器

    5 ConnectInterceptor:网络请求拦截器。用来跟服务端进行连接

    6 CallServerInterceptor:这个是我们网络请求的链式调用的最后一个拦截器,用来将数据丢到网络中进行处理

    第二:RealInterceptorChain 初始化,链式调用正式开始

    RealInterceptorChain类我们主要看process()方法。

    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
    
    calls++;
    
    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }
    
    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }
    
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    
    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }
    
    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }
    
    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }
    
    return response;
    }  
    

    前面的异常处理逻辑我们一律不看。我们主要看这一段

     // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);  
    

    我们在前RealCall中AsynCall的execute()方法中,传入RealInterceptorChain初始化的时候,index是0的,也就是说,上面的interceptors.get(index);其实等于interceptors.get(0)=client.interceptors(),也即是我们移动端自定义的拦截器。最后通过

    Response response = interceptor.intercept(next);
    

    我们可以看到,最后回到的,就是各个自定义拦截器中的intercept()方法中(各位如果不理解,可以去看看官方demo里面HttpLoggingInterceptor.java的实现)。

    if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }  
    

    这段代码就是为了回调前面我们调用enqueue的时候的listener,返回当前的网络请求是成功的,还是失败的。这个简单,暂且不做分析。

    3.4.3:client.dispatcher().finished(this);

    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!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }
    
    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
     }
    }
    

    我们可以看到,无论是同步还是异步执行网络请求,最终都会调用到这个finish方法中。这个就是OkHttp维护当前连接池的方法,每当一个请求结束后,都会把当前的call从当前的运行队列中移除。然后再执行promoteCalls(),把等待运行队列中的下一个请求放入运行队列中。

    四 总结

    自此,OkHttp3.0的执行流程分析到此结束。但是OkHttp的内容远不止这么复杂,接下来我会在下一篇文章,对OkHttp的其他技术细节结合源码进行详细分析。

    相关文章

      网友评论

          本文标题:OkHttp3 之旅

          本文链接:https://www.haomeiwen.com/subject/dbomqftx.html