Android ViewModelとナビゲーション
koin-android Gradleモジュールは、singleやfactoryを補完する新しいviewModel DSLキーワードを導入し、ViewModelコンポーネントを宣言し、Androidコンポーネントのライフサイクルにバインドするのに役立ちます。viewModelOfキーワードも利用でき、コンストラクタでViewModelを宣言できます。
val appModule = module {
// ViewModel for Detail View
viewModel { DetailViewModel(get(), get()) }
// or directly with constructor
viewModelOf(::DetailViewModel)
}宣言するコンポーネントは、少なくともandroid.arch.lifecycle.ViewModelクラスを継承している必要があります。クラスのコンストラクタをどのようにインジェクトするかを指定でき、get()関数を使用して依存関係をインジェクトできます。
INFO
viewModel/viewModelOfキーワードは、ViewModelのファクトリインスタンスを宣言するのに役立ちます。このインスタンスは内部のViewModelFactoryによって処理され、必要に応じてViewModelインスタンスを再アタッチします。また、パラメータをインジェクトすることも可能です。
ViewModelのインジェクション
Activity、Fragment、ServiceでViewModelをインジェクトするには、以下を使用します:
by viewModel()- プロパティにViewModelをインジェクトするための遅延デリゲートプロパティgetViewModel()- ViewModelインスタンスを直接取得
class DetailActivity : AppCompatActivity() {
// Lazy inject ViewModel
val detailViewModel: DetailViewModel by viewModel()
}NOTE
ViewModelのキーは、Keyおよび/またはQualifierに対して計算されます。
Activityでの共有ViewModel
1つのViewModelインスタンスは、FragmentとそのホストActivity間で共有できます。
Fragmentで共有ViewModelをインジェクトするには、以下を使用します:
by activityViewModel()- 共有ViewModelインスタンスをプロパティにインジェクトするための遅延デリゲートプロパティgetActivityViewModel()- 共有ViewModelインスタンスを直接取得
NOTE
sharedViewModelはactivityViewModel()関数に置き換えられ、非推奨となりました。後者の名前はより明示的です。
ViewModelは一度だけ宣言してください:
val weatherAppModule = module {
// WeatherViewModel declaration for Weather View components
viewModel { WeatherViewModel(get(), get()) }
}注: ViewModelのQualifierは、ViewModelのTagとして扱われます。
そして、ActivityとFragmentで再利用します:
class WeatherActivity : AppCompatActivity() {
/*
* Declare WeatherViewModel with Koin and allow constructor dependency injection
*/
private val weatherViewModel by viewModel<WeatherViewModel>()
}
class WeatherHeaderFragment : Fragment() {
/*
* Declare shared WeatherViewModel with WeatherActivity
*/
private val weatherViewModel by activityViewModel<WeatherViewModel>()
}
class WeatherListFragment : Fragment() {
/*
* Declare shared WeatherViewModel with WeatherActivity
*/
private val weatherViewModel by activityViewModel<WeatherViewModel>()
}コンストラクタへのパラメータの渡し方
viewModelキーワードAPIは、インジェクションパラメータと互換性があります。
モジュール内:
val appModule = module {
// ViewModel for Detail View with id as parameter injection
viewModel { parameters -> DetailViewModel(id = parameters.get(), get(), get()) }
// ViewModel for Detail View with id as parameter injection, resolved from graph
viewModel { DetailViewModel(get(), get(), get()) }
// or Constructor DSL
viewModelOf(::DetailViewModel)
}インジェクション呼び出し元からは:
class DetailActivity : AppCompatActivity() {
val id : String // id of the view
// Lazy inject ViewModel with id parameter
val detailViewModel: DetailViewModel by viewModel{ parametersOf(id)}
}SavedStateHandleのインジェクション (3.3.0)
ViewModelの状態を扱うために、SavedStateHandle型の新しいプロパティをコンストラクタに追加します:
class MyStateVM(val handle: SavedStateHandle, val myService : MyService) : ViewModel()Koinモジュールでは、get()またはパラメータで解決するだけです:
viewModel { MyStateVM(get(), get()) }またはコンストラクタDSLで:
viewModelOf(::MyStateVM)Activity、Fragmentで状態を持つViewModelをインジェクトするには、以下を使用します:
by viewModel()- 状態を持つViewModelインスタンスをプロパティにインジェクトするための遅延デリゲートプロパティgetViewModel()- 状態を持つViewModelインスタンスを直接取得
class DetailActivity : AppCompatActivity() {
// MyStateVM viewModel injected with SavedStateHandle
val myStateVM: MyStateVM by viewModel()
}INFO
すべてのstateViewModel関数は非推奨になりました。SavedStateHandleをインジェクトするには、通常のviewModel関数を使用するだけで済みます。
ナビゲーショングラフのViewModel
ViewModelインスタンスをナビゲーショングラフにスコープすることができます。by koinNavGraphViewModel()で取得するだけです。グラフIDが必要となります。
class NavFragment : Fragment() {
val mainViewModel: NavViewModel by koinNavGraphViewModel(R.id.my_graph)
}ViewModelスコープAPI
ViewModelとScopeに使用されるすべてのAPIを参照してください:ViewModel Scope
ViewModel汎用API
Koinは、ViewModelインスタンスを直接調整するための「内部的な」APIを提供しています。利用可能な関数は、ComponentActivityおよびFragment向けのviewModelForClassです:
ComponentActivity.viewModelForClass(
clazz: KClass<T>,
qualifier: Qualifier? = null,
owner: ViewModelStoreOwner = this,
state: BundleDefinition? = null,
key: String? = null,
parameters: ParametersDefinition? = null,
): Lazy<T>NOTE
この関数はまだstate: BundleDefinitionを使用していますが、CreationExtrasに変換されます。
どこからでも呼び出し可能なトップレベル関数にアクセスできることに注意してください:
fun <T : ViewModel> getLazyViewModelForClass(
clazz: KClass<T>,
owner: ViewModelStoreOwner,
scope: Scope = GlobalContext.get().scopeRegistry.rootScope,
qualifier: Qualifier? = null,
state: BundleDefinition? = null,
key: String? = null,
parameters: ParametersDefinition? = null,
): Lazy<T>ViewModel API - Java互換性
Java互換性を依存関係に追加する必要があります:
// Java Compatibility
implementation "io.insert-koin:koin-android-compat:$koin_version"ViewModelCompatのviewModel()またはgetViewModel()静的関数を使用することで、JavaコードベースにViewModelインスタンスをインジェクトできます:
@JvmOverloads
@JvmStatic
@MainThread
fun <T : ViewModel> getViewModel(
owner: ViewModelStoreOwner,
clazz: Class<T>,
qualifier: Qualifier? = null,
parameters: ParametersDefinition? = null
)