天天看点

OKHttp的使用和源码解析

突发奇想想自己总结一下学习OKHttp的经过,更大家分享一下。。

先介绍一下OKHttp的简单使用:

//第一步 创建OKHttpClient对象
OkHttpClient client=new OkHttpClient.Builder().build();
//第二步 创建Request对象
Request request=new Request.Builder().url("https://www.csdn.net").build();
//第三步 将Request通过newCall封装成Call对象(底层newCall是调用realCall)
Call call=client.newCall(request);

try {
    //第四步 调用Call的execute()发送同步请求
    Response response=call.execute();
    response.close();
} catch (IOException e) {
    e.printStackTrace();
}

//调用Call的enqueue()发送异步请求
call.enqueue(new Callback(){
    @Override
    public void onFailure(Call call, IOException e) {
        System.out.println("连接失败");
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        System.out.println(response.body());
    }
});      

OkHttpClient是采用Build模式,点击OkHttpClient.Builder()进入源码后会发现里面为很多值进行赋值,里面两个比较重要的也是后面我们要讲到的Dispatcher()和ConnectionPool(),它们分别是OKHttp的分发器类和连接池。

我们下面先来讲解一下OKHttp的同步请求:

点击源码我们可以看到:(以下不是全部源码,只是提取关键部分)

public Response execute() throws IOException {
    try {
        this.client.dispatcher().executed(this);
        Response e = this.getResponseWithInterceptorChain();
    } catch (IOException var7) {
    } finally {
        this.client.dispatcher().finished(this);
    }
}      

      我们可以很清晰的看到execute()调用了dispatcher().executed(this),这里调用了Dispatcher分发器,在同步请求中Dispatcher分发器的作用并不是很大,它仅仅把当前的call放入到同步请求队列中去。

        关键在于Response e = this.getResponseWithInterceptorChain();这个为了构建OKHttp拦截器的链,OKHttp拦截器链会依次执行拦截器链中的每一个不同作用的拦截器来获取服务器的返回,从而获取得到Response。(讲完异步请求后我们在分析)

        我们先看一下异步请求的源码:(删减)

public void enqueue(Callback responseCallback) {
    this.client.dispatcher().enqueue(new RealCall.AsyncCall(responseCallback));
}      

        可以看到它是直接调用了dispatcher().enqueue()方法,并且传入了一个AsyncCall对象,我们进去看一下AsyncCall的源码:

通过源码我们知道了AsyncCall是RealCall的一个内部类,它是继承自NamedRunnable,而NamedRunnable是一个Runnable;

      通过源码我们可以找到AsyncCall执行的方法:

protected void execute() {
    boolean signalledCallback = false;

    try {
        Response e = RealCall.this.getResponseWithInterceptorChain();
        if(RealCall.this.retryAndFollowUpInterceptor.isCanceled()) {
            signalledCallback = true;
            this.responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
            signalledCallback = true;
            this.responseCallback.onResponse(RealCall.this, e);
        }
    } catch (IOException var6) {
        if(signalledCallback) {
            Platform.get().log(4, "Callback failure for " + RealCall.this.toLoggableString(), var6);
        } else {
            RealCall.this.eventListener.callFailed(RealCall.this, var6);
            this.responseCallback.onFailure(RealCall.this, var6);
        }
    } finally {
        RealCall.this.client.dispatcher().finished(this);
    }

}      

        可以发现也是通过RealCall.this.getResponseWithInterceptorChain();拦截器链的方式获得response。

        我们接下来看一下Dispatcher类的enqueue()方法,再这之前要先提一下3个重要的队列,分别是

        Deque<AsyncCall> readyAsyncCalls = new ArrayDeque();            //异步的就绪队列

        Deque<AsyncCall> runningAsyncCalls = new ArrayDeque();        //异步的执行队列

        Deque<RealCall> runningSyncCalls = new ArrayDeque();            //同步的请求队列

   在enqueue()方法中,我们会先判断runningAsyncCalls和runningCallsForHost的数量是否超出最大值,最大值分别是64和5,如果超出的话将AsyncCall加到readyAsyncCalls队列中来,否则就加入到runningAsyncCalls队列中并且执行。源码如下:(runningAsyncCalls 和 runningSyncCalls 数量包含一些已经取消但没执行完成的请求。)

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

}      

         执行是调用this.executorService().execute(call);executorService()是创建一个无上限的线程池,但是因为限制了runningAsyncCalls的最大个数,所以线程数不会超过64个。

public synchronized ExecutorService executorService() {
    if(this.executorService == null) {
        this.executorService = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
    }

    return this.executorService;
}      

            接下来看一下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) {
            this.promoteCalls();
        }

        runningCallsCount = this.runningCallsCount();
        idleCallback = this.idleCallback;
    }

    if(runningCallsCount == 0 && idleCallback != null) {
        idleCallback.run();
    }

}      

        promoteCalls为ture代表是异步请求的,为false代表是同步请求的。可以看出在同步请求或者异步请求队列中移除相应的call,并且更新队列的数量,我们主要来看一下promoteCalls()这个方法里面的实现,

private void promoteCalls() {
    if(this.runningAsyncCalls.size() < this.maxRequests) {
        if(!this.readyAsyncCalls.isEmpty()) {
            Iterator i = this.readyAsyncCalls.iterator();

            do {
                if(!i.hasNext()) {
                    return;
                }

                AsyncCall call = (AsyncCall)i.next();
                if(this.runningCallsForHost(call) < this.maxRequestsPerHost) {
                    i.remove();
                    this.runningAsyncCalls.add(call);
                    this.executorService().execute(call);
                }
            } while(this.runningAsyncCalls.size() < this.maxRequests);

        }
    }
}      

        循环readyAsyncCalls,判断当前运行的队列是否超出最大值,然后依次移出readyAsyncCalls加入到runningAsyncCalls队列中区,并且执行executorService().execute(call)方法。

        下面我们来分析一下getResponseWithInterceptorChain()的源码:

Response getResponseWithInterceptorChain() throws IOException {
    ArrayList interceptors = new ArrayList();
    interceptors.addAll(this.client.interceptors());
    interceptors.add(this.retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(this.client.cookieJar()));
    interceptors.add(new CacheInterceptor(this.client.internalCache()));
    interceptors.add(new ConnectInterceptor(this.client));
    if(!this.forWebSocket) {
        interceptors.addAll(this.client.networkInterceptors());
    }

    interceptors.add(new CallServerInterceptor(this.forWebSocket));
    RealInterceptorChain chain = new RealInterceptorChain(interceptors, (StreamAllocation)null, (HttpCodec)null, (RealConnection)null, 0, this.originalRequest, this, this.eventListener, this.client.connectTimeoutMillis(), this.client.readTimeoutMillis(), this.client.writeTimeoutMillis());
    return chain.proceed(this.originalRequest);
}      

    创建了一系列拦截器,并将拦截器放到拦截器的集合中去,创建一个RealInterceptorChain 拦截器链,通过执行拦截器链的proceed方法让拦截器形成链式结构。

拦截器一共分为:

1、applicationInterceptor

2、networkInterceptor

3、OKHttp中自带的5个Interceptor

我们重点分析一下OKHttp中自带的5个Interceptor:包括

retryAndFollowUpInterceptor  重试重定向拦截器

BridgeInterceptor                    桥接拦截器

CacheInterceptor                    缓存拦截器

ConnectInterceptor                连接拦截器

CallServerInterceptor              请求服务器拦截器

1、retryAndFollowUpInterceptor  主要是创建了StreamAllocation对象,该对象是建立Http请求所需要的网络组件,其作用是用来分配stream,调用RealInterceptorChain.proceed()进行网络请求,根据异常结果或者响应结果判断是否进行重新请求,调用下一个拦截器,对Response进行处理,返回给上一个拦截器。接下来看一下proceed()源码:(只有关键部分)

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
   
            RealInterceptorChain next = new RealInterceptorChain(this.interceptors, streamAllocation, httpCodec, connection, this.index + 1, request, this.call, this.eventListener, this.connectTimeout, this.readTimeout, this.writeTimeout);
            Interceptor interceptor = (Interceptor)this.interceptors.get(this.index);
            Response response = interceptor.intercept(next);
       
}      

可以看到proceed中又创建了一个RealInterceptorChain,并且它把index的值设置为this.index + 1,这就是为什么我们能把拦截器串成拦截器链。然后把response返回给上一个拦截器。

2、BridgeInterceptor起到的是设置请求头部的作用(包括设置内容长度、编码方式....),将网络请求回来的响应Response转化为用户可用的Response。

3、CacheInterceptor里面有Cachestrategy(缓存策略),内部维护了一个networkRequest和cacheResponse;有兴趣可以去了解一下Cache类,它只缓存了GET方法的响应,内部是采用了DiskLrucache。这里就不详解了,,

4、ConnectInterceptor:我们先看一下它的源码:

public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain)chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    HttpCodec httpCodec = streamAllocation.newStream(this.client, chain, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
}      

        streamAllocation是由retryAndFollowUpInterceptor传递过来的,下面它转换成了HttpCodec,HttpCodec的作用是编码Request和解码Response;streamAllocation.connection()得到的RealConnection对象是用于网络IO的,最后将RealConnection和httpCodec传递给后面的拦截器。

5、CallServerInterceptor这是发起真正的网络请求以及接受到服务器给我们返回的响应。

好了,简单的解析就讲到这里了。。这是第一次写文章总结,如果有写得不好的或者有错的欢迎大家给我指正……

继续阅读