Android 弹出片段后的多个LiveData观察者 问题

Android 弹出片段后的多个LiveData观察者 问题,android,android-fragments,kotlin,android-navigation,android-livedata,Android,Android Fragments,Kotlin,Android Navigation,Android Livedata,摘要:导航到新片段、弹出新片段并返回原始片段后,片段中会触发多个LiveData观察者 详细信息:该体系结构由MainActivity组成,它承载一个HomeFragment,作为MainActivity的导航图中的起始目标。在HomeFragment中是一个通过编程方式膨胀的PriceGraphFragment。HomeFragment正在使用导航组件启动新的子片段ProfileFragment。按back键时,弹出ProfileFragment,应用程序返回承载PriceGraphFragm

摘要:导航到新片段、弹出新片段并返回原始片段后,片段中会触发多个LiveData观察者

详细信息:该体系结构由MainActivity组成,它承载一个HomeFragment,作为MainActivity的导航图中的起始目标。在HomeFragment中是一个通过编程方式膨胀的PriceGraphFragmentHomeFragment正在使用导航组件启动新的子片段ProfileFragment。按back键时,弹出ProfileFragment,应用程序返回承载PriceGraphFragment的HomeFragment。PriceGraphFragment是多次调用观察者的地方

我正在记录观察者发出的HashMap的hashcode,当我转到profile片段、弹出profile片段并返回price片段时,它显示了2个唯一的hashcode。这与我在旋转屏幕而不启动概要文件片段时从HashMap中看到的hashcode相反

实施
  • 用于在HomeFragment中启动新ProfileFragment的导航组件

    view.setOnClickListener(Navigation.createNavigationClickListener(
    R.id.action_homeFragment_to_profileFragment,null))

  • 视图模型在片段中创建(PriceGraphFragment)。ViewModel已被记录,并且具有多个观察者的数据在ViewModel中只初始化了一次数据

    override-fun-onCreate(savedInstanceState:Bundle?){
    super.onCreate(savedInstanceState)
    priceViewModel=ViewModelProviders.of(this.get)(PriceDataViewModel::class.java)
    }

  • 在原始片段(PriceGraphFragment)中侦听来自视图模型的数据。这会被多次调用,但是在加载片段时,它只会有一个观察者

    priceViewModel.graphLiveData.observe(
    观察者{priceGraphDataMap:HashMap?->
    //这被多次调用。
    })

  • 尝试的解决方案
  • onCreate()方法中创建片段的视图模型。
    priceViewModel=ViewModelProviders.of(this).get(PriceDataViewModel::class.java)
  • 使用片段的活动和子片段的父片段创建ViewModel。
    priceViewModel=ViewModelProviders.of(activity!!).get(PriceDataViewModel::class.java)

    priceViewModel=ViewModelProviders.of(parentFragment!!).get(PriceDataViewModel::class.java)

  • 将创建观察者的方法移动到片段的onCreate()onActivityCreated()方法

  • 在方法
    中为生命周期所有者使用
    viewLifecycleOwner
    而不是
    this
    (@NonNull LifecycleOwner owner,@NonNull Observer这基本上是体系结构中的一个bug。您可以阅读更多关于它的信息。您可以使用
    getViewLifecycleOwner
    来解决它,而不是在
    Observer
    中使用它

    像这样:

    mViewModel.methodToObserve().observe(getViewLifecycleOwner(), new Observer<Type>() {
            @Override
            public void onChanged(@Nullable Type variable) {
    
    mViewModel.methodToObserve().observe(getViewLifecycleOwner(),new Observer()){
    @凌驾
    公共void onChanged(@Nullable类型变量){
    

    并将此代码放入
    onActivityCreated()
    中,因为使用
    getViewLifecycleOwner
    需要一个视图。

    首先,感谢所有在此发布的人。在过去5天中,您的建议和指针帮助我解决了此错误,因为涉及多个问题

    解决的问题
  • 在父片段(HomeFragment)中正确创建嵌套片段
  • 之前:

    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()
        }
    ...
    }
    
    之后:

    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()
    中创建ViewModels,而不是为父片段和子片段创建
    onCreateView()

  • 正在初始化
    onCreate()
    中的子片段(PriceFragment)的数据请求(Firebase Firestore查询)数据,而不是
    onViewCreated()
    ,但仅当saveInstanceStatenull时,才会执行此操作

  • 非因素 有人建议了几个项目,但结果证明对解决此错误没有影响

  • onActivityCreated()
    中创建观察者。我将我的观察者保存在子片段(PriceFragment)的
    onViewCreated()

  • 在创建过程中使用
    viewLifecycleOwner
    。我以前使用过子片段(PriceFragment)的
    这个
    。尽管
    viewLifecycleOwner
    不会影响这个bug,但总体来说它似乎是最佳实践,所以我保留了这个新的实现


  • 最好在
    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
    仅获取数据