挂起函数的作用以及使用场景:
挂起函数会让协程从正在执行它的线程上脱离,并在挂起函数执行结束恢复到原线程,实现非阻塞式挂起。
可用于耗时的函数比如联网获取数据,数据库读写,文件io等
1.标准挂起函数应该定义线程切换,取消机制,结果返回三个部分
retrofit2.7.1里面部分源码KotlinExtensions.class
@JvmName("awaitNullable")
suspend fun <T : Any> Call<T?>.await(): T? {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T?> {
override fun onResponse(call: Call<T?>, response: Response<T?>) {
if (response.isSuccessful) {
continuation.resume(response.body())
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T?>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
2.如何自定义挂起函数
仅仅依靠suspend修饰符是改变不了线程的
suspend fun getUser():String{
log("getUser")
return ""
}
suspend fun main(){
getUser()
}
打印:
16:07:05:167 [main,1] getUser
示例1 只有返回值跟切换线程
suspend fun getUserInIo()= withContext(Dispatchers.IO){
"hhahah"
}
suspend fun main(){
GlobalScope.launch {
val userInIo = getUserInIo()
log(userInIo)
}.join()
}
打印:
16:23:50:267 [DefaultDispatcher-worker-3,14] hhahah
示例 不做线程切换,依靠协程做切换
suspend fun getUser1()= suspendCoroutine<String> {
//直接使用当前协程所在的线程,不做切换
it.resume("hhahahah")
}
suspend fun main(){
val user1 = getUser1()
log(user1)
GlobalScope.launch {
log(1)
val user11 = getUser1()
log(user1)
withContext(Dispatchers.IO){
val user11 = getUser1()
log(user1)
}
}.join()
}
示例
既做线程切换,又有取消
suspend fun getUserInFile()= suspendCancellableCoroutine<Int> {
var threadStop=true
it.invokeOnCancellation {
//如果没有这个,那么挂起函数是无法取消的
threadStop=false
}
thread (name = "hahaha"){
//模拟耗时操作
try {
var i=0;
while (threadStop&&i<1000){
Thread.sleep(100)
i++
log(i)
}
it.resume(i)
}catch (e: Exception){
it.resumeWithException(e)
}
}
}
suspend fun main(){
val launch = GlobalScope.launch{
log("start")
val userInFile = getUserInFile()
log(userInFile)
}
delay(1000)
launch.cancel()
delay(1000)
}