Android中runOnUiThread与CoroutineScope(Dispatchers.Main).launch的区别
你说得没错,两者确实都会把任务投递到主线程执行,但从生命周期管理、代码灵活性等维度来看,差异非常大:
结构化并发与生命周期绑定
协程的launch是和所属CoroutineScope绑定的(比如常用的lifecycleScope、viewModelScope),当Scope对应的组件(Activity/ViewModel)销毁时,所有在这个Scope下启动的协程会被自动取消,从根源避免内存泄漏。而runOnUiThread只是把Runnable扔进主线程的消息队列,完全不感知组件生命周期,哪怕Activity已经销毁,只要Runnable还没执行或正在执行,就会继续跑,很容易引发泄漏或空指针崩溃。支持挂起与异步代码线性化
协程可以配合suspend函数实现非阻塞的异步操作,还能在launch内部通过withContext灵活切换调度器,比如先在IO线程做耗时的网络/数据库操作,再切回主线程更新UI,代码是线性书写的,完全没有回调嵌套:lifecycleScope.launch { // 自动切到IO线程执行耗时操作 val userData = withContext(Dispatchers.IO) { api.fetchUserInfo() } // 自动切回主线程更新UI userNameTv.text = userData.name }而
runOnUiThread只能执行同步代码,如果要做异步操作,必须手动开启子线程,再嵌套runOnUiThread更新UI,很容易陷入回调地狱:Thread { val userData = api.fetchUserInfo() runOnUiThread { userNameTv.text = userData.name } }.start()异常处理能力
协程支持全局的CoroutineExceptionHandler统一捕获Scope内的异常,也可以直接在launch代码块里用try-catch包裹逻辑,异常处理更集中可控。而runOnUiThread中的异常如果没在Runnable内部手动加try-catch,会直接导致应用崩溃,且无法统一捕获所有runOnUiThread的异常。上下文扩展性
协程可以通过上下文传递额外的信息(比如CoroutineName用于调试、自定义Job控制协程生命周期),还能通过coroutineScope或supervisorScope实现父子协程的联动。runOnUiThread没有这类扩展能力,只是单纯的主线程任务投递工具。
内容的提问来源于stack exchange,提问作者Dev007

