Android ViewModelとNavigation
koin-android
Gradleモジュールは、ViewModelコンポーネントを宣言し、それをAndroidコンポーネントのライフサイクルにバインドするのに役立つ、single
やfactory
を補完する新しいviewModel
DSLキーワードを導入します。viewModelOf
キーワードも利用可能で、コンストラクタを使ってViewModelを宣言できます。
val appModule = module {
// ViewModel for Detail View
viewModel { DetailViewModel(get(), get()) }
// or directly with constructor
viewModelOf(::DetailViewModel)
}
宣言されたコンポーネントは、少なくともandroid.arch.lifecycle.ViewModel
クラスを拡張する必要があります。クラスのコンストラクタをどのように注入するかを指定し、get()
関数を使って依存関係を注入できます。
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()
}
ViewModelキーは、Keyおよび/またはQualifierに基づいて計算されます。
Activity共有ViewModel
1つのViewModelインスタンスを、複数のFragmentとそのホストActivityの間で共有できます。
Fragment
で共有ViewModelを注入するには、以下を使用します。
by activityViewModel()
- 共有ViewModelインスタンスをプロパティに注入するための遅延デリゲートプロパティgetActivityViewModel()
- 共有ViewModelインスタンスを直接取得
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()) }
またはConstructor DSLを使用します。
viewModelOf(::MyStateVM)
Activity
、Fragment
でstate ViewModelを注入するには、以下を使用します。
by viewModel()
- state ViewModelインスタンスをプロパティに注入するための遅延デリゲートプロパティgetViewModel()
- state ViewModelインスタンスを直接取得
class DetailActivity : AppCompatActivity() {
// MyStateVM viewModel injected with SavedStateHandle
val myStateVM: MyStateVM by viewModel()
}
すべてのstateViewModel
関数は非推奨です。通常のviewModel
関数を使用してSavedStateHandleを注入できます。
Navigation Graph ViewModel
ViewModelインスタンスをNavigation graphにスコープすることができます。by koinNavGraphViewModel()
で取得するだけです。グラフIDが必要です。
class NavFragment : Fragment() {
val mainViewModel: NavViewModel by koinNavGraphViewModel(R.id.my_graph)
}
ViewModel Scope API
ViewModelおよびScopeで使用されるすべてのAPIを参照してください: ViewModel Scope
ViewModel Generic 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>
この関数はまだ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
)