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

如何在Service中获取SharedViewModel的LiveData存储的Clients列表

问题根本原因

ViewModel 的作用域是和 ViewModelStoreOwner 绑定的,你在 Activity/Fragment 中通过 activityViewModels() 获取的 SharedViewModel 依托的是 Activity 的 ViewModelStore,而你在 Service 中通过 ViewModelProvider(this) 获取的是和 Service 绑定的全新 ViewModel 实例,和 Activity 侧的不是同一个对象,自然拿不到已有的客户列表数据,观察也不会收到回调。


最优解决方案:引入单例数据层统一持有数据

通过全局单例的 Repository 层托管客户列表数据,不管是 ViewModel 还是 Service 都从该单例存取数据,保证全应用范围内数据统一。

步骤1:创建单例 Repository 类

class ClientRepository private constructor() {
    // 全局统一持有客户列表的LiveData
    val clientListLiveData = MutableLiveData<List<Client>>()

    companion object {
        @Volatile
        private var INSTANCE: ClientRepository? = null
        fun getInstance(): ClientRepository {
            return INSTANCE ?: synchronized(this) {
                INSTANCE ?: ClientRepository().also { INSTANCE = it }
            }
        }
    }

    // 对外提供更新列表的统一入口
    fun updateClientList(newList: List<Client>) {
        clientListLiveData.postValue(newList)
    }
}

步骤2:修改原有 SharedViewModel 逻辑

所有数据读写都走 Repository 层,保证数据变更全局同步:

class SharedViewModel : ViewModel() {
    private val repository = ClientRepository.getInstance()
    val clientList: LiveData<List<Client>> = repository.clientListLiveData

    // 原有新增客户逻辑修改为通过Repository更新
    fun addClient(client: Client) {
        val currentList = clientList.value?.toMutableList() ?: mutableListOf()
        currentList.add(client)
        repository.updateClientList(currentList)
    }

    // 原有编辑、删除客户的逻辑同理,全部通过Repository更新数据
}

步骤3:修改 Service 逻辑,直接从 Repository 取数据

Service 没有默认的 LifecycleOwner,使用 observeForever 观察数据,记得销毁时取消观察避免内存泄漏:

class ClientSaveService : Service() {
    private val repository = ClientRepository.getInstance()
    private var latestClientList: List<Client>? = null
    // 全局声明Observer,方便后续取消注册
    private val listObserver = Observer<List<Client>> { list ->
        latestClientList = list
    }

    override fun onCreate() {
        super.onCreate()
        // 注册数据观察者
        repository.clientListLiveData.observeForever(listObserver)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 原有runBlocking没有实际作用可以直接删除,CountDownTimer回调默认运行在主线程
        object : CountDownTimer(1000000000, 10000) {
            override fun onTick(millisUntilFinished: Long) {
                latestClientList?.let { list ->
                    val jsonString = Gson().toJson(list)
                    File("/storage/emulated/0/Documents/myFile.txt").printWriter().use { out ->
                        out.write(jsonString)
                    }
                    Toast.makeText(applicationContext, "Saved", Toast.LENGTH_SHORT).show()
                    Log.d("Tick", jsonString)
                }
            }

            override fun onFinish() {
                latestClientList?.let { list ->
                    val jsonString = Gson().toJson(list)
                    File("/storage/emulated/0/Documents/myFile.txt").printWriter().use { out ->
                        out.write(jsonString)
                    }
                    Toast.makeText(applicationContext, "Saved", Toast.LENGTH_SHORT).show()
                }
            }
        }.start()
        return START_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        // 移除观察者避免内存泄漏
        repository.clientListLiveData.removeObserver(listObserver)
    }

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

注意事项

  • Android 10及以上版本直接写公共目录路径会有权限问题,建议改用 MediaStore 或者存储访问框架(SAF)实现文件写入
  • 后台服务在高版本Android系统弹出Toast会被系统限制,如有必要请将服务改为前台服务
  • 长时间运行的定时任务建议改用 WorkManager 实现,兼容性和稳定性更好

内容的提问来源于stack exchange,提问作者Андрей Ерошенко

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 02:45:07