Kotlin协程N问(二)Android中的应用

66 阅读3分钟

1、 Android使用kotlin协程举例,麻烦版

var job:Job?=null
fun test15(){
    //让协程跑在主线程当中,因为默认Default是非主线程。同事加上SupervisorJob 其中协程错误不要干扰到其它协程
    job = GlobalScope.launch(Dispatchers.Main+ SupervisorJob()) { 
        launch { 
            throw NullPointerException("空指针")
        }
        
        val result = withContext(Dispatchers.IO){
            "子线程中的请求结果"
        }
        
        launch { 
            //网络请求3
        }
         
        btn.text = result
    }
}

override fun onDestroy() {
    super.onDestroy()
    //防止内存泄漏
    job?.cancel()
}

2、上述太麻烦了,于是有个官方版本的 MainScope

//帮我们直接省了
    public fun MainScope():CoroutineScope = CoroutineScope(SupervisorJob()+ Dispatchers.Main)
private val mainScope = MainScope()
private fun start() {
    mainScope.launch {
        launch {
            throw  NullPointerException("空指针")
        }
        val result = withContext(Dispatchers.IO) {
            //网络请求...
            "请求结果"
        }
        launch {
            //网络请求3...
        }
        btn.text = result
    }
}
override fun onDestroy() {
    super.onDestroy()
    mainScope.cancel()
}

3、在Activity与Framgent中使用协程

3.1、有生命周期很好用

  • lifecycleScope会在DESTROYED是销毁,不用担心忘记主动协程cancel导致的内存泄漏,它自己在内部会cancel。

3.2、 在全局初始化时调用

init { lifecycleScope.launchWhenResumed { Log.d("init", "在类初始化位置启动协程") } }
- `launchWhenCreated`
- `launchWhenStarted`
- `launchWhenResumed`
  • 值得一提的时onResume()执行完之后,STATE才被设置为RESUME。所以会先跑完onResume()

3.3、整一个CoroutineExceptionHadler就好了

class MainActivity : AppCompatActivity() {
    val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        Log.d("exceptionHandler", "${coroutineContext[CoroutineName]} $throwable")
    }
    fun load() {
        lifecycleScope.launch(exceptionHandler) {
           //省略...
        }
         lifecycleScope.launch(exceptionHandler) {
           //省略...
        }
         lifecycleScope.launch(exceptionHandler) {
           //省略...
        }
    }
}

3.4、能不能CoroutineExceptionHadler也不写了?写个扩展函数呗!

3.4.1、Activity版本的
//GlobalCoroutineExceptionHandler.KT

class GlobalCoroutineExceptionHandler(private val errCode: Int, private val errMsg: String = "", private val report: Boolean = false):CoroutineExceptionHandler {
    override val key: CoroutineContext.Key<*>
        get() = CoroutineExceptionHandler

    override fun handleException(context: CoroutineContext, exception: Throwable) {
        val stackTraceToString = exception.stackTraceToString()
        Log.e("$errCode","GlobalCoroutineExceptionHandler:${stackTraceToString}")
    }
}

//其它扩展函数所用的kt文件
inline  fun AppCompatActivity.requestMain(
    errCode: Int = -1, errMsg: String = "", report: Boolean = false,
    noinline block:suspend CoroutineScope.()->Unit){
    lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode,errMsg, report)){
        block.invoke(this)
    }

}

inline fun AppCompatActivity.requestIO(
    errCode: Int = -1, errMsg: String = "", report: Boolean = false,
    noinline block: suspend CoroutineScope.() -> Unit): Job {
    return lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)
    }
}

inline fun AppCompatActivity.delayMain(
    errCode: Int = -1, errMsg: String = "", report: Boolean = false,
    delayTime: Long, noinline block: suspend CoroutineScope.() -> Unit) {
    lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        withContext(Dispatchers.IO) {
            delay(delayTime)
        }
        block.invoke(this)
    }
}

//////ACTIVITY.KT
fun test16(){
    requestMain {
        delay(2000)
        Toast.makeText(this@MainActivity,"haha",Toast.LENGTH_SHORT).show()
    }
    requestIO {
        loadNetData()
    }
    delayMain(100){
        Toast.makeText(this@MainActivity,"haha",Toast.LENGTH_SHORT).show()
    }

}

private  suspend fun loadNetData():String{
 return   withContext(Dispatchers.IO){
     //网络请求
        "kentwang"
    }
}


3.4.2、Fragment版本的

inline fun Fragment.requestMain(
    errCode: Int = -1, errMsg: String = "", report: Boolean = false,
    noinline block: suspend CoroutineScope.() -> Unit) {
    lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)
    }
}

inline fun Fragment.requestIO(
    errCode: Int = -1, errMsg: String = "", report: Boolean = false,
    noinline block: suspend CoroutineScope.() -> Unit) {
    lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)
    }
}

inline fun Fragment.delayMain(
    errCode: Int = -1, errMsg: String = "", report: Boolean = false, delayTime: Long,
    noinline block: suspend CoroutineScope.() -> Unit) {
    lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        withContext(Dispatchers.IO) {
            delay(delayTime)
        }
        block.invoke(this)
    }
}

3.5、为什么Activity和Fragment要分开写?他们都是使用的lifecycleScope,我们直接通过lifecycleScope扩展就不可以了吗?不行!可能生命周期不一样

5、ViewModel中使用协程

inline fun ViewModel.requestMain(
        errCode: Int = -1, errMsg: String = "", report: Boolean = false,
        noinline block: suspend CoroutineScope.() -> Unit) {
    viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)
    }
}

inline fun ViewModel.requestIO(
        errCode: Int = -1, errMsg: String = "", report: Boolean = false,
        noinline block: suspend CoroutineScope.() -> Unit) {
    viewModelScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)
    }
}

inline fun ViewModel.delayMain(
        errCode: Int = -1, errMsg: String = "", report: Boolean = false, delayTime: Long,
        noinline block: suspend CoroutineScope.() -> Unit) {
    viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        withContext(Dispatchers.IO) {
            delay(delayTime)
        }
        block.invoke(this)
    }
}

6、其它环境下使用

ServiceDialogPopWindow以及一些其他的环境中又该如何使用。

  • 协同作用域:这一类我们就模仿MainScope自定义一个CoroutineScope
  • 主从(监督)作用域:这一类我们直接使用MainScope,然后在此基础上做一些扩展即可。
6.1、Service等协同作用域
public fun NormalScope(): CoroutineScope = CoroutineScope(Dispatchers.Main)
abstract class BaseService :Service(){
    private val normalScope = NormalScope()
    override fun onDestroy() {
        normalScope.cancel()
        super.onDestroy()
    }

    protected fun requestMain(
        errCode: Int = -1, errMsg: String = "", report: Boolean = false,
        block: suspend CoroutineScope.() -> Unit) {
        normalScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
            block.invoke(this)
        }
    }


    protected fun requestIO(
        errCode: Int = -1, errMsg: String = "", report: Boolean = false,
        block: suspend CoroutineScope.() -> Unit): Job {
        return normalScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
            block.invoke(this)
        }
    }

    protected fun delayMain(
        delayTime: Long,errCode: Int = -1, errMsg: String = "", report: Boolean = false,
        block: suspend CoroutineScope.() -> Unit) {
        normalScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
            withContext(Dispatchers.IO) {
                delay(delayTime)
            }
            block.invoke(this)
        }
    }
}

 
class MainService : BaseService() {

    override fun onBind(intent: Intent): IBinder?  = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        requestIO {
            //网络加载
        }
        return super.onStartCommand(intent, flags, startId)
    }
}
  • 同理在DialogPopWindow以及一些其他的环境中可以依照此方法,定义符合我们自己需求的CoroutineScope一定要记得不要跨域使用,以及及时的关闭协程。