この宣言:
class SettingsViewModel(application: Application) : AndroidViewModel(application)
には2つの意味があります。
1. (application: Application)
これは コンストラクタ引数 です。
class SettingsViewModel(
application: Application
)
という意味で、Applicationオブジェクトを受け取ります。
AndroidのApplicationはアプリ全体で1つだけ存在するオブジェクトで、
applicationContext
に相当する情報を持っています。
例えばあなたのコードでは、
private val dataStore = application.dataStore
としてDataStoreを取得するために使っています。
2. : AndroidViewModel(application)
これは 親クラスのコンストラクタ呼び出し です。
Kotlinでは
class Child(x: Int) : Parent(x)
のように書くと、
ChildがParentを継承するParentのコンストラクタにxを渡す
という意味になります。
つまり
class SettingsViewModel(application: Application)
: AndroidViewModel(application)
は、
AndroidViewModel
のコンストラクタにapplicationを渡しているということです。
AndroidViewModelとは
通常のViewModelは
class MyViewModel : ViewModel()
のように書きます。
しかしこれだとApplicationを取得できません。
そこでAndroidは
class AndroidViewModel(application: Application)
というクラスを用意しています。
内部的には
open class AndroidViewModel(
private val application: Application
) : ViewModel()
のようなイメージです。
継承すると
getApplication()
が使えるようになります。
例えば
class SettingsViewModel(
application: Application
) : AndroidViewModel(application) {
val app = getApplication<Application>()
}
のようにアプリケーションコンテキストへアクセスできます。
なぜ普通のViewModelではなくAndroidViewModel?
あなたのコードでは
private val dataStore = application.dataStore
とApplicationが必要だからです。
もしApplicationが不要なら、
class SettingsViewModel : ViewModel()
の方がシンプルです。
最近は依存性注入(Hiltなど)でContextやDataStoreを渡すことが多いため、
AndroidViewModel
を使う機会は以前より減っています。
まとめると、
class SettingsViewModel(application: Application)
: AndroidViewModel(application)
は
application: ApplicationでApplicationインスタンスを受け取るAndroidViewModel(application)で親クラスにそのApplicationを渡す- ViewModel内でアプリケーションコンテキストを利用できるようにする
という意味です。
ファクトリー不要?
class SettingsViewModel(application: Application) : AndroidViewModel(application)のようにするとMainActivityのsetContent {}内でファクトリー無しでインスタンス出来るようになるのですか? dataStoreを引数にしたclass SettingViewModel(private val dataStoreManager: DataStoreManager) : ViewModel()では、setContent {}内でインスタンス化するときはファクトリーが必要でした。
はい、その理解で概ね正しいです。
AndroidViewModel は Application を受け取ることが前提の ViewModel なので、Activity や Compose の viewModel() は標準で用意されている AndroidViewModelFactory を使って生成できます。
例えば、
class SettingsViewModel(
application: Application
) : AndroidViewModel(application)
なら、
setContent {
val viewModel: SettingsViewModel = viewModel()
}
のように、特別な Factory を書かなくても生成できることが多いです。
一方、
class SettingsViewModel(
private val dataStoreManager: DataStoreManager
) : ViewModel()
の場合、
DataStoreManager
をどうやって作るのかを Android が知りません。
viewModel() が自動生成できるのは基本的に
- 引数なしの ViewModel
Applicationを受け取る AndroidViewModel
程度です。
そのため、
class SettingsViewModelFactory(
private val dataStoreManager: DataStoreManager
) : ViewModelProvider.Factory
を作って、
val vm: SettingsViewModel = viewModel(
factory = SettingsViewModelFactory(dataStoreManager)
)
のように Factory が必要になります。
なぜ Application だけ特別扱いなのかというと、
Activity は自分が属する Application を必ず持っているので、
application
を自動的に取得できます。
しかし、
DataStoreManager
Repository
ApiClient
Database
などの独自クラスは Android が生成方法を知らないため、Factory や Hilt などの DI が必要になります。
整理すると:
| ViewModel定義 | Factory |
|---|---|
class VM : ViewModel() | 不要 |
class VM(application: Application) : AndroidViewModel(application) | 通常不要 |
class VM(repo: Repository) : ViewModel() | 必要 |
class VM(dataStoreManager: DataStoreManager) : ViewModel() | 必要 |
Hilt使用 (@HiltViewModel) | 自前Factory不要 |
つまり、あなたが Application を引数にした形へ変更したことで、DataStoreManager を渡す必要がなくなり、Compose の viewModel() が標準の Factory で生成できるようになった、ということです。