美文网首页
来一波OkHttp3

来一波OkHttp3

作者: MxsQ | 来源:发表于2019-02-05 09:50 被阅读0次

    前言

    目前来看,大多数Andoird项目里,如果没有自主的网络库,大多数会选择使用Retroift作为媒介与网络交互。而Retroift的内核实际为OkHttp3,Retroift使使用OkHttp3来达到目的更简单。可以这样说,OkHttp3提供了足够强大的能力来支撑与网络交互,优势如:

    • 共享Socket
    • 连接池
    • 拦截链,如重试、缓存等
    • 支持gzip压缩

    在掌握OkHttp3附加的功能前,需要先了解OkHttp3是如何运作的,这也是这篇文章的目的

    案例

    添加依赖

    implementation("com.squareup.okhttp3:okhttp:3.12.1")
    

    案例代码

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            String url = "https://wwww.baidu.com";
    
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .build();
    
            final Request request = new Request.Builder()
                    .url(url)
                    .get()
                    .build();
    
            Call call = okHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    Log.d(TAG, "onFailure: ");
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Log.d(TAG, "onResponse: " + response.body().string());
                }
            });
        }
    

    这里仅用最简单的请求作为引子。

    如无意外,上面案例收到了成功的回调。

    正文

    OkHttpClient

    在一开始,首先需要构建OkHttpClient进行配置工作,用以发送Request和接受Response,如下

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .build();
    

    上面完成的是最基本的OkHttpClient构造,在实际的使用中,还会通过Builder进行更多的配置,如

    • callTimeout() // 请求超时
    • connectTimeout() // 连接超时
    • readTimeout() // 读取超时
    • dns() // 设置DSN
    • addInterceptor() // 设置Application 拦截器
    • addNetworkInterceptor() // 设置网络拦截器
    • ......
      案例中暂使用基本配置即可,需要注意的是,OkHttpClient使用单例可以更好地工作,因其拥有独立的连接池和线程池,并且会进行复用。

    请求

    Request
    Request 同样通过 Builder 的方式进行构建,默认情况下,在未指定Method时,为GET, 如需进行POST , PATCH 等,则需要提供RequestBody。

    在设置url时,url会被封装为HttpUrl,此类在存储url信息的同时,也负责网络地址的编码和解码工作,暂且简单了解即可。

    Call
    请求通过Call进行发送,Call包含了应有的信息。

    // 位于OkHttpClient
     @Override public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
      }
      
    // 位于RealCall
        static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        // 实例化RealCall并将其推送给EventListener
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        // 设置EventListener
        call.eventListener = client.eventListenerFactory().create(call);
        return call;
      }
    

    实际上,拿到的Call为RealCall。需要稍稍留意一下EventListener。EventListener如果需要也是通过OkHttpClient.Builder进行添加,其主要职责在于,能监听所有Call所处阶段的所有状态,如开始、链接、请求等,包括start状态和end状态。

    RealCall构造。

      private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        // OkHttpClient
        this.client = client;
        // Request
        this.originalRequest = originalRequest;
        this.forWebSocket = forWebSocket;
        // retry 拦截器,需要时进行重连
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
        // 后台线程处理timeout的策略
        this.timeout = new AsyncTimeout() {
          @Override protected void timedOut() {
            cancel();
          }
        };
        this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
      }
    

    获取Response

    call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    Log.d(TAG, "onFailure: ");
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    Log.d(TAG, "onResponse: " + response.body().string());
                }
            });
    

    在通过call.enqueue()发送请求后,能拿到结果并进行回调,由CallBack接收。

    从上可知,Call实际为RealCall,见RealCall.enqueue()

      @Override public void enqueue(Callback responseCallback) {
        synchronized (this) {
          // 检查执行状态
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        // EventListener 监听回调
        eventListener.callStart(this);
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
      }
    

    在执行前,需要检查请求是否被重复执行,再通过EventListener监听Call所处阶段的信息,再交接给调度器。需要注意,作为Callback的responseCallback将作为AsyncCall的构造参数被持有,在请求完成处理后进行回调。因此可猜测AsyncCall作为Runnable执行的。

    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;
    
        AsyncCall(Callback responseCallback) {
          super("OkHttp %s", redactedUrl());
          this.responseCallback = responseCallback;
        }
    }
    
    public abstract class NamedRunnable implements Runnable {
     
      @Override public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
          // 让子类执行自己的business
          execute();
        } finally {
          Thread.currentThread().setName(oldName);
        }
      }
    
      protected abstract void execute();
    }
    

    可见,当AsyncCall获得执行时机时,AsyncCall.execute()将完成任务。

    而调度过程则交由Dispatcher经过

    • -> Dispatcher.enqueue()
    • -> Dispatcher.promoteAndExecute()
      后由线程池进行处理,期间此Runnable会被加入各记录状态的AsyncCall队列进行管理(有需要细看)。需要注意,如果runningAsyncCalls得到饱和即并发数达到最高,则此Runnable不会被加入队列,因此也将得不到执行。如果自己提供线程池,需要注意这一点。

    在确保种种状态正常后,AsyncCall获得了执行时机

        @Override protected void execute() {
          boolean signalledCallback = false;
          // timeout策略
          timeout.enter();
          try {
            // 通过拦截链拿到结果
            Response response = getResponseWithInterceptorChain();
            if (retryAndFollowUpInterceptor.isCanceled()) {
              signalledCallback = true;
              // Callback 失败回调
              responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
              signalledCallback = true;
              Callback 成功回调
              responseCallback.onResponse(RealCall.this, response);
            }
          } catch (IOException e) {
            e = timeoutExit(e);
            if (signalledCallback) {
              // Do not signal the callback twice!
              Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
            } else {
              // EventListener 监听
              eventListener.callFailed(RealCall.this, e);
              // 异常流,CallBack 失败回调
              responseCallback.onFailure(RealCall.this, e);
            }
          } finally {
            // 出队 
            client.dispatcher().finished(this);
          }
        }
    

    代码流程很清晰,通过拦截链难道结果,先去检查请求是否被取消或者请求异常,异常和取消走失败流程,成功走正常流程,在处理后出队列。

    到这里为止大致的运行机制已现,如图


    运行机制.png

    总结

    OkHttp的运行机制总体如下

    • 构造出请求
    • 请求被wrap成Runnable任务,由调度规划请求任务
    • 请求任务从线程池获得执行时机
    • 从拦截链获得合适的Response , 回调完成任务

    文章里仅截取了主要部分以期以简短的篇章达到了解整体运行原理的目的,若要清晰了解,打开源码进行跟踪还是必不可少。


    下一篇:拦截器原理

    相关文章

      网友评论

          本文标题:来一波OkHttp3

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