web-dev-qa-db-ja.com

フラグメントをポップした後の複数のLiveDataオブザーバー

問題

概要:複数のLiveDataObserversは、新しいフラグメントにナビゲートした後、フラグメントでトリガーされ、ポップします新しいフラグメント、および元のフラグメントに戻ります。

詳細:アーキテクチャは、HomeFragmentをホストするMainActivityで構成されます開始先 MainActivityのナビゲーショングラフ。 HomeFragment内には、プログラムで膨らんだPriceGraphFragmentがあります。 HomeFragmentは、ナビゲーションコンポーネントを使用して、新しい子FragmentProfileFragmentを起動しています。バックプレスでProfileFragmentがポップされ、アプリはPriceGraphFragmentをホストしているHomeFragmentに戻ります。 PriceGraphFragmentは、オブザーバーが複数回呼び出される場所です。

オブザーバーによって発行されているHashMapのハッシュコードを記録しており、プロファイルFragmentに移動してプロファイルFragmentをポップし、価格Fragmentに戻ると、2つの一意のハッシュコードを表示しています。これは、プロファイルフラグメントを起動せずに画面を回転させたときにHashMapから表示される1つのハッシュコードとは対照的です。

実装

  1. HomeFragment内で新しいProfileFragmentを起動するナビゲーションコンポーネント。

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

  2. ViewModelFragment(PriceGraphFragment)での作成。 ViewModelがログに記録されており、複数のオブザーバーを持つデータのデータは、ViewModelで1回だけ初期化されています。

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

  3. 元のフラグメント(PriceGraphFragment)のViewModelからのデータをリッスンします。これは複数回呼び出されていますが、フラグメントがロードされるときに1つのオブザーバーのみが存在することが予想されます。

    priceViewModel.graphLiveData.observe( this, Observer { priceGraphDataMap: HashMap<Exchange, PriceGraphLiveData>? -> // This is being called multiple times. })

試みられたソリューション

  1. onCreate()メソッドでFragmentViewModelを作成します。 priceViewModel = ViewModelProviders.of(this).get(PriceDataViewModel::class.Java)
  2. Fragmentのアクティビティと子Fragmentの親Fragmentを使用してViewModelを作成します。
    priceViewModel = ViewModelProviders.of(activity!!).get(PriceDataViewModel::class.Java)

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

  3. オブザーバーを作成するメソッドをフラグメントのonCreate()およびonActivityCreated()メソッドに移動します。
  4. メソッドobserve(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)LifecycleOwnerviewLifecycleOwnerの代わりにthisを使用します。
  5. FragmentではなくViewModelに重複として表示されているHashMapデータを保存します。
  6. ChildFragmentManagerおよびSupportFragmentManager(アクティビティレベル)を使用して子フラグメントを起動します。

同様の問題と提案された解決策

次のステップ

  • おそらく、この問題は、ParentFragmentのネストされたChildFragmentPriceGraphFragment)の作成に関連しています。 HomeFragmentonViewCreated()

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()
}
  • LiveDataオブジェクトをRxJavaオブザーバブルに置き換えてテストします。
10
Adam Hurwitz

まず、ここに投稿したすべての人に感謝します。複数の問題が関係していたため、過去5日間でこのバグを解決するのに役立ったのは、あなたのアドバイスとポインターの組み合わせでした。

解決された問題

  1. 親フラグメント(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()
    }
...
}
_
  1. 親と子の両方のフラグメントに対してonCreate()ではなくonCreateView()ViewModel sを作成します。

  2. onCreate()ではなくonViewCreated()の子Fragment(PriceFragment)のデータ(Firebase Firestoreクエリ)データのリクエストを初期化していますが、saveInstanceStateが-の場合のみnull

非要因

いくつかの項目が提案されましたが、このバグの解決には影響がないことが判明しました。

  1. onActivityCreated()Observersを作成しています。私は、子Fragment(PriceFragment)のonViewCreated()に私のものを保持しています。

  2. Observer作成でviewLifecycleOwnerを使用します。以前は、子フラグメント(PriceFragment)のthisを使用していました。 viewLifecycleOwnerはこのバグに影響を与えませんが、全体としてはベストプラクティスであると思われるため、この新しい実装を維持しています。

3
Adam Hurwitz

これは基本的にアーキテクチャのバグです。詳しくは こちら をご覧ください。 getViewLifecycleOwnerでこれの代わりにobserverを使用することで解決できます。

このような:

_mViewModel.methodToObserve().observe(getViewLifecycleOwner(), new Observer<Type>() {
        @Override
        public void onChanged(@Nullable Type variable) {
_

getViewLifecycleOwnerを使用するにはビューが必要なので、このコードをonActivityCreated()に配置します。

8
KvdLingen