Android Okhttp源码和retofit

OkHttp

分发器

  • 对于同步请求,分发器只记录请求,用于判断dleRunnable是否需要执行
  • 对于异步请求,向分发器中提交请求

在这里插入图片描述
1、如何决定将请求放入ready还是running?
答:如果当前正在请求个数为64,则将请求放入ready等待执行;如果小于64,但是已经存在同一域名主机的请求5个,也会放入ready,否则放入running队列立即执行

class Dispatcher.java

  synchronized void enqueue(AsyncCall call) {
  // 先判断请求个数是否符合要求
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    //符合加入执行队列
      runningAsyncCalls.add(call);
      //执行,线程
      executorService().execute(call);
    } else {
    //不符合加入准备队列
      readyAsyncCalls.add(call);
    }
  }

2、从ready移动running的条件是什么?
答:每个请求执行完成就会从running移除,同时进行第一步相同逻辑的判断,决定是否移动!

        client.dispatcher().finished(this);

3、分发器线程池的工作行为?
答:无等待,最大分发。

class Dispatcher.java

//线程池,最大大小
  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;
  }

拦截器

这些拦截器会形成一条链,组织了请求接口的所有工作。

internal fun getResponseWithInterceptorChain(): Response {
    // Build a full stack of interceptors.
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors
    interceptors += RetryAndFollowUpInterceptor(client)
    interceptors += BridgeInterceptor(client.cookieJar)
    interceptors += CacheInterceptor(client.cache)
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
        interceptors += client.networkInterceptors
    }
    interceptors += CallServerInterceptor(forWebSocket)

    val chain = RealInterceptorChain(
        interceptors = interceptors
    //...
    )

    val response = chain.proceed(originalRequest)
}

1、RetryAndFollowUpInterceptor

class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    while (true) {
      try {
        try {
          response = realChain.proceed(request)
        } catch (e: RouteException) {
          //路由错误
          continue
        } catch (e: IOException) {
          // 请求错误
          continue
        }

        //获取响应码判断是否需要重定向
        val followUp = followUpRequest(response, exchange)
        if (followUp == null) {
          //没有重定向
          return response
        }
        //赋予重定向请求,再次进入下一次循环
        request = followUp
      } 
    }
  }
}
  • 当发生请求过程中错误的时候,就需要重试,也就是通过continue进入下一次循环,重新走到realChain.proceed方法进行网络请求。
  • 当请求结果需要重定向的时候,就赋予新的请求,并进入下一次循环,重新请求网络。
  • 当请求结果没有重定向,那么就直接返回response响应结果。

2、封装拦截器(BridgeInterceptor)

class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    //添加头部信息
    requestBuilder.header("Content-Type", contentType.toString())
    requestBuilder.header("Host", userRequest.url.toHostHeader())
    requestBuilder.header("Connection", "Keep-Alive")
    requestBuilder.header("Accept-Encoding", "gzip")
    requestBuilder.header("Cookie", cookieHeader(cookies))
    requestBuilder.header("User-Agent", userAgent)

    val networkResponse = chain.proceed(requestBuilder.build())

    //解压
    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)
    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        networkResponse.promisesBody()) {
      val responseBody = networkResponse.body
      if (responseBody != null) {
        val gzipSource = GzipSource(responseBody.source())
        responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
      }
    }

    return responseBuilder.build()
  }

请求前的代码很简单,就是添加了一些必要的头部信息,包括Content-Type、Host、Cookie等等,封装成一个完整的请求报文,然后交给下一个拦截器。

requestBuilder.header("Accept-Encoding", "gzip")

这一句其实就是在告诉服务器,客户端所能接受的文件的压缩格式,这里设置了gzip之后,服务器看到了就能把响应报文数据进行gzip压缩再传输,提高传输效率,节省流量。

所以请求之后的这段关于gzip的处理其实就是客户端对压缩数据进行解压缩,而GzipSource是okio库里面一个进行解压缩读取数据的类。

3、缓存拦截器(CacheInterceptor)

class CacheInterceptor(internal val cache: Cache?) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    //取缓存
    val cacheCandidate = cache?.get(chain.request())

    //缓存策略类
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    val cacheResponse = strategy.cacheResponse

    // 如果不允许使用网络,并且缓存数据为空
    if (networkRequest == null && cacheResponse == null) {
      return Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(HTTP_GATEWAY_TIMEOUT)//504
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build().also {
            listener.satisfactionFailure(call, it)
          }
    }

    // 如果不允许使用网络,但是有缓存
    if (networkRequest == null) {
      return cacheResponse!!.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build().also {
            listener.cacheHit(call, it)
          }
    }


    networkResponse = chain.proceed(networkRequest)

    // 如果缓存不为空
    if (cacheResponse != null) {
      //304,表示数据未修改
      if (networkResponse?.code == HTTP_NOT_MODIFIED) {
        cache.update(cacheResponse, response)
        return response
      } 
    }

    //如果开发者设置了缓存,则将响应数据缓存
    if (cache != null) {
      if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
        //缓存header
        val cacheRequest = cache.put(response)
        //缓存body
        return cacheWritingResponse(cacheRequest, response)
      }
    }

    return response
  }
}
  • 请求之前,通过request获取了缓存,然后判断缓存为空,就直接返回code为504的结果。如果有缓存并且缓存可用,则直接返回缓存。

  • 请求之后,如果返回304代表服务器数据没修改,则直接返回缓存。如果cache不为空,那么就把response缓存下来。

  • cache在实例化CacheInterceptor的时候传进去的,所以这个cache是需要我们创建OkHttpClient的时候设置的,比如这样:

val okHttpClient =
  OkHttpClient().newBuilder()
      .cache(Cache(cacheDir, 10 * 1024 * 1024))
      .build()
      

在这里插入图片描述

4、连接拦截器(ConnectInterceptor)

private fun findConnection(): RealConnection {

  // 1、复用当前连接
  val callConnection = call.connection 
  if (callConnection != null) {
      //检查这个连接是否可用和可复用
      if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
        toClose = call.releaseConnectionNoEvents()
      }
    return callConnection
  }

 //2、从连接池中获取可用连接
  if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
    val result = call.connection!!
    eventListener.connectionAcquired(call, result)
    return result
  }

  //3、从连接池中获取可用连接(通过一组路由routes)
  if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
      val result = call.connection!!
      return result
    }
  route = localRouteSelection.next()


  // 4、创建新连接
  val newConnection = RealConnection(connectionPool, route)
  newConnection.connect

  // 5、再获取一次连接,防止在新建连接过程中有其他竞争连接被创建了
  if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) { 
    return result
  }

  //6、还是要使用创建的新连接,放入连接池,并返回
  connectionPool.put(newConnection)
  return newConnection
}

获取连接的过程很复杂,为了方便看懂,我简化了代码,分成了6步。

1、检查当前连接是否可用。

怎么判断可用的?主要做了两个判断 1)判断是否不再接受新的连接 2)判断和当前请求有相同的主机名和端口号。

这倒是很好理解,要这个连接是连接的同一个地方才能复用是吧,同一个地方怎么判断?就是判断主机名和端口号。

还有个问题就是为什么有当前连接??明明还没开始连接也没有获取连接啊,怎么连接就被赋值了?

还记得重试和重定向拦截器吗?对了,就是当请求失败需要重试的时候或者重定向的时候,这时候连接还在呢,是可以直接进行复用的。

2和3、从连接池中获取可用连接。

第2步和第3步都是从连接池获取连接,有什么不一样吗?

connectionPool.callAcquirePooledConnection(address, call, null, false)
connectionPool.callAcquirePooledConnection(address, call, routes, false)

好像多了一个routes字段?

这里涉及到HTTP/2的一个技术,叫做 HTTP/2 CONNECTION COALESCING(连接合并),什么意思呢?

假设有两个域名,可以解析为相同的IP地址,并且是可以用相同的TLS证书(比如通配符证书),那么客户端可以重用相同的TCP连接从这两个域名中获取资源。

再看回我们的连接池,这个routes就是当前域名(主机名)可以被解析的ip地址集合,这两个方法的区别也就是一个传了路由地址,一个没有传。

继续看callAcquirePooledConnection代码:

internal fun isEligible(address: Address, routes: List<Route>?): Boolean {

  if (address.url.host == this.route().address.url.host) {
    return true 
  }

  //HTTP/2 CONNECTION COALESCING
  if (http2Connection == null) return false
  if (routes == null || !routeMatchesAny(routes)) return false
  if (address.hostnameVerifier !== OkHostnameVerifier) return false
  return true 
}

1)判断主机名、端口号等,如果请求完全相同就直接返回这个连接。

2)如果主机名不同,还可以判断是不是HTTP/2请求,如果是就继续判断路由地址,证书,如果都能匹配上,那么这个连接也是可用的。

4、创建新连接。

如果没有从连接池中获取到新连接,那么就创建一个新连接,这里就不多说了,其实就是调用到socket.connect进行TCP连接。

5、再从连接池获取一次连接,防止在新建连接过程中有其他竞争连接被创建了。

创建了新连接,为什么还要去连接池获取一次连接呢?因为在这个过程中,有可能有其他的请求和你一起创建了新连接,所以我们需要再去取一次连接,如果有可以用的,就直接用它,防止资源浪费。

其实这里又涉及到HTTP2的一个知识点:多路复用。

简单的说,就是不需要当前连接的上一个请求结束之后再去进行下一次请求,只要有连接就可以直接用。

HTTP/2引入二进制数据帧和流的概念,其中帧对数据进行顺序标识,这样在收到数据之后,就可以按照序列对数据进行合并,而不会出现合并后数据错乱的情况。同样是因为有了序列,服务器就可以并行的传输数据,这就是流所做的事情。

所以在HTTP/2中可以保证在同一个域名只建立一路连接,并且可以并发进行请求。

6、新连接放入连接池,并返回。

最后一步好理解吧,走到这里说明就要用这个新连接了,那么就把它存到连接池,返回这个连接。
在这里插入图片描述

IO拦截器(CallServerInterceptor)

class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {

    //写header数据
    exchange.writeRequestHeaders(request)
    //写body数据
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
      requestBody.writeTo(bufferedRequestBody)
    } else {
      exchange.noRequestBody()
    }

    //结束请求
    if (requestBody == null || !requestBody.isDuplex()) {
      exchange.finishRequest()
    }

    //获取响应数据
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .build()

    var code = response.code
    response = response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()
    return response
  }
}

这个拦截器 倒是没干什么活,之前的拦截器兄弟们都把准备工作干完了,它就调用下exchange类的各种方法,写入header,body,拿到code,response。

被遗漏的自定义拦截器(networkInterceptors)

好了,最后补上这个拦截器networkInterceptors,它也是一个自定义拦截器,位于CallServerInterceptor之前,属于倒数第二个拦截器。

那为什么OkHttp在有了一个自定义拦截器的前提下又提供了一个拦截器呢?

可以发现,这个拦截器的位置是比较深的位置,处在发送数据的前一刻,以及收到数据的第一刻。这么敏感的位置,决定了通过这个拦截器可以看到更多的信息,比如:

请求之前,OkHttp处理之后的请求报文数据,比如增加了各种header之后的数据。

请求之后,OkHttp处理之前的响应报文数据,比如解压缩之前的数据。

所以,这个拦截器就是用来网络调试的,调试比较底层、更全面的数据。

总结

  • addInterceptor(Interceptor),这是由开发者设置的,会按照开发者的要求,在所有的拦截器处理之前进行最早的拦截处理,比如一些公共参数,Header都可以在这里添加。
  • RetryAndFollowUpInterceptor,这里会对连接做一些初始化工作,以及请求失败的重试工作,重定向的后续请求工作。
  • BridgeInterceptor,这里会为用户构建一个能够进行网络访问的请求,同时后续工作将网络请求回来的响应Response转化为用户可用的Response,比如添加文件类型,content-length计算添加,gzip解包。
  • CacheInterceptor,这里主要是处理cache相关处理,会根据OkHttpClient对象的配置以及缓存策略对请求值进行缓存,而且如果本地有了可⽤的Cache,就可以在没有网络交互的情况下就返回缓存结果。
  • ConnectInterceptor,这里主要就是负责建立连接了,会建立TCP连接或者TLS连接,以及负责编码解码的HttpCodec
  • networkInterceptors,这里也是开发者自己设置的,所以本质上和第一个拦截器差不多,但是由于位置不同,用处也不同。这个位置添加的拦截器可以看到请求和响应的数据了,所以可以做一些网络调试。
  • CallServerInterceptor,这里就是进行网络数据的请求和响应了,也就是实际的网络I/O操作,通过socket读写数据。

retrofit

okHttp问题:

  • a、用户网络请求的接口配置繁琐,尤其需要配置复杂请求body,请求头,参数的时候
  • b、数据解析过程需要用户手动拿到Responsebody进行解析,不能复用
  • c、无法适配自动进行线程切换
  • d、万一我们的存在嵌套网络请求就会嵌入“回调陷阱”

在这里插入图片描述

retrofit创建流程

在这里插入图片描述

函数解析

1、 retrofit.create(service)
 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //使用了动态代理
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //利用反射得到ServiceMethod
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }
2、 ServiceMethod

在这里插入图片描述

  ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    //如果本地缓存有这个方法,则直接返回
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
  • serviceMethod 通过retofit创建,用于执行,每个接口都会有一个独立的serviceMethod,去查找具体需要用到的方法,一一对应、
  • Retrofit.Builder()是在全局上,引入用到类,但在每个接口具体实现,还是通过serviceMethod
  public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
    //使用了抽象工厂模式
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    ·····

通过注解解析到okhttpcall上,发送给okhttp2请求

数据适配

在初始化retofit的时候,会传入
new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())

  call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
        //数据解析
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

通过parseResponse(rawResponse)方法里的

T body = serviceMethod.toResponse(catchingBody);

通过responseConverter.convert(body),获取到,最初拿到的数据解析类型,进行解析返回

回调嵌套处理

通过Rxjava处理 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())

serviceMethod.adapt(okHttpCall);
--->

线程切换

  static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }

1、用户网络
retrofit 大局上获取可能用到的方法
构建设计模式:bind >5个参数

serviceMethod 通过retofit创建,用于执行,每个接口都会有一个独立的,去查找需要用到的类

rxjava:嵌套 链式调用
okhttp :call -> java对象
线程切换:platform 线程切换 ExecuorCallbackCall

RxJavaCallAdapterFactory

抽象工厂类型 根据不同的三种类型adapter

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值