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

Android屏幕旋转时Fragment通过ViewModel填充的RecyclerView数据丢失如何解决

Android屏幕旋转后RecyclerView数据丢失解决方案

问题核心原因

你遇到的问题本质是Event包装类的一次性消费特性和配置变更后Fragment重建的逻辑冲突导致的:

  • Event类的设计逻辑是单消费者一次性消费,首次Fragment拿到数据消费后,Event内部的hasBeenHandled标记会被置为true
  • 屏幕旋转时Fragment销毁重建,新的Fragment实例重新订阅LiveData时,LiveData中存储的是已经被消费过的Event,EventObserver不会再触发回调,导致RecyclerView无数据填充
  • 额外不合理逻辑:你在Observer回调中才初始化Adapter、配置RecyclerView的LayoutManager,每次收到数据都重复执行初始化操作,也会增加不必要的性能损耗

修复步骤

1. ViewModel层新增缓存字段,持久化存储图片列表

在ViewModel中额外存储一份全量的图片列表,生命周期和ViewModel绑定,不会随屏幕旋转销毁:

@HiltViewModel
class ImagePickerViewModel @Inject constructor(
    private val imageListUseCase: ImageListUseCase,
    private val responseManager: ResponseManager
) : BaseViewModel() {

    // 新增:缓存最新的图片列表
    private var currentImageList: ArrayList<Image> = arrayListOf()
    private val _observeImageListData = MutableLiveData<Event<ArrayList<Image>>>()

    init {
        getImageList(Constants.KEY_WORD)
    }

    private fun getImageList(keyWord: String) {
        responseManager.loading()
        val disposable = imageListUseCase.execute(keyWord, { success ->
            responseManager.hideLoading()
            // 先更新缓存再发送事件
            currentImageList = success
            _observeImageListData.value = Event(success)
        }, { error ->
            responseManager.failed(error)
        })
        compositeDisposable.add(disposable)
    }

    fun filterSearchKeyWord(filteredKeyWord: String) {
        if(filteredKeyWord.isNotEmpty())
            getImageList(filteredKeyWord)
    }

    // 新增:对外暴露缓存的图片列表,供Fragment重建时直接读取
    fun getCachedImageList(): ArrayList<Image> = currentImageList

    val observeImageListData: LiveData<Event<ArrayList<Image>>>
        get() = _observeImageListData
}

2. Fragment层调整初始化逻辑,提前配置RecyclerView

把RecyclerView、Adapter的初始化逻辑提前到View创建阶段,重建时优先读取ViewModel的缓存填充列表:

@AndroidEntryPoint
class ImagePickerFragment : Fragment() {

    private lateinit var imagePickerBinding: FragmentImagePickerBinding
    private val imagePickerViewModel: ImagePickerViewModel by activityViewModels()
    private lateinit var imagePickerAdapter: ImagePickerAdapter

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        imagePickerBinding = FragmentImagePickerBinding.inflate(inflater, container, false)
        // 先初始化RecyclerView相关配置
        initRecyclerView()
        observeImageList()
        observeSearchText()
        return imagePickerBinding.root
    }

    private fun initRecyclerView() {
        // 初始化Adapter时优先读取ViewModel缓存,旋转重建时直接拿到历史数据
        imagePickerAdapter = ImagePickerAdapter(imagePickerViewModel.getCachedImageList(), imagePickerViewModel)
        imagePickerBinding.apply {
            rvImageList.setHasFixedSize(true)
            rvImageList.layoutManager = GridLayoutManager(requireContext(),2)
            rvImageList.adapter = imagePickerAdapter
        }
    }

    private fun observeImageList() {
        imagePickerViewModel.observeImageListData.observe(
            viewLifecycleOwner,
            EventObserver { imageList ->
                // 仅更新Adapter数据,不重复执行初始化逻辑
                imagePickerAdapter.updateData(imageList)
            })
    }
}

3. 给Adapter新增数据更新方法

在ImagePickerAdapter中新增更新数据的方法,避免每次都重新创建Adapter实例:

class ImagePickerAdapter(
    private var dataList: ArrayList<Image>,
    private val viewModel: ImagePickerViewModel
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
    // 新增数据更新方法
    fun updateData(newList: ArrayList<Image>) {
        dataList = newList
        notifyDataSetChanged()
        // 可接入DiffUtil实现局部刷新,提升性能
    }

    // 其余原有Adapter逻辑保持不变
}

可选优化方案

图片列表属于状态类数据,本身不属于需要一次性消费的事件,你也可以直接去掉Event包装,从根源解决问题:

  • 把_observeImageListData的类型从MutableLiveData<Event<ArrayList<Image>>>改为MutableLiveData<ArrayList<Image>>
  • 去掉EventObserver,使用普通的Observer观察LiveData即可,屏幕旋转后Fragment重新订阅时,LiveData会自动回调最新的列表数据,不需要额外添加缓存字段

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 17:09:03