改造 2.6.0为我们带来了对 suspend
功能的支持。 Call
和 enqueue
在后台使用:
Behind the scenes this behaves as if defined as fun user(...): Call and then invoked with Call.enqueue. You can also return Response for access to the response metadata.
这意味着请求是异步的,网络调用是在 ThreadPoolExecutor
表单 OkHttp
上完成的。我们不关心此 question 中描述的切换线程.
interface ApiService {
@GET("custom/data/api")
suspend fun getData(): String
}
class CustomViewModel: ViewMode() {
// I omitted the initialization of the repository or usecase for simplicity
fun getData() {
viewModelScope.launch { // Dispatchers.Main
apiService.getData() // network call on ThreadPoolExecutor
// continue on Main thread
}
}
}
此时我们有一个线程上下文切换。
但是如果我想在网络调用后做一些额外的工作怎么办,例如映射。而且我不想在主线程上执行此操作:
fun getData() {
viewModelScope.launch { // Dispatchers.Main
val result = apiService.getData() // network call on ThreadPoolExecutor
// continue on Main thread
val uiData = withContext(Dispatchers.IO) { // Coroutine runs on a shared thread pool
mapResult(result) // suspending long running task
}
// continue on Main thread
}
}
此时我们有两个线程上下文切换:一个用于网络校准,另一个用于映射。
我的问题是关于优化的。不使用Retrofit接口(interface)中的suspend
函数,而是使用一个线程切换协程调度器来进行网络调用等工作,是不是更优化?
interface ApiService {
@GET("custom/data/api")
fun getData(): Call<String>
}
fun getData() {
viewModelScope.launch { // Dispatchers.Main
// Main thread
val uiData = withContext(Dispatchers.IO) { // Coroutine runs on a shared thread pool
val result = apiService.getData().execute().body() // network call
mapResult(result) // suspending long running task
}
// continue on Main thread
}
}
我知道在一个简单的应用程序中优化不是那么大并且以纳秒为单位进行测量,但这不是主要问题。此外,问题不在于代码、异常处理等。问题在于理解多线程的内部机制,支持 Retrofit suspend
和协程。
最佳答案
如果协程在同一个线程中运行,则协程之间的上下文切换比线程之间的上下文切换要便宜得多。但是,如果它们在不同的线程中,则切换协程上下文需要进行昂贵的线程上下文切换。考虑下一个例子:
suspend fun doWork() {
val data1 = fetchData1() // calling suspend function
val data2 = fetchData2(data1) // calling another suspend function
withContext(Dispatchers.Main) {
updateUI(data2)
}
}
这里切换协程上下文需要昂贵的线程上下文切换。
在您的情况下,Api 调用发生在后台线程中,您使用 Dispatchers.IO
在后台线程中运行映射。所以映射有可能发生在同一个后台线程中。因此,您的操作并不昂贵。
但是如果 OkHttp
有它自己的 ExecutorService
,确保协程和 OkHttp
可以使用相同的 Thread
s 我们可以创建通用的 ExecutorService
并将其用于 OkHttp
(在使用 OkHttpClient.Builder.dispatcher
构造 OkHttpClient
时设置它>) 和协程(在切换上下文时设置它 withContext(commomExecutorInstance.asCoroutineDispatcher())
)。
关于android - 支持 Retrofit 挂起的线程切换优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70413352/