问题
摘要:多个LiveData观察者在导航到新片段、弹出新片段并返回到原始片段之后在片段中被触发。
详细信息:架构由以下部分组成MainActivity它承载了一个HomeFragment作为起始目的地在MainActivity的导航图。在HomeFragment中是以编程方式膨胀的PriceGraphFragment。HomeFragment正在使用导航组件启动新的子片段ProfileFragment。按下back键时,ProfileFragment会弹出,应用程序会返回到托管PriceGraphFragment的HomeFragment。PriceGraphFragment是观察者被多次调用的地方。
我正在记录观察者发出的HashMap的哈希码,当我转到配置文件片段、弹出配置文件片段并返回到价格片段时,它显示了两个唯一的哈希码。这与我在不启动配置文件片段的情况下旋转屏幕时从HashMap中看到的散列代码相反。
实现
view.setOnClickListener(Navigation.createNavigateOnClickListener( R.id.action_homeFragment_to_profileFragment, null))
2. ViewModel在片段中创建(PriceGraphFragment)。ViewModel已被记录,并且具有多个观察者的数据仅在ViewModel中初始化一次。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.java) }
3. 侦听来自ViewModel在原始片段(PriceGraphFragment)中。它被多次调用,但是当片段被加载时,它应该只有一个观察者。
priceViewModel.graphLiveData.observe( this, Observer { priceGraphDataMap: HashMap? -> // This is being called multiple times. })
尝试的解决方案
1. 创建片段%sViewModel在onCreate()方法。
priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.java)
2. 使用片段的活动和子片段的父片段创建ViewModel。
priceViewModel = ViewModelProviders.of(activity!!).get(PriceDataViewModel::class.java)priceViewModel = ViewModelProviders.of(parentFragment!!).get(PriceDataViewModel::class.java)
3. 创建对片段的观察者的移动方法onCreate()和onActivityCreated()方法。
4. 使用viewLifecycleOwner而不是this对于LifecycleOwner在该方法中observe(@NonNull LifecycleOwner owner, @NonNull Observer observer)。
5. 在与片段相反的ViewModel中存储显示为重复的HashMap数据。
6. 方法启动该子片段。ChildFragmentManager和SupportFragmentManager(在活动级别上)。
相似的问题和建议的解决方案
https://github.com/googlesamples/android-architecture-components/issues/47
https://medium.com/@BladeCoder/architecture-components-pitfalls-part-1-9300dd969808
https://plus.google.com/109072532559844610756/posts/Mn9SpcA5cHz
下一步
这个问题可能与创建嵌套的ChildFragment(PriceGraphFragment)中ParentFragment%s(HomeFragment)onViewCreated()?
ParentFragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
user = viewModel.getCurrentUser()
if (savedInstanceState == null) {
fragmentManager
?.beginTransaction()
?.replace(binding.priceDataContainer.id,
PriceGraphFragment.newInstance())
?.commit()
}测试用RxJava可观察对象替换LiveData对象。
发布于 2018-08-22 14:59:37
首先,感谢所有在这里发帖的人。这是你的建议和指针的组合,帮助我在过去的5天里解决了这个bug,因为涉及到多个问题。
已解决的问题
之前:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
if (savedInstanceState == null) {
fragmentManager
?.beginTransaction()
?.add(binding.priceDataContainer.id, PriceGraphFragment.newInstance())
?.commit()
fragmentManager
?.beginTransaction()
?.add(binding.contentFeedContainer.id, ContentFeedFragment.newInstance())
?.commit()
}
...
}之后:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null
&& childFragmentManager.findFragmentByTag(PRICEGRAPH_FRAGMENT_TAG) == null
&& childFragmentManager.findFragmentByTag(CONTENTFEED_FRAGMENT_TAG) == null) {
childFragmentManager.beginTransaction()
.replace(priceDataContainer.id, PriceGraphFragment.newInstance(),
PRICEGRAPH_FRAGMENT_TAG)
.commit()
childFragmentManager.beginTransaction()
.replace(contentFeedContainer.id, ContentFeedFragment.newInstance(),
CONTENTFEED_FRAGMENT_TAG)
.commit()
}
...
}onCreate()与之相对的是onCreateView()父片段和子片段。onCreate()而不是onViewCreated()但仍然只在以下情况下才这样做saveInstanceState是空。非因素
有几个项目被建议,但事实证明对解决这个bug没有影响。
onActivityCreated()。我把我的留在里面onViewCreated()子片段(PriceFragment)的。viewLifecycleOwner在观察者创造。我使用的是子片段(PriceFragment)的this之前。即使viewLifecycleOwner不会影响这个bug,这似乎是总体上的最佳实践,所以我保留了这个新的实现。发布于 2018-08-21 02:13:16
这基本上是架构中的一个bug。你可以阅读更多关于它的内容here..。您可以使用以下命令来解决此问题getViewLifecycleOwner而不是在observer。
如下所示:
mViewModel.methodToObserve().observe(getViewLifecycleOwner(), new Observer() {
@Override
public void onChanged(@Nullable Type variable) {并将此代码放入onActivityCreated()因为使用getViewLifecycleOwner需要一个视图。
发布于 2021-02-26 10:32:44
最好初始化视图模型并在中观察实时数据对象onCreate。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(MyFragmentViewModel::class.java)
// 'viewLifecycleOwner' is not available here, so use 'this'
viewModel.myLiveData.observe(this) {
// Do something
}
}但是,无论您在何处初始化视图模型,无论是在onCreate或者onViewCreated,它仍然会为您提供相同的视图模型对象,因为它在片段的生命周期中只创建了一次。
最重要的部分是观察onCreate。因为onCreate仅在创建片段时调用,则调用observe只有一次。
onViewCreated无论是在创建片段时还是从后台堆栈取回片段时(在弹出其上的片段之后),都会调用。如果您在onViewCreated它将在从后台堆栈返回时立即从上一次调用中获取您的实时数据所持有的现有数据。
取而代之的是,使用onViewCreated仅用于从视图模型获取数据。因此,每当片段出现时,无论是在创建时还是从后台堆栈返回时,它都会获取最新的数据。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.fetchData()
...
}https://stackoverflow.com/questions/51892478
复制相似问题