【发布时间】:2018-08-17 09:35:37
【问题描述】:
问题
总结:在导航到新 Fragment、弹出新 Fragment 并返回原始片段。
详细信息:该架构由 MainActivity 组成,其中承载一个 HomeFragment 作为 MainActivity 的 开始目的地 strong>导航图。 HomeFragment 内部是一个以编程方式膨胀的 PriceGraphFragment。 HomeFragment 正在使用导航组件来启动一个新的子 Fragment ProfileFragment。按下后,ProfileFragment 被弹出,应用程序返回到承载 PriceGraphFragment 的 HomeFragment。 PriceGraphFragment 是 Observer 被多次调用的地方。
我正在记录观察者发出的 HashMap 的哈希码,当我转到配置文件片段、弹出配置文件片段并返回价格片段时,它显示了 2 个唯一的哈希码。这与我在不启动配置文件片段的情况下旋转屏幕时从 HashMap 看到的一个哈希码相反。
实施
-
在 HomeFragment 中启动新 ProfileFragment 的导航组件。
view.setOnClickListener(Navigation.createNavigateOnClickListener( R.id.action_homeFragment_to_profileFragment, null)) -
ViewModel 在 Fragment (PriceGraphFragment) 中创建。 ViewModel 已经被记录,拥有多个 Observer 的数据只有在 ViewModel 中初始化过一次。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.java) } -
在原始 Fragment (PriceGraphFragment) 中侦听来自 ViewModel 的数据。这会被多次调用,但是在加载 Fragment 时它只需要一个 Observer。
priceViewModel.graphLiveData.observe( this, Observer { priceGraphDataMap: HashMap<Exchange, PriceGraphLiveData>? -> // This is being called multiple times. })
尝试的解决方案
- 在 onCreate() 方法中创建 Fragment 的 ViewModel。
priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.java) -
使用 Fragment 的 Activity 和子 Fragment 的父 Fragment 创建 ViewModel。
priceViewModel = ViewModelProviders.of(activity!!).get(PriceDataViewModel::class.java)priceViewModel = ViewModelProviders.of(parentFragment!!).get(PriceDataViewModel::class.java) - 将创建 Observer 的方法移至 Fragment 的 onCreate() 和 onActivityCreated() 方法。
- 在方法
observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)中对LifecycleOwner 使用viewLifecycleOwner而不是this。 - 存储在 ViewModel 中显示为重复的 HashMap 数据,而不是 Fragment。
- 使用
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
后续步骤
- 也许该问题与在 ParentFragment 的 (HomeFragment 中创建嵌套的 ChildFragment (PriceGraphFragment) 有关>)
onViewCreated()?
父片段
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 对象。
【问题讨论】:
-
关于 1:为什么要将 View 引用存储在 ViewModel 中? ViewModel 不能有任何对 View 的引用。如果您使用数据绑定,您可以在创建视图时通过代码设置点击侦听器,或者在 ViewModel 中放置一个返回点击侦听器的方法并绑定到布局中的按钮。
-
关于 3:你在哪里注册你的观察者?确保它在
onActivityCreated()中完成,并使用viewLifeCycleOwner作为生命周期而不是this。如果在那之后观察者仍然被多次调用,那可能只是因为你的LiveData被多次更新了。 -
只有在没有
savedInstanceState并且您在FragmentTransaction上使用add而不是replace时,您才调用PriceGraphFragment.newInstance()- 这有什么原因吗?您是否还检查过是否每次返回ParentFragment时都不会调用它,因为如果是这样,您基本上只是每次都在旧的PriceGraphFragment之上添加一个新的PriceGraphFragment,这就是为什么您要多次调用观察者的原因,因为所有PriceGraphFragment-instances 都在观察相同的 LiveData,因此发送了多个信号。 -
要添加子片段,您需要使用 childFragmentManager,而不是 fragmentManager
-
如果可能的话,您能否在 Github 上提供一个小型工作项目来解决这个问题?当始终存在多个小问题时,很难弄清楚问题。我的猜测是你有一些东西坚持
ViewModel,然后间接地坚持Fragment,当片段的新实例出现并在你的ViewModel上调用observe时,它也会调用回到原来的ViewModel。
标签: android android-fragments kotlin android-navigation android-livedata