You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何替换Activity.runOnUiThread实现ViewModel无依赖UI线程操作?

问题:用Handler替换Activity.runOnUiThread实现ViewModel脱离Activity依赖是否正确?

我有一个接收Activity参数并在Activity UI线程运行的函数,该函数由ViewModel调用,希望将其修改为基于ViewModel实现且不依赖Activity。现将原代码中的activity.runOnUiThread替换为以下Handler实现,请问该写法是否正确?

原代码

@SuppressLint("MissingPermission")
private fun onIntervalScanUpdate(over: Boolean, activity: Activity, bleStateCallback: BleStateCallback) {
    val devices: List<ScanResult> = ArrayList<ScanResult>(mDeviceMap.values)
    Collections.sort(
        devices
    ) { dev1: ScanResult, dev2: ScanResult ->
        val rssi1 = dev1.rssi
        val rssi2 = dev2.rssi
        rssi2.compareTo(rssi1)
    }
    activity.runOnUiThread(Runnable {
        mBleList.clear()
        mBleList.addAll(devices)
        if (over) {
            if(mBleList.isNotEmpty()) {
                Timber.d("xxx device found - ${mBleList.first().device.name}")
                mDevice = mBleList.first().device
                connect(bleStateCallback)
            } else {
                Timber.d("xxx device found - NONE")
            }
        }
    })
}

修改后的代码

val uiHandler = Handler(Looper.getMainLooper())
uiHandler.post {
    mBleList.clear()
    mBleList.addAll(devices)
    if (over) {
        if(mBleList.isNotEmpty()) {
            Timber.d("xxx device found - ${mBleList.first().device.name}")
            mDevice = mBleList.first().device
            connect(bleStateCallback)
        } else {
            Timber.d("xxx device found - NONE")
        }
    }
}

回答

这个写法功能上是可行的:Handler(Looper.getMainLooper())确实能把任务投递到主线程执行,和activity.runOnUiThread的核心效果一致,而且不再依赖Activity实例,满足你让ViewModel脱离Activity的需求。

但有几个细节需要优化:

  1. 避免重复创建Handler:不要每次调用函数都新建uiHandler实例,应该把它作为ViewModel的全局属性初始化一次,减少不必要的对象开销:
    private val uiHandler = Handler(Looper.getMainLooper())
    
  2. 防止内存泄漏:ViewModel的生命周期长于Activity,若Handler投递的任务在ViewModel销毁后仍未执行,可能引发泄漏。建议在ViewModel的onCleared()方法中清理未完成的任务:
    override fun onCleared() {
        super.onCleared()
        uiHandler.removeCallbacksAndMessages(null)
    }
    
  3. 推荐更贴合Jetpack的方案:其实用ViewModel搭配Coroutines或LiveData是更标准的实现,无需手动管理线程切换,也更符合MVVM设计:
    • 用viewModelScope(Coroutines)的示例:
      private fun onIntervalScanUpdate(over: Boolean, bleStateCallback: BleStateCallback) {
          val devices: List<ScanResult> = ArrayList(mDeviceMap.values)
          Collections.sort(devices) { dev1, dev2 ->
              dev2.rssi.compareTo(dev1.rssi)
          }
          viewModelScope.launch(Dispatchers.Main) {
              mBleList.clear()
              mBleList.addAll(devices)
              if (over) {
                  if(mBleList.isNotEmpty()) {
                      Timber.d("xxx device found - ${mBleList.first().device.name}")
                      mDevice = mBleList.first().device
                      connect(bleStateCallback)
                  } else {
                      Timber.d("xxx device found - NONE")
                  }
              }
          }
      }
      
      viewModelScope会在ViewModel销毁时自动取消协程,从根源避免泄漏,代码也更简洁。

内容的提问来源于stack exchange,提问作者BRDroid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 12:53:15