当前位置: 首页 > news >正文

Android-kotlin协程学习总结

Kotlin协程实战对话

真题1:协程与线程的本质区别是什么?为什么说协程是轻量级的?​

面试官​:
“我看你项目中用协程替代了线程池,能说说协程和线程的核心区别吗?为什么协程更适合高并发?”

候选人​:
“协程和线程的区别有点像‘快递员’和‘卡车’的关系。线程是操作系统直接管理的‘大卡车’,每辆卡车得自己带一整个货箱(1MB的栈内存),启动和换路线得经过调度中心(内核态),成本高还慢。而协程更像是快递员,他们骑电动车(用户态调度),一辆卡车能装几千个快递员,换任务时只需要记下当前位置,轻装上阵,内存开销只有几十KB。

比如我们做电商秒杀,10万用户同时抢购。如果用线程池,开500个线程就得吃掉500MB内存,线程切换还得排队等调度,CPU都忙不过来。换成协程,一个线程就能跑几万个请求,内存省了90%,QPS直接翻倍——这就像用电动车送快递,不堵车还省油。”


真题2:GlobalScope为什么会导致内存泄漏?如何正确使用作用域?​

面试官​:
“你们项目里把GlobalScope全换成了lifecycleScope,是踩过坑吧?”

候选人​:
“没错!之前做视频弹幕功能时,用GlobalScope启动了一个无限循环的弹幕请求。结果用户退出了页面,协程还在后台疯狂拉数据,Fragment像僵尸一样赖在内存里,直接导致OOM崩溃。后来发现GlobalScope是‘长生不老’的,它的生命周期和整个App绑定,根本不管Activity的死活。

现在我们用lifecycleScope,相当于给协程装了‘智能开关’。Activity销毁时,自动触发onDestroy里的取消逻辑。就像给电器装了个漏电保护器——页面一关,所有后台任务立马断电,内存泄漏风险直接清零。

比如在ViewModel里这么写:

viewModelScope.launch { val data = withContext(Dispatchers.IO) { fetchData() } _liveData.value = data  //自动绑定到ViewModel生命周期
}

就算用户疯狂滑动页面,旧的请求也会被及时取消,再也不用担心后台跑‘幽灵任务’了。”


真题3:如何处理协程中的并发任务?async和launch有什么区别?​

面试官​:
“如果要同时调三个接口,等结果全到了再更新UI,用协程怎么搞?如果有一个接口挂了怎么办?”

候选人​:
“这得请出‘协程三剑客’——asyncawaitcoroutineScope。比如用户主页需要同时拉取用户信息、订单列表和消息通知,可以这么写:

lifecycleScope.launch {try {val (user, orders, messages) = coroutineScope {val userDeferred = async { api.getUser() }      // 并行启动val ordersDeferred = async { api.getOrders() }val messagesDeferred = async { api.getMessages() }Triple(userDeferred.await(), ordersDeferred.await(), messagesDeferred.await())}updateUI(user, orders, messages)  // 三个结果都到了才更新} catch (e: Exception) {showErrorToast("有一个接口挂了:${e.message}")  // 任一失败都会跳到这里}
}

这里的关键是coroutineScope会‘一损俱损’——只要有一个子协程抛异常,整个作用域里的任务全取消。而async和launch的核心区别在于‘带不带回执’——async返回Deferred对象(类似快递单号),需要用await获取结果,适合需要聚合数据的场景;launch则像‘寄平邮’,适合日志上报等无需返回值的任务


真题4:协程的挂起函数底层是如何实现的?​

面试官​:
“你说delay(1000)不阻塞线程,那协程怎么做到‘暂停而不卡死’的?”

候选人​:
“这得看Kotlin编译器的‘魔法’——CPS变换状态机。比如这个挂起函数:

suspend fun fetchData(): String {delay(1000)          // 挂起点1return "Data"         // 挂起点2
}

编译后会变成‘代码乐高’:

Object fetchData(Continuation $completion) {switch (label) {case 0: delay(1000, $completion);  // 记录位置1label = 1;return COROUTINE_SUSPENDED; // 挂起case 1: return "Data";             // 从位置1恢复}
}

Continuation就像书签,记录执行到哪里了。当delay触发时,协程把书签交给线程:‘你先去忙别的,1秒后喊我’。这时候线程腾出手来处理UI点击或者别的请求,完全不卡顿。时间一到,线程通过resume()把书签插回去,继续执行——整个过程像接力赛,而不是傻站着等。”


真题5:如何用协程优化RecyclerView的图片加载?​

面试官​:
“列表快速滑动时图片加载卡顿,你们怎么用协程解决的?”

候选人​:
“传统方案在onBindViewHolder里直接开线程,用户一滑到底,几百个请求把线程池挤爆。我们给每个Item绑定独立的Job,滑动时自动取消不可见的请求:

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {private var loadJob: Job? = nullfun bind(url: String) {loadJob?.cancel()  // 先取消之前的任务loadJob = lifecycleScope.launch {val bitmap = withContext(Dispatchers.IO) { loadImage(url)  // 耗时操作}if (isActive) {     // 检查是否已被取消itemView.image.setImageBitmap(bitmap)}}}fun unbind() {loadJob?.cancel()  // 视图滚出屏幕时取消}
}

对比Glide,这套方案更灵活——比如先加载缩略图,再用协程组合高清图加载,还能统一处理异常。之前列表滑动FPS只有30,优化后稳定60,内存波动减少70%。”

面试追问扩展

面试官​:

“协程和线程的本质区别是什么?为什么说协程更适合高并发场景?”

候选人​:

“您可以想象协程就像快递站里的一群快递员,而线程是送货的大卡车。卡车每次出发都要装货、申请路线、排队等调度,一趟只能送一个包裹,成本高还慢。而快递员们共用几辆电动车,一个卡车能塞下几百个快递员,每个人记下自己的送货路线,到了路口灵活切换——协程就这么干的。它不用等操作系统调度,自己管理任务切换,一个线程能跑几万个协程,内存开销只有几十KB。比如我们做秒杀活动,10万人同时抢购,用线程池开500个线程内存就爆了,但换成协程,一个线程轻松扛住,还能自动取消没必要的请求,这就是轻量级的威力。”


面试官​:

“听说GlobalScope容易导致内存泄漏,你们项目里是怎么解决的?”

候选人​:

“这真是血泪教训!之前做视频弹幕功能,用GlobalScope启动了一个无限循环的弹幕请求,结果用户退出页面后,协程还在后台疯狂拉数据,Fragment像‘僵尸’一样赖在内存里,最后直接OOM崩溃。后来才明白,GlobalScope是‘长生不老’的,它的生命周期和整个App绑定,根本不管Activity的死活。现在我们全员改用lifecycleScope——相当于给协程装了智能开关,页面销毁时自动断电。比如在Fragment里发起网络请求,只要用lifecycleScope.launch,用户一返回,请求立刻取消,再也不会出现后台偷偷耗流量的问题了。”


面试官​:

“用户快速滑动商品列表,每个Item都要加载图片,用协程怎么防止卡顿和错乱?”

候选人​:

“这个问题我们优化了三个月!首先,​给每个图片加载任务打标签。比如商品ID是123,加载完成后对比ImageView当前绑定的ID,如果不一样就直接丢弃,防止图片错位。其次,​用协程作用域控制生命周期。在RecyclerView的ViewHolder里,每次绑定新数据时,先取消前一个协程任务,像这样——”

候选人用手比划着空气代码:
“在onBindViewHolder里启动协程前,先检查job是否活跃,如果还在跑就立刻cancel。最后,​给IO操作加限流。比如用Dispatchers.IO的limitedParallelism限制同时加载的图片数,避免100张图同时开线程,线程池直接被打爆。现在我们的列表滑动FPS稳定在60帧,内存占用降了60%。”


面试官​:

“如果协程嵌套了三层异步任务,突然父协程被取消,会发生什么?”

候选人​:

“这就好比拆炸弹时剪错了线——子协程会连锁爆炸!结构化并发的核心思想就是‘要活一起活,要死一起死’。比如父协程负责订单支付,内部启动了子协程A查库存、子协程B扣积分。如果用户突然退出了页面,父协程取消,A和B会立刻收到取消信号,哪怕B已经完成了90%,也会立刻回滚。这虽然残酷,但保证了资源不会部分提交(比如积分扣了但库存没锁)。如果想让某个子协程‘苟活’,比如日志上报任务,可以用SupervisorJob把它包起来,这样即使父协程挂了,它还能在后台默默执行完。”

Android学习总结之Kotlin 协程_android kotlin协程-CSDN博客https://blog.csdn.net/2301_80329517/article/details/146909197Android学习总结之协程对比优缺点(协程一)_android 协程和线程-CSDN博客https://blog.csdn.net/2301_80329517/article/details/147256220

相关文章:

  • 瑞数6代jsvmp简单分析(天津电子税x局)
  • Linux云计算训练营笔记day17(Python)
  • 【b站计算机拓荒者】【2025】微信小程序开发教程 - chapter3 项目实践 - 3人脸识别采集统计人脸检测语音识别
  • 中间件redis 功能篇 过期淘汰策略和内存淘汰策略 力扣例题实现LRU
  • Unity屏幕适配——适配信息计算和安全区域适配
  • ElectronBot复刻-电路测试篇
  • PMO价值重构:从项目管理“交付机器”到“战略推手”
  • UE5 Niagara 如何让四元数进行旋转
  • Vite Vue3 配置 Composition API 自动导入与项目插件拆分
  • Ubuntu系统rsyslog日志突然占用磁盘空间超大怎么办?
  • mybatis-plus实现增删改查(新手理解版)
  • 弱光环境下如何手持相机拍摄静物:摄影曝光之等效曝光认知
  • 【深度学习-pytorch篇】2. Activation, 多层感知机与LLaMA中的MLP实现解析
  • 学习率及相关优化参数详解:驱动模型高效训练
  • DNS解析过程以及使用的协议名称
  • Pytorch中一些重要的经典操作和简单讲解
  • Fastmcp本地搭建 ,查询本地mysql,接入agent-cursor--详细流程
  • P2278 HNOI2003 操作系统
  • 【Python3教程】Python3基础篇之OS文件目录方法
  • 通过阿里云服务发送邮件
  • 杭州网站建设优化/网站快速优化排名官网
  • 大良网站建设基本流程/青岛seo服务
  • 网站制作真人游戏娱乐平台怎么做/b2b网站免费推广平台
  • 宠物商店的网站开发论文/网络游戏推广员
  • 深圳建设管理中心网站首页/上海网络营销上海网络推广
  • 贵阳高端网站建设/宁波网站建设优化企业