アプリケーションでナビゲーションコンポーネントを使用しており、同じグラフにある複数のフラグメント間で共有ViewModelも使用しています。次に、このグラフスコープで this を使用してViewModelをインスタンス化します。
ご存じのとおり、断片的に onAttach
にオブジェクト(ViewModel、.. etc)を挿入する必要があります :
しかし、これを実行したい場合(onAttach
にグラフスコープを含むViewModelを注入)、このエラーが発生します。
IllegalStateException: NavController is not available before onCreate()
私がこれをどうやってできるか知っていますか?
つまり、ViewModel
にダガーProvider
またはLazy
をレイジーに提供できます。
長い説明は:
あなたの注射ポイントは正しいです。 https://dagger.dev/Android#when-to-inject
によると
DaggerActivityは、super.onCreate()を呼び出す前に、onCreate()ですぐにAndroidInjection.inject()を呼び出します。DaggerFragmentは、onAttach()でも同じことを行います。
問題は、AndroidがActivity
に付けられたFragments
とFragmentManger
を再作成するときと、NavController
を指定できます。
Activity
が付いているFragments
はOSによって破棄されます(「開発者設定」の「アクティビティを保持しない」で再現できます)Activity
に戻り、OSがActivity
の再作成を続行しますActivity
は、再作成中にsetContentView
を呼び出します。Fragments
のFragmentManager
が再接続されます。これには、Fragment#onAttach
の呼び出しが含まれます。Fragment
はFragment#onAttach
に挿入されますNavController
を提供しようとしますしかし、Activity#onCreate
がまだ完了していないため、この時点ではNavController
からActivity
を取得できません。
IllegalStateException: NavController is not available before onCreate()
私が見つけた解決策は、NavCotroller
またはNavController
に依存するもの(ViewModel
など)を注入することです。Android NavController
を使用してnavスコープのVideModels
)を遅延取得します。これは2つの方法で実行できます。
Lazy
Provided
(参照: https://proandroiddev.com/dagger-2-part-three-new-possibilities-3daff12f7ebf )
つまり、ViewModel
をFragment
に挿入するか、次のようなナビゲーターの実装を使用します。
@Inject
lateinit var viewModel: Provider<ViewModel>
次に、次のように使用します。
viewModel.get().events.observe(this) {....}
現在、ViewModel
はDaggerによって次のように提供できます。
@Provides
fun provideViewModel(
fragment: Fragment,
argumentId: Int
): CreateMyViewModel {
val viewModel: CreateMyViewModel
by fragment.navGraphViewModels(R.id.nested_graph_id)
return viewModel
}
DaggerはFragment
が挿入されたときにプロビジョニングを解決しようとしませんが、それを使用すると、競合状態が解決されます。
私は自分のviewModelsを直接使用できないのが本当に嫌で、Provider
を使用する必要がありますが、これはこの問題を解決するために私が見た唯一の回避策です。フラグメントとアクティビティの不条理なライフサイクルを追跡することは非常に難しいので、それらを非難します)。
...オブジェクト(ViewModelなど)をonAttachに挿入する必要があります...
現在、ソースコードから、_androidx.navigation
_パッケージによって提供される元のby navGraphViewModels(R.id.nav_graph)
委任プロパティを使用したそのようなインジェクションはできません
findNavController().getBackStackEntry(navGraphId)
および
public final NavController getNavController()
それは次のように述べています:
_* Returns the {@link NavController navigation controller} for this navigation Host. * This method will return null until this Host fragment's {@link #onCreate(Bundle)}
_
そしてここにいくつかの回避策があります: