OkHttp-android网络三方请求框架

OkHttp-android网络请求框架

基础使用方式

基础请求

不啰嗦,直接上代码
导包:
implementation 'com.squareup.okhttp3:okhttp:3.7.0' implementation 'com.squareup.okio:okio:1.12.0'

 private void requestDataByOkhttp() {
        //OkHttp是支持链式调用的,这里为了说明重点先区分开
        //1.创建OkHttpClient
        OkHttpClient client = new OkHttpClient();
        //2.创建一个request,这个request中包含很多的请求参数,比如url,body,header等
        Request request = new Request.Builder().url("https://www.baidu.com").build();
        //3.将请求封装成一个任务,获得call对象
        Call call = client.newCall(request);
        //4.使用call发起请求,这里只写异步
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });
    }

post请求

    private void postDtataByOkhttp() {
        //1.创建OkHttpClient
        OkHttpClient client = new OkHttpClient();
        //2.创建要提交的数据封装成RequestBody类型
        RequestBody body = RequestBody.create(MediaType.parse("len.hu"), "a");
        //3.将参数封装创建request将参数封装创
        Request request = new Request.Builder().url("https://www.baidu.com").post(body).build();
        //4.发起请求
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });
    }

这里是基本使用,其实post有很多的用法,上次图片,文件,流等,具体可以参考跳转至OkHttp的用法

OkHttp的表层原理

使用的方法在使用的时候查询一下就ok了,作为代码人员,查看这是怎么做到的才是关键对吧

创建部分(okHttpClient,request,call)

1.okHttpClient
    public OkHttpClient() {
    this(new Builder());
  }

  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
      isTLS = isTLS || spec.isTls();
    }

    if (builder.sslSocketFactory != null || !isTLS) {
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
      X509TrustManager trustManager = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;
  }

上图中是okHttpClient中的构造方法,了解build构建者模式跳转至build设计模式的一眼就明白了为什么可以链式调用了;在第一个构造方法中(以下简称A,第二个称为B),A中有个Builder对象,那么来看这个对象

Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      this.connectionSpecs = okHttpClient.connectionSpecs;
      this.interceptors.addAll(okHttpClient.interceptors);
      this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
      this.eventListenerFactory = okHttpClient.eventListenerFactory;
      this.proxySelector = okHttpClient.proxySelector;
      this.cookieJar = okHttpClient.cookieJar;
      this.internalCache = okHttpClient.internalCache;
      this.cache = okHttpClient.cache;
      this.socketFactory = okHttpClient.socketFactory;
      this.sslSocketFactory = okHttpClient.sslSocketFactory;
      this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
      this.hostnameVerifier = okHttpClient.hostnameVerifier;
      this.certificatePinner = okHttpClient.certificatePinner;
      this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
      this.authenticator = okHttpClient.authenticator;
      this.connectionPool = okHttpClient.connectionPool;
      this.dns = okHttpClient.dns;
      this.followSslRedirects = okHttpClient.followSslRedirects;
      this.followRedirects = okHttpClient.followRedirects;
      this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
      this.connectTimeout = okHttpClient.connectTimeout;
      this.readTimeout = okHttpClient.readTimeout;
      this.writeTimeout = okHttpClient.writeTimeout;
      this.pingInterval = okHttpClient.pingInterval;
    }

看上去好像密密麻麻,其实就是一些成员变量对吧,那么这个意思即使我们在使用A构造方法时,事实上,Builder对象已经产生了,并且这个对象的默认值赋给了OKHttpClient中的对象。
好了,第一步的创建结束了,总体来说是使用构建者模式创建了一个okHttpClient对象,并且将成员变量赋了默认值,当然,对于某些成员变量,我们应当是需要自己去修改的,这些,比如说这几个:
connectTimeout、readTimeout、writeTimeout;修改的方式也很简单,先通过获取Builder对象再修改,也就是修改一下创建方式:

        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(20,TimeUnit.MILLISECONDS)
                .writeTimeout(20,TimeUnit.MILLISECONDS)
                .readTimeout(20,TimeUnit.MILLISECONDS);
        OkHttpClient client = builder.build();
   //这里当然是可以链式调用的,这样写只是为了看起来方便一下

对于这些成员变量有什么作用呢,后面提到再说。
####2. request
打开ruquest的方法发现构造方法

  Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }

同样有一个builder对象,那么这肯定也是符合链式调用的,然后说一下参数:url不多说了,method方法,headers请求头,body请求体,tag请求标志,这些弄不清楚的话可以自行百度。

3.call
public interface Call extends Cloneable {

  Response execute() throws IOException;

  void enqueue(Callback responseCallback);

  void cancel();

  boolean isExecuted();

  boolean isCanceled();

  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

为了看起来清晰,我将源码中的注释删除了,可以看到call是一个接口,这个接口中还有一个接口,内部的接口中有方法可以获得Call对象,那么外部的接口被谁实现了呢,当然是okHttpClient了,不明白?

Call call = client.newCall(request);

我们是通过client.newCall(request);来获取call对象的对吧,好了到这里,创建部分就已经理解清楚了,(其实这还不是最终的call,在源码中还有一个realCall

okHttp核心原理

掌握上面的就大致明白了基础原理了,但是离彻底掌握还有点距离,下面看核心原理。
先上一张图
在这里插入图片描述
一步一步来吧

创建realCall

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

上面聊到这里,可以看到有三个参数被传递了,继续看

 RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    final EventListener.Factory eventListenerFactory = client.eventListenerFactory();

    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

    // TODO(jwilson): this is unsafe publication and not threadsafe.
    this.eventListener = eventListenerFactory.create(this);
  }

这个构造方法就是在原有的call上增加了一个拦截器(后面会聊到)RetryAndFollowUpInterceptor和一个事件监听。

realCall的enqueue方法

现在这个realCall已经有了,接着往下看

call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });

接着看enqueue这个方法:(我们同样只研究异步请求)

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

1.第一部分是一个同步线程锁,这个是因为同一个call只能请求一次,如果执行两次那么就会抛出异常。
2.第二部分这个方法可以看到主要是为retryAndFollowUpInterceptor增加了一个callStackTrace,主要是用来追踪堆栈的信息。然后是前面创建的eventevenListener进行了一个回调。

 private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

3.第三部分就到了我们这个异步请求的核心

client.dispatcher().enqueue(new AsyncCall(responseCallback));

继续向下看enqueue()这个方法

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

可以看到这个方法同样加了锁,这里调用这个方法,传了一个response的callback进来,这里看一下这个图
在这里插入图片描述
图结合代码就可以看出来,dispatcher.enqueue方法new 了一个AsyncCall(responseCallback)并传入了responseCallback,在这个方法内部,先做正在运行的请求数判断,如果没有大于同步请求的最大size,就调用runningAsyncCalls.add(call)加入这个队列,如果大于了,就先加到准备队列。
然后看一下这行代码

线程池执行任务

executorService().execute(call);

进入这个executorService()

 public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;

可以看到,创建了一个线程池,线程池的最大长度无限制,但如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。到这里,其实最终就是依靠线程池执行了任务,那么就关注一下这个rennable吧(其实就是executorService().execute(call)中的call),OK,进去看一眼:

  AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

... ...

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

构造方法很简单,看execute方法把,注意到

getResponseWithInterceptorChain()获取response

getResponseWithInterceptorChain();//这个方法执行得到了结果

那么就进入:

 Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    //创建一个List寸拦截器
    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));
	//8.把这些拦截器们一起封装在一个拦截器链条上面
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
        //执行proceed方法
    return chain.proceed(originalRequest);
  }

进入这个方法:

  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);
    //获取当前index对应的拦截器里面的具体的某个拦截器,
    Interceptor interceptor = interceptors.get(index);
    //然后执行拦截器的intercept方法,同时传入新的RealInterceptorChain对象(主要的区别在于index+1了)
    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");
    }

    return response;
  }

注意注释的两行,在proceed的方法内,又实例化了一个RealInterceptorChain,然后取出拦截器,执行方法,注意

 Response response = interceptor.intercept(next);

intercept这是一个借口中的方法,所以执行这个就相当于每个实现类都会执行,于是在这一步,每个拦截器都会执行这个方法,各司其职,各自完成自己的功能,至于每个拦截器完成怎样的功能,分析的文章太多了,可以参考点击跳转到拦截器分析

说两句

以上只分析了异步处理,其实不管是异步还是同步,都是一样的三部曲:
1.加入到Dispatcher里面的同步(或异步)队列
2.执行getResponseWithInterceptorChain方法
3.从Dispatcher里面的同步(或异步)队列移除。(只不过同步操作是直接运行了getResponseWithInterceptorChain方法,而异步是通过线程池执行Runnable再去执行getResponseWithInterceptorChain方法

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值