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

Android中单例无引用时会销毁吗?Repository模式遇数据丢失问题

问题解答:切换标签页后Repository中LiveData数据丢失

这种数据变为0的现象不正常,因为伴生对象属于类级别的单例,其持有的news MutableLiveData理论上会在应用进程存活期间保留数据。出现这个问题大概率是你的实现方式有漏洞,下面分析原因并给出修复方案:

可能的原因

  • MutableLiveData被意外重置:你的news变量用了var修饰,意味着它可以被重新赋值。如果在下载逻辑中每次请求都创建新的MutableLiveData并赋值给news,旧的LiveData会被丢弃,新的LiveData初始为空列表,就会出现返回Fragment后数据为0的情况。
  • 错误的单例实现逻辑:虽然伴生对象是单例,但如果你的Repository在某些场景下被多次初始化(比如误写了构造函数调用),可能导致数据状态混乱。
  • Fragment/ViewModel生命周期未正确绑定:如果你的ViewModel是直接new出来的,而非通过ViewModelProvider获取,当Fragment因切换标签页被销毁重建时,ViewModel会被重新创建,若此时未正确复用Repository的单例数据,也可能出现数据丢失。

修复方案

1. 修正Repository的实现

把news改为val避免被意外替换,同时用更可靠的单例模式实现,更新数据时修改LiveData内部的列表而非替换LiveData对象:

class NewsRepository {
    // 用val确保LiveData对象不会被替换,初始化为空列表
    val news: MutableLiveData<MutableList<Item>> = MutableLiveData(mutableListOf())
    private val mainRepository = MainRepository(ApiHelper(RetrofitBuilder.apiService))

    // 双重校验锁实现单例,避免多线程问题
    companion object {
        @Volatile
        private var instance: NewsRepository? = null

        fun getInstance(): NewsRepository {
            return instance ?: synchronized(this) {
                instance ?: NewsRepository().also { instance = it }
            }
        }
    }

    // 示例:正确的新闻数据获取逻辑
    fun fetchNews() {
        mainRepository.getNews().enqueue(object : Callback<List<Item>> {
            override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
                response.body()?.let { fetchedItems ->
                    // 获取当前LiveData中的列表,更新后重新设置
                    val currentList = news.value ?: mutableListOf()
                    currentList.clear()
                    currentList.addAll(fetchedItems)
                    news.postValue(currentList)
                }
            }

            override fun onFailure(call: Call<List<Item>>, t: Throwable) {
                // 处理请求失败逻辑
            }
        })
    }
}

2. 正确使用ViewModel

ViewModel必须通过ViewModelProvider获取,确保在Fragment销毁重建时不会被重新创建,从而保持对Repository单例的引用:

class NewsViewModel : ViewModel() {
    private val newsRepository = NewsRepository.getInstance()
    // 对外暴露不可变的LiveData,避免UI层直接修改数据
    val newsLiveData: LiveData<MutableList<Item>> = newsRepository.news

    init {
        // 仅当数据为空时触发下载,避免重复请求
        if (newsLiveData.value.isNullOrEmpty()) {
            newsRepository.fetchNews()
        }
    }
}

3. Fragment中正确绑定ViewModel与观察者

在Fragment中通过viewLifecycleOwner订阅LiveData,确保生命周期同步:

class NewsFragment : Fragment() {
    private lateinit var viewModel: NewsViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        // 通过ViewModelProvider获取ViewModel,关联当前Fragment的生命周期
        viewModel = ViewModelProvider(this)[NewsViewModel::class.java]
        
        // 订阅LiveData,用viewLifecycleOwner确保Fragment销毁时自动取消订阅
        viewModel.newsLiveData.observe(viewLifecycleOwner) { items ->
            // 在这里更新UI,比如给RecyclerView设置数据源
        }
        
        return inflater.inflate(R.layout.fragment_news, container, false)
    }
}

总结

  • 永远用val声明LiveData对象,防止被意外替换
  • 避免依赖伴生对象的静态单例,改用更可靠的单例实现(或依赖注入框架如Hilt)
  • 必须通过ViewModelProvider获取ViewModel,保证生命周期一致性
  • 更新LiveData时修改内部列表而非替换LiveData,确保观察者能收到正确的更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 16:35:50