每日一道面试题
问:什么是反射机制?反射机制的应用场景有哪些?
答:在运行状态中,对于任意一个类,都能够获取这个类中的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。 应用场景:
1、逆向代码,例如反编译
2、与注解相结合的框架,如 Retrofit
3、单纯的反射机制应用框架,例如 EventBus(事件总线)
4、动态生成类框架 例如Gson
前言
1、okhttp怎样使用呢?
2、它的原理到底是怎样的?
3、由于篇章问题,本章不讲解拦截器的源码,这部分留到下一篇章讲解
正文
使用方式
流程图
伪代码
1、创建okHttp对象
2、构建Request请求对象
3、生成call对象
4、call发起请求(同步/异步)
代码
kotlin
fun createAndEnqueue(){
val urlString = ""
val okhttpClient = OkHttpClient()
val request = Request.Builder().url(urlString).get().build()
val call = okhttpClient.newCall(request)
//同步执行
Thread(object :Runnable{
override fun run() {
val response = call.execute()
val bodyString = response.body?.toString()
}
}).start()
//异步执行
call.enqueue(object:Callback{
override fun onFailure(call: Call, e: IOException) {
//请求失败
}
override fun onResponse(call: Call, response: Response) {
//请求成功
val body = response.body?.string()
}
})
}
在createAndEnqueue方法中,我们使用了okhttp的同步(execute)、异步(enqueue)方法进行网络请求,并获取了网络请求返回的结果(body)。细心的读者可能注意到,okhttp的同步(execute)和异步(enqueue)请求使用方式有点不同,为什么同步(exeute)方法需要放到子线程执行,而异步(enqueue)方法却不需要? 其实它们的命名已经给了我们答案。同步(execute)请求是在当前线程执行并返回结果 ;在android中,当前线程是主线程,而主线程又负责着界面的绘制等等流程,如果我们在主线程执行的话,那么就有可能导致主线程阻塞,直接导致app发生了ANR(应用无响应)。异步(enqueue)请求是在线程池中执行并通过回调返回结果,不会造成当前线程的阻塞。
源码
okhttpClient类
kotlin
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
@get:JvmName("dispatcher")
val dispatcher: Dispatcher = builder.dispatcher
...
//省略部分代码
constructor() : this(Builder())
...
//省略部分代码
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
...
//省略部分代码
class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher()
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
...//省略部分代码
internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher
this.eventListenerFactory = okHttpClient.eventListenerFactory
...//省略部分代码
}
...//省略部分代码
/**
* Sets the dispatcher used to set policy and execute asynchronous requests. Must not be null.
*/
fun dispatcher(dispatcher: Dispatcher) = apply {
this.dispatcher = dispatcher
}
...//省略部分代码
}
}
在OkHttpClient源码中可以看到,okHttpClient构造方法传入的是okhttp里的Builder对象,而Builder对象的构造方法主要是初始化属性。调用okHttpClient的newClall方法生成的是RealCall对象(请侧重注意这个RealCall对象,后面的同步/异步执行都是在这个对象里)
Request类
kotlin
class Request internal constructor(
@get:JvmName("url") val url: HttpUrl,
@get:JvmName("method") val method: String,
@get:JvmName("headers") val headers: Headers,
@get:JvmName("body") val body: RequestBody?,
internal val tags: Map<Class<*>, Any>
) {
...//省略部分源码
fun newBuilder(): Builder = Builder(this)
...//省略部分源码
open class Builder {
internal var url: HttpUrl? = null
internal var method: String
internal var headers: Headers.Builder
internal var body: RequestBody? = null
...//省略部分属性
constructor() {
this.method = "GET"
this.headers = Headers.Builder()
}
internal constructor(request: okhttp3.Request) {
this.url = request.url
this.method = request.method
this.body = request.body
...//省略部分属性初始化
this.headers = request.headers.newBuilder()
}
...//省略部分代码
open fun build(): okhttp3.Request {
return Request(
checkNotNull(url) { "url == null" },
method,
headers.build(),
body,
tags.toImmutableMap()
)
}
}
}
在Request源码中可以看到,调用newBuilder方法获取的是Request的Bulider对象,属性初始化都在Builder执行;在Bulider的默认构造方法可以看到Request默认使用的是Get请求。Builder对象的build方法返回的是Request对象。简单来说,就是初始化一个Request对象。
RealCall类
timeout、cancel方法
kotlin
private val timeout = object : AsyncTimeout() {
override fun timedOut() {
//取消当前请求的执行
cancel()
}
}.apply {
timeout(client.callTimeoutMillis.toLong(), MILLISECONDS)
}
//取消
override fun cancel() {
if (canceled) return // Already canceled.
//标志该执行已经取消
canceled = true
exchange?.cancel()
//在连接拦截器ConnectInterceptor-->realChain.call.initExchange()-->exchangeFinder.find()-->findHealthyConnection()-->findConnection()-->val newConnection =RealConnection(connectionPool, route) call.connectionToCancel = newConnection
connectionToCancel?.cancel()
//监听回调
eventListener.canceled(this)
}
enqueue(异步执行)
流程图
enqueue源码
kotlin
override fun enqueue(responseCallback: Callback) {
//检查当前是否请求是否正在执行
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
在源码中可以看到,异步(enqueue)请求方法源码非常简单,只有3句。那么它的具体逻辑是怎样的呢?首先执行的canllStart()方法,我们进入callStart()源码一探究竟
Dispatcher-->callStart()
kotlin
private fun callStart() {
初始化callStackTrace属性
this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
//eventLitener -->client.eventListenerFactory.create(this)-->EventListener.NONE.asFactory()
eventListener.callStart(this)
}
在callstart()方法,它主要的作用是初始化callStackTrace属性和调用eventListener的callStart()方法。那么eventListener是什么?在RealCall类中,internal val eventListener:EventListener = client.eventListenerFactory.create(this)
可以看到eventListener其实是client(OkHttpClient)中的eventListenerFactory属性。我们继续一步一步追踪发现,eventListenerFactory其实是在OkHttpClient的Builder类中被赋值的,它的初始值是EventListener.NONE.asFactory(),是一个空实现类;当然我们在构建OkHttpClient对象可以传入自己的EventListener来监听请求执行到了哪个步骤。(eventListener赋值链:RealCall:eventLitener -->OkHttpClient:eventListenerFactory-->Builder:eventListenerFactory=EventListener.NONE.asFactory())
我们继续在enqueue方法里往下看client.dispatcher.enqueue(AsyncCall(responseCallback))
, 在这段代码中,它调用了client(OkHttpClient)的dispatcher属性的enqueue方法。而OkHttpClient的dispatcher属性是获取OkHttpClient中Builer的dispatcher属性。在Buidler对象,dispather属性的默认值是Dispatcher()对象;当然我们也可以通过构建OkHttpClient对象传入自定义的dispather属性值,但是一般情况下我们使用框架默认的Dispatcher对象即可。
因此client.dispatcher.enqueue(AsyncCall(responseCallback))
其实调用的是Dispathcher类中的enqueue方法。具体逻辑是如何的呢?让我们进入源码一探究竟
Dispatcher-->enqueue()
kotlin
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
//判断forWebSocket是为false(不是网页请求)
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
在enqueue方法的同步代码块中,我们可以看到call(AsyncCall)添加到了readyAsyncCalls集合中,那么readyAsyncCalls到底是什么集合?在Dispatcher属性声明的地方,可以看到除了readyAsyncCalls还有runningAsyncCalls、runningSyncCalls,它们到底是什么?作用是什么?
kotlin
/** Ready async calls in the order they'll be run. */
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private val runningSyncCalls = ArrayDeque<RealCall>()
通过属性声明和注释,我们可知readyAsyncCalls(等待队列)、runningAsyncCalls(正在执行请求的异步队列)、runningSyncCalls(正在执行请求的同步队列)都是双端队列。(什么是双端队列?为什么okHttp使用双端队列?请查看okHttp源码阅读(一)--->双端队列(ArrayDeque)源码分析 - 掘金 (juejin.cn)) 它们的作用是:
readyAsyncCalls(等待队列):将需要等待的请求加入到此队列中
runningAsyncCalls(正在执行请求的异步队列):将正在异步执行的请求加入到此队列
runningSyncCalls(正在执行请求的同步队列):将正在同步执行的请求加入到此队列
了解readyAsyncCalls、runningAsyncCalls、runningSyncCalls之后。我们继续往下看
findExistingCallWithHost()
kotlin
private fun findExistingCallWithHost(host: String): AsyncCall? {
for (existingCall in runningAsyncCalls) {
if (existingCall.host == host) return existingCall
}
for (existingCall in readyAsyncCalls) {
if (existingCall.host == host) return existingCall
}
return null
}
在findExistingCallWithHost方法中,首先遍历了异步执行队列runningAsyncCalls,判断执行队列中是否有这个域名(host)的元素(AsyncCall),如果有,则返回。否,则遍历等待队列readyAsyncCalls,判断等待队列中是否有这个域名(host)的元素(AsyncCall),如果有,则返回。否,则返回null。if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
如果返回的元素不为null,则进入RealCall的reuseCallsPerHostFrom方法,这个方法的主要作用是将上一步找到相同域名的RealCall对象中的callsPerHost属性值赋值给当前执行的AsyncCall的callsPerHost。那么callsPerHos到底是什么,它的作用是什么?我们继续往后看 (在进入promoteAndExecute方法之前我先需要了解线程池的概念和线程池各个参数的意义)
线程池
线程池:由一组线程组成的线程队列。线程池里的线程可被复用,从而避免需要频繁创建线程来执行需要异步执行的各个任务。线程池的使用降低了线程创建和销毁的开销,提高了系统的性能。
js
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
corePoolSize:指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去
maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量
keepAliveTime:当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁
unit:keepAliveTime的单位
workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种
threadFactory:线程工厂,用于创建线程,一般用默认即可
handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务
promoteAndExecute()
kotlin
private fun promoteAndExecute(): Boolean {
//判断当前显示已经被锁住
this.assertThreadDoesntHoldLock()
//声明数组executableCalls
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
//获取双端队列的iterator对象
val i = readyAsyncCalls.iterator()
//遍历等待队列
while (i.hasNext()) {
//获取等待队列的元素
val asyncCall = i.next()
//maxRequests=64 判断当前所有正在执行的异步请求是否大于等于64,是则跳出遍历
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
//callsPerHost:当前所有执行请求的同一个host请求数量
//maxRequestsPerHost = 5判断所有正在执行请求的桶一个域名请求是否大于等于5
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
//等待队列移除该元素
i.remove()
//callsPerHost+1
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
//添加到异步执行队列中
runningAsyncCalls.add(asyncCall)
}
//runningCallsCount(): Int = runningAsyncCalls.size + runningSyncCalls.size
//判断当前执行队列(异步执行队列和同步执行队列的和)是否大于0
isRunning = runningCallsCount() > 0
}
//遍历队列(可以执行的所有请求)
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
//使用线程池执行请求
asyncCall.executeOn(executorService)
}
return isRunning
}
//线程池
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
promoteAndExecute方法的具体解析请看上面的注释;它主要的作用是判断当前请求是否可以执行,判断条件是:
1、正在执行的异步请求数是否小于等于64
2、正在执行异步队列中和这个请求相同域名的请求数是否小于等于5
executorService是一个核心线程数为0,最大线程数为Int的最大值,线程等待存活时间为60秒,任务队列为SynchronousQueue的线程池。线程池的工作原理如下图所示:
SynchronousQueue是一个无容量阻塞队列。从线程池的工作原理可以得出线程池executorService这样创建的好处是:任务可以及时运行,无需等待。
promoteAndExecute()方法里的asyncCall.executeOn(executorService)
,它调用的是AsyncCall类中的executeOn方法,executorService是Dispatcher类中所创建的一个无需等待即可执行任务的线程池。
AsyncCall
kotlin
internal inner class AsyncCall(private val responseCallback: Callback) : Runnable {
@Volatile var callsPerHost = AtomicInteger(0)
//记录当前执行请求中相同域名(host)请求数量
private set
fun reuseCallsPerHostFrom(other: AsyncCall) {
this.callsPerHost = other.callsPerHost
}
//获取域名
val host: String
get() = originalRequest.url.host
//获取Request对象
val request: Request
get() = originalRequest
//获取RealCall对象
val call: RealCall
get() = this@RealCall
//任务执行
fun executeOn(executorService: ExecutorService) {
//判断当前线程是否被锁住
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
//线程池执行该任务AsyncCall,请看AsyncCall的run方法
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
//进入失败回调
responseCallback.onFailure(this@RealCall, ioException)
} finally {
//移除退出后,结束该请求
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
//任务超时计算
timeout.enter()
try {
/**拦截器 client.interceptors、RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、CallServerInterceptor**/ //进行网络请求并返回结果
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 {
//将执行完的请求移出运行队列(runningAsyncCalls),并将等待队列(readyAsyncCalls)中的请求加入到执行队列中,使用线程池执行请求
client.dispatcher.finished(this)
}
}
}
}
在AsyncCall(Runnable)类的run()方法可以看到,它主要的流程分为3步:
-
执行请求并返回结果。在getResponseWithInterceptorChain()方法源码中可以看到,它依次通过自定义拦截器(client.interceptors)、重试及重定向拦截器(RetryAndFollowUpInterceptor)、桥接拦截器(BridgeInterceptor:主要功能将Request请求转化成网络请求,并执行请求,将请求返回的结果(response)解析成用户可用的response)、缓存策略拦截器(CacheInterceptor)、连接拦截器(ConnectInterceptor:与服务端建立连接,并且获得通向服务端的输入和输出流对象)、读取拦截器(CallServerInterceptor)后的结果进行解析返回 (由于篇章问题,拦截器的讲解请期待下一篇文章)
-
通过回调方式将执行结果返回
-
将执行完的请求移出运行队列(runningAsyncCalls),并将等待队列(readyAsyncCalls)中的请求加入到执行队列中,使用线程池执行请求。
Dispatcher类的finished方法
kotlin
internal fun finished(call: AsyncCall) {
//该请求的域名计数减1
call.callsPerHost.decrementAndGet()
finished(runningAsyncCalls, call)
}
// callsPerHost-1
public final int decrementAndGet() {
return (int)VALUE.getAndAdd(this, -1) - 1;
}
private fun <T> finished(calls: Deque<T>, call: T) {
val idleCallback: Runnable?
//同步代码块
synchronized(this) {
//正在异步执行队列移除该任务;如果当前异步执行队列没有该任务,则抛出异常
if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
idleCallback = this.idleCallback
}
//将等待队列的任务加入到执行队列里面并执行任务
val isRunning = promoteAndExecute()
if (!isRunning && idleCallback != null) {
idleCallback.run()
}
}
在Dispatcher类的finished方法可以看到,它主要的逻辑是将同域名请求的计数器值减1并将该任务移出执行队列,等待队列加入执行队列的逻辑请前往promoteAndExecute方法源码分析查看。
execute(同步执行)
流程图
源码
kotlin
override fun execute(): Response {
//检查请求是否正在执行
check(executed.compareAndSet(false, true)) { "Already Executed" }
//请求执行时间超时判断
timeout.enter()
//执行监听回调
callStart()
try {
//client.dispatcher-->Dispatcher() -->executed(call: RealCall) --> runningSyncCalls.add(call)
client.dispatcher.executed(this)
/**拦截器 client.interceptors、RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、CallServerInterceptor**/
//进行网络请求并返回结果
return getResponseWithInterceptorChain()
} finally {
//client.dispatcher-->Dispatcher() --> finished(call: RealCall) -->finished(runningSyncCalls, call)
client.dispatcher.finished(this)
}
}
在execute()方法可以看到,它主要的流程分为3步:
-
eventListener监听执行callStart方法。在RealCall类中可以看到enventListener实际是OkHttpClient中Builder对象的eventListenerFactory属性,开发者可以传入回调监听。eventListenerFactory默认是okhttp3包下的EventListener类,空实现。
-
执行请求并返回结果。在getResponseWithInterceptorChain()方法源码中可以看到,它依次通过自定义拦截器(client.interceptors)、重试及重定向拦截器(RetryAndFollowUpInterceptor)、桥接拦截器(BridgeInterceptor:主要功能将Request请求转化成网络请求,并执行请求,将请求返回的结果(response)解析成用户可用的response)、缓存策略拦截器(CacheInterceptor)、连接拦截器(ConnectInterceptor:与服务端建立连接,并且获得通向服务端的输入和输出流对象)、读取拦截器(CallServerInterceptor)后的结果进行解析返回 (由于篇章问题,拦截器的讲解请期待下一篇文章)
-
将执行完的请求移出运行队列(runningAsyncCalls),并将等待队列(readyAsyncCalls)中的请求加入到执行队列中,使用线程池执行请求。(请看Dispatcher的finish方法分析)
okhttp的同步请求、异步请求主干源码分析到此已经告一段落了,我们下一章再见~