从源码层面解析OkHttp的工作方式

目录

1.OkHttp简介

2.阻塞方式使用OkHttp

①:首先创建一个OkHttpClient对象,这个对象用来统领一切的功能组件。

②:通过Request类的静态方法把访问网络的各种条件封装成一个Request对象。

③:把Request对象封装为Call对象。

④:操作Call对象

⑤:操作response获取信息

2.非阻塞方式使用OkHttp

3.小结


分析源码,首先导入相应的包,OkHttpClient包和Okio包。

下载地址(当然也可以从github导入)

OkHttpClient

Okio

1.OkHttp简介

OkHttp是一个用于Android的网络框架。比起早期的HttpClient,OkHttp有更灵活的使用方式,支持http和https。许多知名的框架,例如retrofit底层也是通过封装OkHttp来实现的。

OkHttp的有两个特色。一是它请求时的效率非常高,可以重用socket连接。二是OkHttp拥有非常实用的拦截器链,底层通过巧妙的方式实现。可以说理解了OkHttp的拦截器链,也就理解了OkHttp的核心思想。


2.阻塞方式使用OkHttp

//阻塞请求方式
public static void blockRequest() throws IOException {
        String url = "http://wwww.baidu.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        //封装request
        Request request = new Request.Builder()
                .url(url)
                .get()//默认就是GET请求,可以不写
                .build();
        //封装call
        Call call = okHttpClient.newCall(request);
        //阻塞请求方式
        Response response = call.execute();
        System.out.println("run: " + response.body().string());
        System.out.println("success");
}

这是一段特别基础的代码,功能就是访问一下百度的主页,然后打印一下结果(我是直接使用Java测试,所以打印方式就选择了system.out.println)。

:首先创建一个OkHttpClient对象,这个对象用来统领一切的功能组件。

看看OkHttpClient的代码吧

public class OkHttpClient implements Cloneable, Factory, okhttp3.WebSocket.Factory {
    static final List<Protocol> DEFAULT_PROTOCOLS;
    static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS;
    //任务调度器
    final Dispatcher dispatcher;
    @Nullable
    final Proxy proxy;
    final List<Protocol> protocols;
    final List<ConnectionSpec> connectionSpecs;
    //全局拦截器拦截器链
    final List<Interceptor> interceptors;
    //network拦截器链
    final List<Interceptor> networkInterceptors;
    final okhttp3.EventListener.Factory eventListenerFactory;
    final ProxySelector proxySelector;
    final CookieJar cookieJar;
    @Nullable
    final Cache cache;
    @Nullable
    final InternalCache internalCache;
    final SocketFactory socketFactory;
    final SSLSocketFactory sslSocketFactory;
    final CertificateChainCleaner certificateChainCleaner;
    final HostnameVerifier hostnameVerifier;
    final CertificatePinner certificatePinner;
    final Authenticator proxyAuthenticator;
    final Authenticator authenticator;
    final ConnectionPool connectionPool;
    final Dns dns;
    final boolean followSslRedirects;
    final boolean followRedirects;
    final boolean retryOnConnectionFailure;
    final int callTimeout;
    final int connectTimeout;
    final int readTimeout;
    final int writeTimeout;
    final int pingInterval;

    ....
}

哇塞,好庞大的类。OkHttp整个框架的所有核心功能组件都在这里面了,连接池、各种超时时间、各种组件工厂等等都定义在这里。可以说OkHttpClient类是整个框架的核心。

这篇文章主要会运用三个加注释的属性,任务调度器Dispatcher和两个拦截器集合。

②:通过Request类的静态方法把访问网络的各种条件封装成一个Request对象。

public final class Request {
    final HttpUrl url;
    final String method;
    final Headers headers;
    @Nullable
    final RequestBody body;
    final Map<Class<?>, Object> tags;
    @Nullable
    private volatile CacheControl cacheControl;

    ....
}

可以看到Request对象更像一个JavaBean实体,它描述了一个请求的模型,功能上与JavaEE中封装Request类似。

③:把Request对象封装为Call对象。

Call是一个接口,这里用的是它的实现类RealCall。这个类的功能类似于一个终端的功能,里面封装了调用核心网络请求功能的方法。这个类把用户与核心功能接口进行了解耦,用户只需操作RealCall这个类即可以使用相应的方法进行网络请求。

来看看这个类吧。

final class RealCall implements Call {
    final OkHttpClient client;
    final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
    final AsyncTimeout timeout;
    @Nullable
    private EventListener eventListener;
    final Request originalRequest;
    final boolean forWebSocket;
    private boolean executed;

    ...
}

类中包含了指向OkHttpClient的引用,Request对象和其他的功能组件。大体可以猜得出在RealCall的方法中通过调用这些对象的核心功能方法来进行网络访问。

④:操作Call对象

继续往下看,现在重头戏要来了,先总结一下上面的知识,以免混乱。

首先创建了OkHttpClient对象,其中封装了访问网络的核心代码。接着把访问网络的各类参数封装成一个Request。然后把Request封装为一个Call对象,这个Call类有很多的功能组件,并且把用户操作和核心功能代码进行了解耦。

现在要开始操作Call对象来进行网络访问了。

阻塞方式请求通过调用Call对象中的execute方法实现的。

先看看execute方法吧

 public Response execute() throws IOException {
        synchronized(this) {
            if (this.executed) {
                throw new IllegalStateException("Already Executed");
            }

            this.executed = true;
        }

        this.captureCallStackTrace();
        this.timeout.enter();
        this.eventListener.callStart(this);

        Response var2;
        //重点代码区
        try {
            //调用OkHttpClient中的dispatcher对象的executed方法执行操作
            this.client.dispatcher().executed(this);
            //执行拦截器链
            Response result = this.getResponseWithInterceptorChain();
            if (result == null) {
                throw new IOException("Canceled");
            }

            var2 = result;
        } catch (IOException var7) {
            IOException e = this.timeoutExit(var7);
            this.eventListener.callFailed(this, e);
            throw e;
        } finally {
            //把任务出队
            this.client.dispatcher().finished(this);
        }

        return var2;
}

首先它会执行一些辅助操作,判断是否可以执行操作,设置超时时间,执行注册的监听器。

然后是两个重量级操作

A.调用OkHttpClient中的Dispatchar对象的executed方法操作Call对象,并把自己传入(this.client.dispatcher().executed(this);)

dispatcher()方法是返回OkHttpClient类中的Dispatcher任务调度器组件。

这个Dispatchar是什么来头呢?来看看它的代码

public final class Dispatcher {
    private int maxRequests = 64;//最大请求数量
    private int maxRequestsPerHost = 5;//每台主机最大的请求数量
    @Nullable
    private Runnable idleCallback;
    @Nullable
    private ExecutorService executorService;//任务执行线程池
    //准备执行的请求队列
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque();
    //正在运行的请求队列
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque();
    //正在运行的同步请求队列
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque();
    ...
}

通过这些变量不难发现,Dispatcher通过三个请求队列来管理和调度请求,在通过一个线程池去并发执行请求。

接着看它的executed()方法。

synchronized void executed(RealCall call) {
        this.runningSyncCalls.add(call);
}

就一行代码,把传入的Call对象加入请求队列,因为现在是通过阻塞请求的方法访问网络,因此加入runningSynCalls中。而且ArrayDeque并不是线程安全的,因此该方法用synchronized关键件修饰了。

之后不管任务执行如果,都要出队。

这样第一个重量级代码就完毕了。

B.执行拦截器链( Response result = this.getResponseWithInterceptorChain())

这一行代码我看了一下午。可见它的重要性,我称它为重量级代码S。

首先有一个问题,什么是拦截器。

拦截器是我们在执行相应操作之前进行的一个检查操作。就像坐飞机之前要安检,只有安检通过才能进入候机大厅。

OkHttp添加拦截器可以在创建OkHttpClient对象时直接添加。

OkHttpClient okHttpClient = new OkHttpClient.Builder()
                //通过Builder方法和addInterceptor方法组合添加拦截器
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();

                        long startTime = System.nanoTime();
                        System.out.println(String.format("Sending request %s on %s%n%s",
                                request.url(), chain.connection(), request.headers()));

                        //放行函数
                        Response response =  chain.proceed(request);

                        long endTime = System.nanoTime();
                        System.out.println( String.format("Received response for %s in %.1fms%n%s",
                                response.request().url(), (endTime - startTime) / 1e6d, response.headers()));
                        //返回response对象
                        return response;
                    }
                })
                .build();

我是直接通过匿名内部类添加拦截器,也可以直接写一个类去作为拦截器,实现Interceptor接口就OK。

public interface Interceptor {
    Response intercept(Interceptor.Chain var1) throws IOException;

    public interface Chain {
        Request request();

        Response proceed(Request var1) throws IOException;

        @Nullable
        Connection connection();

        Call call();

        int connectTimeoutMillis();

        Interceptor.Chain withConnectTimeout(int var1, TimeUnit var2);

        int readTimeoutMillis();

        Interceptor.Chain withReadTimeout(int var1, TimeUnit var2);

        int writeTimeoutMillis();

        Interceptor.Chain withWriteTimeout(int var1, TimeUnit var2);
    }
}

这个接口只有一个抽象方法interceptor(),在执行拦截器时会运行这个方法。其中还有一个Chain接口,这个接口是interceptor方法参数类型,后面会说到他。

现在来看看 getResponseWithInterceptorChain()这个函数吧。

Response getResponseWithInterceptorChain() throws IOException {
        //定义一个拦截器集合,记录所有存在的拦截器
        List<Interceptor> interceptors = new ArrayList();
        //把OkHttpClient中的全局拦截器加入集合
        interceptors.addAll(this.client.interceptors());
        //加入一些必要的功能拦截器(这些拦截器是框架本身写好的,用于实现cookies和缓存等功能)
        interceptors.add(this.retryAndFollowUpInterceptor);
        interceptors.add(new BridgeInterceptor(this.client.cookieJar()));
        interceptors.add(new CacheInterceptor(this.client.internalCache()));
        interceptors.add(new ConnectInterceptor(this.client));
        //如果该请求不是web请求就加入network拦截器
        if (!this.forWebSocket) {
            interceptors.addAll(this.client.networkInterceptors());
        }
        //加入最终执行操作对象
        interceptors.add(new CallServerInterceptor(this.forWebSocket));
        //生成链对象
        Chain 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());
        //执行链对象,把最初封装好的Request对象传入
        return chain.proceed(this.originalRequest);
    }

这个函数太重要了,重要到我几乎每一行都写了注释

就如注释中写的,定义一个拦截器集合,把所有注册的拦截器都加入,以便之后遍历这个集合执行拦截器。

这里要注意,拦截器加入的顺序必须要保持严格的顺序,因为之后的遍历是顺序遍历,错误的插入顺序会导致操作流程异常。

还有一个点,就是“最终执行操作对象”,这里先买个关子。

跟着代码走,接着通过集合生成了Chain对象,Chain是个接口,定义在Interceptor接口中,这里用他的实现类RealInterceptorChain。

看看这个类中有些什么。

public final class RealInterceptorChain implements Chain {
    //拦截器集合
    private final List<Interceptor> interceptors;
    private final StreamAllocation streamAllocation;
    private final HttpCodec httpCodec;
    private final RealConnection connection;
    //当前执行的拦截器的下标
    private final int index;
    private final Request request;
    private final Call call;
    private final EventListener eventListener;
    private final int connectTimeout;
    private final int readTimeout;
    private final int writeTimeout;
    private int calls;
    
    ...
}

又有很多组件,就目前已知的,有拦截器集合,执行拦截器的下标,Request,Call对象。还有很多第一次见到的组件(streamAllocation,HttpCodec这些),之后慢慢了解也不迟,因为在生成RealInterceptorChain都在构造函数中传入的都是空值,所以目前他们都是null。

接着调用它的proceed方法。

public Response proceed(Request request) throws IOException {
        //调用重载方法
        return this.proceed(request, this.streamAllocation, this.httpCodec, this.connection);
    }

    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
        //判断下标是否越界
        if (this.index >= this.interceptors.size()) {
            throw new AssertionError();
        } else {
            ++this.calls;
            if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
                throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must retain the same host and port");
            } else if (this.httpCodec != null && this.calls > 1) {
                throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must call proceed() exactly once");
            } else {
                //又创建一个链对象,并把当前对象的组件传入(可以理解为保持当前对象状态)
                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);
                //执行interceptor方法,传入链对象
                Response response = interceptor.intercept(next);
                if (httpCodec != null && this.index + 1 < this.interceptors.size() && next.calls != 1) {
                    throw new IllegalStateException("network interceptor " + interceptor + " must call proceed() exactly once");
                } else if (response == null) {
                    throw new NullPointerException("interceptor " + interceptor + " returned null");
                } else if (response.body() == null) {
                    throw new IllegalStateException("interceptor " + interceptor + " returned a response with no body");
                } else {
                    return response;
                }
            }
        }
    }

代码很多,但如果无视它的各种异常判断,只有三行代码(究极重要)。

首先又创建了一个拦截器链对象,并把当前链对象的状态复制到新对象中(index加一之后传入)。

接着通过index获得对于拦截器。

执行拦截器的intercept方法,传入新的链对象。

现在可以往上滑看看之前贴的Interception接口和拦截器的实现那些代码。我们Interception接口的intercept方法需要一个Chain对象,并且如果拦截器条件判断成功,就执行Chain对象的proceed放行函数,表示已经成功通过这次检查。那么接下来会发生什么事情呢?看看上面这个函数的函数名,不就是proceed吗?proceed函数中是继续新建Chain对象,根据下标获得interception对象,执行intercept方法,传入新的Chain对象,如果通过检查就调用放行函数proceed。然后继续这样的操作,一直递归执行下去,除了每次获得Interception对象不同以外都是一样的操作。

为了更好的理解,我决定画个图

就像图中所示一样proceed方法和intercept方法交替执行遍历整个拦截器集合。

这其中OkHttp框架把cookies操作,缓存操作等功能也做成了相应的拦截器加入了拦截器集合,在遍历集合的过程中也就顺便对这些方面做了处理,真是非常棒的设计思想。

现在来说说之前卖的关子。最终执行对象CallServerInterceptor,最后的一个拦截器究竟是干什么的。如果大家一路跟下来,就会发现我们目前把所有前置操作都做完了,我们使用这个框架的目的就是为了进行网络请求,那么网络请求的代码在哪呢?在最上层的execute函数,执行完拦截器链就返回response对象了。因此这最后一个拦截器就是真正的执行网络请求操作的代码,OkHttp把它做成了拦截器,放在拦截器链的末尾,如果前面的拦截都被放行,那么就执行最后的操作。

看看CallServerInterceptor类的intercept方法究竟干了什么。

public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain)chain;
        //取得网络操作核心对象
        HttpCodec httpCodec = realChain.httpStream();
        //取得流分配对象
        StreamAllocation streamAllocation = realChain.streamAllocation();
        //获取网络连接
        RealConnection connection = (RealConnection)realChain.connection();
        //获得最初封装的Request对象
        Request request = realChain.request();
        long sentRequestMillis = System.currentTimeMillis();
        realChain.eventListener().requestHeadersStart(realChain.call());
        //请求头设置
        httpCodec.writeRequestHeaders(request);
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);
        Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                //网络请求(一切的一切都是为这行代码服务的)
                httpCodec.flushRequest();
                realChain.eventListener().responseHeadersStart(realChain.call());
                //获得响应,封装为responseBuilder 对象
                responseBuilder = httpCodec.readResponseHeaders(true);
            }

            if (responseBuilder == null) {
                realChain.eventListener().requestBodyStart(realChain.call());
                long contentLength = request.body().contentLength();
                CallServerInterceptor.CountingSink requestBodyOut = new CallServerInterceptor.CountingSink(httpCodec.createRequestBody(request, contentLength));
                BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
                request.body().writeTo(bufferedRequestBody);
                bufferedRequestBody.close();
                realChain.eventListener().requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
            } else if (!connection.isMultiplexed()) {
                streamAllocation.noNewStreams();
            }
        }
        
        httpCodec.finishRequest();
        if (responseBuilder == null) {
            realChain.eventListener().responseHeadersStart(realChain.call());
            responseBuilder = httpCodec.readResponseHeaders(false);
        }
        //取得Response对象
        Response response = responseBuilder.request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
        //通过响应码判断本次网络请求的结果
        int code = response.code();
        if (code == 100) {
            responseBuilder = httpCodec.readResponseHeaders(false);
            response = responseBuilder.request(request).handshake(streamAllocation.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();
            code = response.code();
        }

        realChain.eventListener().responseHeadersEnd(realChain.call(), response);
        if (this.forWebSocket && code == 101) {
            response = response.newBuilder().body(Util.EMPTY_RESPONSE).build();
        } else {
            response = response.newBuilder().body(httpCodec.openResponseBody(response)).build();
        }

        if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) {
            streamAllocation.noNewStreams();
        }

        if ((code == 204 || code == 205) && response.body().contentLength() > 0L) {
            throw new ProtocolException("HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
        } else {
            //结果没问题,返回response对象
            return response;
        }
    }

代码有点多,简而言之就是通过HttpCodec和RealConnect这些网络访问组件读取封装到Request中的信息,然后进行网络请求,之后封装响应,通过code判断结果,结果无误,返回响应对象response。然后递归回溯,每一个拦截器都执行proceed函数下面的代码,然后把response对象返回,直到主函数。

如果大家还记得上面的代码,就会想到在第一个RealInterceptorChain对象生成时HttpCodec这些对象传入的都是空值,为什么现在就可以调用了呢?这一块是由ConnectInterceptor这个拦截器准备的,这个拦截器的目的就是为网络请求准备连接和实例化和各个组件,然后传入下一个RealInterceptorChain中,然后层层传递就到了CallServerInterceptor中了。

⑤:操作response获取信息

Response对象中封装着一个响应的所有信息,通过它的各种函数可以拿到相应的信息。

至此,我们第一个示例,阻塞请求就完美结束了。可喜可贺。在进行非阻塞请求示例解析之前先来梳理一下整个流程。

如果能理解OkHttpClient的拦截器链的处理过程,那也就理解了整个核心操作过程。

 

2.非阻塞方式使用OkHttpClient

其实在开发的过程中,非阻塞方式使用的更多,但是非阻塞方法要比阻塞方法麻烦一点,所以我们才要从阻塞方式开始,不过他们的核心思想都是一样的。

public static void nonblockRequest() {
        String url = "http://wwww.baidu.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .get()//默认就是GET请求,可以不写
                .build();
        //封装call
        Call call = okHttpClient.newCall(request);
        //非阻塞请求方式
        call.enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e) {
                System.out.println("error");
            }

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

还是一样的访问百度,还是一样创建OkHttpClient对象,封装request对象,封装Call对象。

不过这次调用enqueue方法,而且传入一个CallBack类型的参数对象,这一块很好理解。使用阻塞方式进行操作,线程会等待方法返回才继续执行,非阻塞方法就不同了,线程调用enqueue方法后直接就继续执行了。请求可能在任意时间执行完毕,CallBack方法就是在请求完成之后用来回调提醒我们的。

直接进入enqueue方法。

public void enqueue(Callback responseCallback) {
        //防止任务重复执行
        synchronized(this) {
            if (this.executed) {
                throw new IllegalStateException("Already Executed");
            }

            this.executed = true;
        }
        //和阻塞方式一样的前置操作
        this.captureCallStackTrace();
        this.eventListener.callStart(this);
        //调用OkHttpClient的Dispatcher对象得到enqueue方法
        this.client.dispatcher().enqueue(new RealCall.AsyncCall(responseCallback));
}

核心代码还是this.client.dispatcher().enqueue(new RealCall.AsyncCall(responseCallback));

这里把Call对象封装为AsyncCall类型的对象。关于AsyncCall等下再说。

先看Dispatcher的enqueue方法吧

void enqueue(AsyncCall call) {
        //加入到等待执行队列
        synchronized(this) {
            this.readyAsyncCalls.add(call);
        }
            
        this.promoteAndExecute();
}

两行代码,先把任务加入到等待队列,然后调用promoteAndExecute方法

private boolean promoteAndExecute() {
        assert !Thread.holdsLock(this);

        List<AsyncCall> executableCalls = new ArrayList();
        boolean isRunning;
        AsyncCall asyncCall;
        synchronized(this) {
            Iterator i = this.readyAsyncCalls.iterator();

            while(true) {
                if (i.hasNext()) {
                    asyncCall = (AsyncCall)i.next();
                    if (this.runningAsyncCalls.size() < this.maxRequests) {
                        if (this.runningCallsForHost(asyncCall) < this.maxRequestsPerHost) {
                            i.remove();
                            executableCalls.add(asyncCall);
                            this.runningAsyncCalls.add(asyncCall);
                        }
                        continue;
                    }
                }

                isRunning = this.runningCallsCount() > 0;
                break;
            }
        }

        int i = 0;

        for(int size = executableCalls.size(); i < size; ++i) {
            asyncCall = (AsyncCall)executableCalls.get(i);
            //执行任务
            asyncCall.executeOn(this.executorService());
        }

        return isRunning;
    }

这个方法大部分的代码都做一个任务,就是把等待队列里的任务存放到执行任务队列,还有一些阈值判断等等,并解决了线程安全问题。

关注的点应该是asynCall.executeOn(this.executorService())这个方法,就是调用之前封装之后的AsynCall的方法,并传入了线程池对象。现在来看看这个类。

final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

        void executeOn(ExecutorService executorService) {
            assert !Thread.holdsLock(RealCall.this.client.dispatcher());

            boolean success = false;

            try {
                //向线程池添加任务
                executorService.execute(this);
                success = true;
            } catch (RejectedExecutionException var8) {
                InterruptedIOException ioException = new InterruptedIOException("executor rejected");
                ioException.initCause(var8);
                RealCall.this.eventListener.callFailed(RealCall.this, ioException);
                this.responseCallback.onFailure(RealCall.this, ioException);
            } finally {
                if (!success) {
                    RealCall.this.client.dispatcher().finished(this);
                }

            }

        }

        protected void execute() {
               .......
        }
    }

先不看execute方法,只看executeOn方法,除去异常处理代码意外,只有一行代码,就是加入到线程池中运行。然后在finally代码块中把任务从执行队列中出队。

等等,线程池的execute方法不是只能传入类似Runnable的线程对象吗?

再看看AsynCall类,发现它继承自NamedRunnable,那么看看这个NamedRunnable类。

public abstract class NamedRunnable implements Runnable {
    protected final String name;

    public NamedRunnable(String format, Object... args) {
        this.name = Util.format(format, args);
    }

    public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(this.name);

        try {
            //调用抽象方法execute
            this.execute();
        } finally {
            Thread.currentThread().setName(oldName);
        }

    }

    protected abstract void execute();
}

恍然大悟,原来NamedRunnable类实现了Runnable接口,再看看run方法,调用了抽象方法execute。这种把部分方法实现交给子类的设计是不是有点眼熟,当然是大名鼎鼎的模版方法模式啦。

现在回过头看看AsynCall类怎么重写execute方法的。

protected void execute() {
            boolean signalledCallback = false;
            RealCall.this.timeout.enter();

            try {
                //这个方法也太眼熟了吧
                Response response = 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, response);
                }
            } catch (IOException var6) {
                IOException e = RealCall.this.timeoutExit(var6);
                if (signalledCallback) {
                    Platform.get().log(4, "Callback failure for " + RealCall.this.toLoggableString(), e);
                } else {
                    RealCall.this.eventListener.callFailed(RealCall.this, e);
                    this.responseCallback.onFailure(RealCall.this, e);
                }
            } finally {
                RealCall.this.client.dispatcher().finished(this);
            }

        }

getResponseWithInterceptorChain()。这个方法就不用多说了吧,几乎整篇博客都是围绕这个方法写的,递归调用拦截器链。

如果任务正常执行完毕,就调用CallBack的onResponse()方法,否则调用onFailure()方法。这两个方法都是可以重写的,可在这两个方法中写一些请求完毕的逻辑。

最后在finally块中执行一些清理工作。

现在,非阻塞示例也正是分析完毕,还是通过一个流程图说明一下吧


 

3.小结

到目前为止,OkHttp的源码分析基本就结束了,OkHttp还提供了丰富的请求方式和请求封装。例如post方式请求和上传文件或表单这样的操作,不过其底层工作原理与上述操作也是大同小异。OkHttpClient最核心的就是他的拦截器链,再搭配多个队列和线程池操作来实现非阻塞操作。

这篇博客是我写的最长的一片博客,也是我第一篇Android学习的博客。谢谢你能坚持下来看到这里,如果文中有错误的地方的还请指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值