Kotlin 之 协程,2024大厂Android面试题精选

  • 业务框架层指的是我们常用的协程函数

基础设施层(原生api)实现例子


    private fun doContinuation() {

        //协程体

        val continuation = suspend {

            println("协程执行中...")

            "协程的返回值"

        }.createCoroutine(object : Continuation<String> {

            override fun resumeWith(result: Result<String>) {

                //回调

                println("协程执行结束: $result")

            }



            override val context: CoroutineContext = EmptyCoroutineContext



        })

        continuation.resume(Unit)

    }



如上代码使用的是:import kotlin.coroutines.*

而kotlin协程业务框架层使用的是:import kotlinx.coroutines.*

Android中协程解决了什么问题?

  • 处理耗时任务,这种任务常常会阻塞主线程

  • 保证主线程安全,确保安全的在主线程调用suspend函数

在Android 11 谷歌建议使用协程来替代异步任务(asynctask)

协程的挂起与恢复

常规函数包括:invoke(call)和return,协程新增了suspend和resume

suspend:挂起或暂停,表示暂停执行当前协程,并保存所有局部变量

resume:让已暂停的协程从暂停处恢复执行

使用 suspend关键字修饰的函数叫挂起函数

挂起函数只能在协程体内或其他挂起函数内调用

挂起和阻塞

挂起:挂起点先记录下来,然后去做耗时任务,做完以后再恢复

阻塞:不做别的事情,一直等待


    private fun doGlobalScope() {

        //如果没有指定调度器,那么默认就是使用:Dispatchers.Default(default也是非主线程)

        GlobalScope.launch(Dispatchers.Main) {

            //挂起(不会阻塞主线程,比如按钮按下去会立马弹起来,然后6秒后打印)

            delay(6000)

            Log.v("zx", "${Thread.currentThread().name},挂起6秒后")



        }

        //阻塞(会阻塞主线程,比如按钮按下去要等5秒后才能弹起来)

        Thread.sleep(5000)

        Log.v("zx", "${Thread.currentThread().name},阻塞5秒后")

    }



主线程在遇到挂起点后,可以不用等待直接更新UI,但是遇到阻塞就必须等待阻塞完成之后

协程的调度器 Dispatchers

所有协程必须的调度器中运行

Dispatchers.Main:主线程,处理UI交互和轻量级任务(调用suspend,调用UI函数,更新liveData)

Dispatchers.IO:非主线程,为磁盘和网络IO进行了优化(数据库,文件读写,网络请求)

Dispatchers.Default:非主线程,专为CPU密集型任务进行了优化(数组排序,json解析,差异判断)

任务泄露

当某个协程任务丢失,导致内存,cpu资源浪费,称为任务泄露,为了避免协程泄露,Kotlin引入了结构化并发机制。

结构化并发的作用:取消任务,追踪任务,发送错误信号

结构化并发

定义协程,必须指定其CoroutineScope,它会跟踪所有协程,还可以取消由它所启动的所有协程。

常用的api有:

GlobalScope:生命周期是process级别的,即使Acitivty和fragment已经销毁,协程仍然在执行,GlobalScope是一个顶级协程,不太建议直接用

MainScope:在Activity中使用,可以在onDestroy中取消协程

viewModelScope:只能在viewModel中使用,绑定viewModel的生命周期

lifecycleScope:只能在Activity和fragment中使用,会绑定Activity和Fragment的生命周期

MainScope使用案例:

class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {



    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    private val mainScope = MainScope()

    

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(binding.root)

		doMainScope()

    }

    

    private fun doMainScope() {



        //retrofit不用写withcontext(Dispatchers.IO)因为retrofit会自动侦察到,

        //如果你是挂起函数会自动启用协程,创建一个异步线程去做操作

       mainScope.launch {

           //Toast.makeText(this@MainActivity, "MainScope", Toast.LENGTH_SHORT).show()

           try {

               //retrofit请求

               delay(1000)

           } catch (e: Exception) {

               //调用 mainScope.cancel()取消协程会抛出异常

               e.printStackTrace()

           }

       }



        //MainActivity 继承了委托以后CoroutineScope by MainScope(),就可以直接使用launch了

        launch {

        }

     }



    override fun onDestroy() {

        super.onDestroy()

        mainScope.cancel()

        //class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {}

        //MainActivity 继承了委托以后CoroutineScope by MainScope(),就可以直接使用cancle

        cancel()

    }

}



viewModelScope使用案例

class MyViewModel:ViewModel() {



       private val _count = MutableLiveData<Int>()

    val count: LiveData<Int>

        get() = _count

        

    fun getUser(){

        //因为继承了ViewModel,那么就可以直接使用viewModelScope

        viewModelScope.launch {

            //如果是耗时操作的话,retrofit会自动起一个io线程来执行,可以省略withContext(Dispatchers.IO)

            delay(3000)

            _count .value = 123



        }

    }

}



调用


class MainActivity : AppCompatActivity() {



 	private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    private val myViewModel by viewModels<MyViewModel>()

    

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(binding.root)

        initViewModelAndLiveData()

      

    }



    private fun initViewModelAndLiveData() {

        myViewModel.count.observe(this, Observer {

            binding.btnRandom.text = it.toString()

        })

        

	myViewModel.getUser()

    }

}

    



(二)启动取消协程

=======================================================================

协程的构建器


launch和async构建器都用来启动新协程

launch,返回一个job并且不附带任何结果值

async,返回一个Deferred,Deferred也是一个job,可以使用.await()在一个延期的值上得到它的最终结果


    //等待一个作业:join与await

    private fun runBlocking1(){

        //runBlocking可以把主线程变成一个协程

        //job1和job2是runBlocking的子协程

        //runBlocking会等待job1和job2这两个子协程执行完毕,会阻塞主线程(阻塞:按钮按下不会立马弹起job1和job2执行完了才会弹起)

        runBlocking {



            val job1 = launch {

                delay(2000)

                Log.v("zx", "job1 to finish")

            }



            val job2 = async {

                delay(2000)

                Log.v("zx", "job2 to finish")

                "job2 value"

            }

            //await可以得到返回值

            val job2Result = job2.await()

            Log.v("zx", "job2的返回值:$job2Result")



        }



需求:等待job1执行完毕以后再执行job2和job3

如果通过launch来启动的话,用join函数

如果通过async来启动的话,用await函数


        

        //join和await都是挂起函数,不会阻塞主线程



        //如果通过launch来启动的话,用join函数

        runBlocking {



            val job1 = launch {

                delay(2000)

                Log.v("zx", "job1 to finish")

            }

            //这个函数会等待job1执行完后才会执行后面的

            job1.join()



            val job2 = launch {

                delay(100)

                Log.v("zx", "job2 to finish")

            }

            val job3 = launch {

                delay(100)

                Log.v("zx", "job3 to finish")

            }



        }

        //如果通过async来启动的话,用await函数

        runBlocking {



            val job1 = async {

                delay(2000)

                Log.v("zx", "job1 to finish2")

            }

            //这个函数会等待job1执行完后才会执行后面的

            job1.await()



            val job2 = async {

                delay(100)

                Log.v("zx", "job2 to finish2")

            }

            val job3 = async {

                delay(100)

                Log.v("zx", "job3 to finish2")

            }



        }

    }



需求:前面2个任务相加的结果给第三个任务(async结构化并发)


   //runBlocking 在主线程中,子协程会继承父协程的上下文

   //runBlocking是Dispatchers.Main中启动的,doOne和doTwo也会使用父协程的调度器Dispatchers.Main中启动

    private fun runBlocking2() {

        //前面2个任务相加的结果给第三个任务(async结构化并发)

        runBlocking {

            val time = measureTimeMillis {

                //同步的

                val one = doOne()

                val two = doTwo()

                Log.v("zx", "数据${one + two}")

            }

            Log.v("zx", "time = $time")

        }

        runBlocking {

            val
  • 25
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值