LiveData Room进入后台时丢失数据

时间:2019-05-06 10:53:51

标签: android android-room android-architecture-components android-livedata

我有一个带有一个选项卡的ViewPager,该选项卡通过带有LiveData对象的ViewModel通过Room检索数据。

当我切换到任何选项卡并返回到该选项卡都显示项目列表时,一切正常,但是从后台进入时我不知道为什么,但是我收到的列表较少。

例如我第一次进入“列表片段”。
我从5个不同的实体中检索数据,并显示了5个列表项

转到背景并返回“列表片段”
4个列表有数据,但1个为空

转到背景并返回“列表片段”
3个列表有数据,但2个为空

...直到完全为空,然后切换选项卡,并充分填充列表

感谢您的帮助。

ViewModel

deferred


存储库getAllText()返回tagsDao.getTextList()
,Dao是:

class TagsViewModel(application: Application) : AndroidViewModel(application) {

private var repository: TagsRepository = TagsRepository(application)

private var listText: LiveData<List<Text>> = repository.getAllText()

private var listUrl: LiveData<List<Url>> = repository.getAllUrl()

private var listEmail: LiveData<List<Email>> = repository.getAllEmail()

private var listPhone: LiveData<List<Phone>> = repository.getAllPhone()

private var listLauncher: LiveData<List<Launcher>> = repository.getAllLauncher()



fun getData(): List<BaseTag>{
    var allData= mutableListOf<BaseTag>()

    if (listText.value != null){
        listText.value!!.forEach {
            allData.add(it)
        }
    }

    if (listUrl.value != null){
        listUrl.value!!.forEach {
            allData.add(it)
        }
    }

    if (listEmail.value != null){
        listEmail.value!!.forEach {
            allData.add(it)
        }
    }

    if (listPhone.value != null){
        listPhone.value!!.forEach {
            allData.add(it)
        }
    }

    if (listLauncher.value != null){
        listLauncher.value!!.forEach {
            allData.add(it)
        }
    }

   return allData
}


//-------TEXT--------
fun insertText(text: Text) {
    repository.insertText(text)
}

fun deleteTextByID(id: Int) {
    repository.deleteTextByID(id)
}

fun getAllText(): LiveData<List<Text>> {
    return listText
}

fun deleteAllText() {
    repository.deleteAllText()
}

编辑

我将viewPager更改为不再重新创建片段,在onViewCreated中观察LiveData,清除onPause中的列表,并再次在onResume中设置数据,并添加viewLyfeCycleOwner

@Query("SELECT * FROM Text")
    fun getTextList(): LiveData<List<Text>>

,它的工作原理与以前相同,当我从onResume中的背景回来时,viewModel总是返回的列表少了一点,而这完全是随机的,有时是textList,有时是emailList ...为什么它丢失了数据?我不知道为什么。

编辑2

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    v =  inflater.inflate(R.layout.fragment_my_tags, container, false)
    return v
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    setupUI()
}

private fun setupUI(){
    val recyclerView = v.findViewById<RecyclerView>(R.id.recycler_view)
    recyclerView.layoutManager = LinearLayoutManager(context)
    recyclerView.setHasFixedSize(true)
    recyclerView.adapter = adapter
    tagsViewModel = ViewModelProviders.of(this).get(TagsViewModel::class.java)

    getTags()

    (activity as MainActivity).mToolbar.onMenuItemClick {
        when (it!!.itemId){
            R.id.btnDeleteAll -> toast("sort")
            R.id.btnOrderBy -> tagsViewModel.deleteAll()
        }
    }
}

private fun getTags(){
    tagsViewModel.getAllText().observe(viewLifecycleOwner,
            Observer<List<Text>> { t -> adapter.setTags(t!!) })
    tagsViewModel.getAllEmail().observe(viewLifecycleOwner,
            Observer<List<Email>> { t -> adapter.setTags(t!!) })
    tagsViewModel.getAllUrl().observe(viewLifecycleOwner,
            Observer<List<Url>> { t -> adapter.setTags(t!!) })
    tagsViewModel.getAllPhone().observe(viewLifecycleOwner,
            Observer<List<Phone>> { t -> adapter.setTags(t!!) })
    tagsViewModel.getAllLauncher().observe(viewLifecycleOwner,
            Observer<List<Launcher>> { t -> adapter.setTags(t!!) })
}

override fun onPause() {

    adapter.clear()
    super.onPause()
}

override fun onResume() {
    adapter.setTags(tagsViewModel.getData())
    super.onResume()
}

1 个答案:

答案 0 :(得分:1)

虽然我不确定为什么片段会以这种方式运行-可能需要看一下适配器的实现-但很少有事情引起我的注意:


observeonResume中,但未在onPause中清除

即使适配器在onPause状态下被“清除”,观察者也不会。这意味着,每当片段到达背景然后再到前景时,该片段都会创建一组新的观察者集,而先前的观察者仍然活着并观察相同的LiveData,因此由于这些重复的观察者而使得适配器的可预测性降低。 / p>


在观察this时使用LiveData

LiveData对象进入DESTROYED状态时,

LifeCycle个观察者将被取消订阅。这意味着为了避免观察者重复,应该在CREATED状态之前订阅观察者,并且onCreate方法是ActivityFragment。如果您不喜欢这种行为,则需要传入另一个LifeCycleOwner对象。

1。使用getViewLifecycleOwner()

fragment.onCreate中观察是非常不切实际的,因为片段的视图甚至还没有准备好。因此,Android具有一种称为getViewLifecycleOwner()的便捷方法,可以在DESTROYED阶段向onDestroyView()发出信号,以便Fragment可以在onCreateView()阶段进行观察。您可以像这样使用它:

override fun onCreateView(...): View {
    ...
    getTags()
    ...
    return view
}

private fun getTags(){
    tagsViewModel.getAllText().observe(getViewLifecycleOwner(),
            Observer<List<Text>> { t -> adapter.setTags(t!!) })
    tagsViewModel.getAllEmail().observe(getViewLifecycleOwner(),
            Observer<List<Email>> { t -> adapter.setTags(t!!) })
    tagsViewModel.getAllUrl().observe(getViewLifecycleOwner(),
            Observer<List<Url>> { t -> adapter.setTags(t!!) })
    tagsViewModel.getAllPhone().observe(getViewLifecycleOwner(),
            Observer<List<Phone>> { t -> adapter.setTags(t!!) })
    tagsViewModel.getAllLauncher().observe(getViewLifecycleOwner(),
            Observer<List<Launcher>> { t -> adapter.setTags(t!!) })
}

2。使用自定义LifeCycleOwner

实施自定义LifeCycleOwner。我认为这个article可以提供帮助。


Fragment个生命周期,位于ViewPager

取决于ViewPager的实现,仅通过切换选项卡,Fragment不会进入onPause-> onResume状态。同样,标签之间的距离也很重要。例如,如果您切换到Fragment旁边的标签,则可能不会“暂停”。尝试记录片段状态,以查看切换选项卡如何影响其生命周期。