还记得 2015 年刚开始学习 Android 那会,还在为 Eclipse 项目中集成 Afinal.jar 、 Volley.jar 爆红而发愁,一晃而过到现在的 2020 年,见证了 AndroidStudio 的兴起。就这短短的5年时间,很多工具和技术都进行了更新换代或者在升级
今天我们就看看经过官方承认并使用的网络请求框架 OkHttp。
先来康康怎么接入,直接 gradle 中一行代码就搞定了。确实比之前还要下载相关 jar 包,然后在复制粘贴,然后在添加相关配置要简单的多了
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
如果一行代码搞不定的话,那就再加几行 - -~,在项目的 build 文件中添加国内镜像
buildscript {
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url "https://dl.bintray.com/thelasterstar/maven/" }
}
}
allprojects {
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url "https://dl.bintray.com/thelasterstar/maven/" }
}
}
调用就比较简单了,构建请求端,发送请求,回调接受请求结果。
//创建请求端
OkHttpClient()
//创建请求体
.newCall(Request.Builder().url("https://www.baidu.com").build())
//创建请求完成后的回调接口
.enqueue(object :Callback{
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
}
})
使用方面无甚可说,主要就是在项目中应用的时候,尽量在 okhttp 之上在包装一层。这样如果后期需要更换底层 okhttp 的时候对于项目改动就比较小了。
下面就从源码简单分析一下 okhttp 的实现
注意看题目,是源码简析,如果你想看完这篇文章就可以搞清楚 okhttp 的源码,那就要大失所望了。不是咱家不想写呀,主要是源码里面的细节处理和架构的设计,用文字表达就没有那个感觉了,还是得去撸源码,所以这里就只是把整个流程梳理一遍,具体的细枝末节都没有去说明。比如:建造者模式、责任链模式、线程池、线程安全等,在源码中都可以找到相应的影子。
闲话不多说,先来一张简易的调用流程图
从上面的调用流程图逐步分析源码,看看涉及到的类的相关调用
/**
* OkHttpClients Should Be Shared
* 意思就是 OkHttpClient 这个玩意在应用中创建一次就可以进行全局共享了,没有比较多次创建。
* 所以我们上面那个写法有点不规范哈,别纠结
*/
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
//① 构造一个 OkHttpClient 请求端
//这里的 builder 用到了建造者模式。里面主要是一些配置相关信息,感兴趣可以撸源码,这里不多说
constructor() : this(Builder())
/** Prepares the [request] to be executed at some point in the future. */
//通过传进来的 request 请求体,构造一个真正的请求结构 RealCall ,将来便于添加到线程池中运行
//② 调用 newCall 方法构造一个真正的请求体
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
}
class RealCall(
val client: OkHttpClient,
/** The application's original request unadulterated by redirects or auth headers. */
val originalRequest: Request,
val forWebSocket: Boolean
) : Call {
// ③ 调用该方法将异步请求 AsyncCall 加入线程池执行
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
// ④ 线程执行 run 方法请求网络返回结果 response
val response = getResponseWithInterceptorChain()
signalledCallback = true
// ⑤ 回调正确结果
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
// ⑤ 回调失败结果
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
// ⑤ 回调取消结果
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
client.dispatcher.finished(this)
}
}
}
}
}
好了,okhttp 的调用流程这里就结束了,够简单吧,不过这是真的啊,就是这么个流程,剔除了一下繁杂的判断和细节处理。
当然,上面那个没有说到 okhttp 的精髓所在,就是步骤 ④ 中的 getResponseWithInterceptorChain
这个方法,下面我们进去看看。
class RealCall(val client: OkHttpClient, val originalRequest: Request,
val forWebSocket: Boolean) : Call {
@Throws(IOException::class)
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)
// connect 请求拦截器
interceptors += ConnectInterceptor
if (!forWebSocket) {
//网络拦截器
interceptors += client.networkInterceptors
}
// 调用服务拦截器
interceptors += CallServerInterceptor(forWebSocket)
// 创建一个真正的拦截链条,并且将当前的拦截器集合传递进去
val chain = RealInterceptorChain(
call = this,interceptors = interceptors,index = 0,exchange = null,
request = originalRequest,connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,writeTimeoutMillis = client.writeTimeoutMillis)
var calledNoMoreExchanges = false
try {
//调用链条的第一个拦截器的处理方法,并返回处理结果
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
}
/**
* A concrete interceptor chain that carries the entire interceptor chain: all application
* interceptors, the OkHttp core, all network interceptors, and finally the network caller.
*
* 一个具体的拦截器链,包含整个拦截器链:所有应用程序*拦截器,OkHttp核心,所有网络拦截器,最后是网络调用者。
*/
class RealInterceptorChain(...){
@Throws(IOException::class)
override fun proceed(request: Request): Response {
......
calls++
......
// Call the next interceptor in the chain.
// 获取下一个拦截器
val next = copy(index = index + 1, request = request)
// 当前的拦截器
val interceptor = interceptors[index]
// 调用当前拦截器的 intercept 方法处理相关逻辑,并且将下一个拦截器传入,执行完成后返回 response
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
......
return response
}
}
上面就是 okhttp 的核心所在了,将一个网络请求的各个逻辑拆分成一个个拦截器去处理,比如重试机制、缓存机制、网络连接等。下面来一张图可能就比较直观了,依据整个责任链条,逐步处理自己相应的逻辑,并最终依据链条,将结果逐步返回。
下面是根据上面的逻辑,实现了一个大致的流程,是相当的粗糙。我觉得通过 150 行代码能梳理清除相关逻辑就可以了,不喜请喷
package com.silence.okhttpdemo.ok
import java.lang.Exception
import java.util.concurrent.SynchronousQueue
import java.util.concurrent.ThreadFactory
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
/**
* Author silence.
* Time:2020/8/19.
* Desc:简易版 okHttp,只为说明其大致流程
*/
object OK {
private val okHttp = OkHttp()
fun newCall(request: Request) = RealCall(okHttp,request)
fun addInterceptor(interceptor: Interceptor){
okHttp.interceptors.add(interceptor)
}
}
class Request(val url:String,var header:String = "")
class Response{
var isCache = false
var url=""
var header = ""
var body = ""
var result = ""
}
interface Callback {
fun onResponse(response: Response)
}
class Dispatcher{
private val executorService = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), ThreadFactory { runnable ->
Thread(runnable, "customer Thread")
}
)
internal fun enqueue(call: RealCall.AsyncCall) {
executorService.execute(call)
}
}
class OkHttp {
val dispatcher = Dispatcher()
val cacheMgr = CacheMgr()
val interceptors = mutableListOf<Interceptor>()
}
class RealCall(private val okHttp: OkHttp,
private val request: Request){
fun enqueue(responseCallback: Callback){
okHttp.dispatcher.enqueue(AsyncCall(responseCallback))
}
fun enqueue() = getResponseWithInterceptorChain()
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf<Interceptor>()
interceptors += okHttp.interceptors
interceptors += RetryAndFollowUpInterceptor()
interceptors += CacheInterceptor(okHttp.cacheMgr)
interceptors += CallServerInterceptor()
val chain = RealInterceptorChain(interceptors,0,request)
return chain.proceed(request)
}
internal inner class AsyncCall(private val responseCallback: Callback) :Runnable{
override fun run() {
responseCallback.onResponse(getResponseWithInterceptorChain())
}
}
}
interface Interceptor {
fun intercept(chain: Chain): Response
interface Chain {
fun proceed(request: Request): Response
fun request():Request
}
}
class RealInterceptorChain(
private val interceptors: List<Interceptor>,
private val index: Int,
private val request: Request
) : Interceptor.Chain{
override fun proceed(request: Request): Response {
val next = RealInterceptorChain(interceptors,index+1,request)
val interceptor = interceptors[index]
return interceptor.intercept(next)
}
override fun request() = request
}
class RetryAndFollowUpInterceptor : Interceptor{
var retryCount = 0
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
while (true){
try {
return chain.proceed(request)
} catch (e:Exception){
retryCount++
}
if (retryCount > 3){
val response = Response()
response.result = "error"
response.url = chain.request().url
return response
}
}
}
}
class CacheMgr{
//这里肯定不能这么写昂,我这么写只是为了便于理解
val cache = hashMapOf<String,Response>()
}
class CacheInterceptor(private val cacheMgr: CacheMgr) : Interceptor{
override fun intercept(chain: Interceptor.Chain): Response {
var response = cacheMgr.cache[chain.request().url]
if (response == null){
response = chain.proceed(chain.request())
cacheMgr.cache[chain.request().url] = response
} else {
response.isCache = true
}
return response
}
}
class CallServerInterceptor : Interceptor{
//注意这里,作为最后一个 Interceptor,这里没有在调用 chain.proceed 方法了
// 所以这里就是最底部了,从这里开始逐级向上返回 response
override fun intercept(chain: Interceptor.Chain): Response {
val response = Response()
response.body = "body"
response.header = chain.request().header
response.result = "ok"
response.url = chain.request().url
return response
}
}
在来康康怎么调用,基本上和 okHttp 调用方式差不多
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
OK.addInterceptor(object :Interceptor{
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
request.header = "customer header"
return chain.proceed(request)
}
})
OkHttpClient()
.newCall(
Request.Builder()
.url("https://www.baidu.com")
.build()
).enqueue(
object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
@Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
}
}
)
}
fun asnyc(view: View) {
OK.newCall(Request("https://www.baidu.com"))
.enqueue(object :Callback{
override fun onResponse(response: Response) {
Log.d("silence","url:${response.url} , result:${response.result} , header:${response.header} , isCache:${response.isCache} , body:${response.body}")
}
})
}
fun Synchronize(view: View) {
thread {
val response = OK.newCall(Request("https://www.baidu.com")).enqueue()
Log.d("silence","url:${response.url} , result:${response.result} , header:${response.header} , isCache:${response.isCache} , body:${response.body}")
}
}
}
ok,到这基本上就大致理清整体思路了,上面没有说到的任何一个细枝末节都足以用一篇文章详细描述,太多了,还是自己去扒拉吧,read the fuck code,best wishes