ViewModelの引数について

この宣言:

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)

のように書くと、

  • ChildParentを継承する
  • 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など)でContextDataStoreを渡すことが多いため、

AndroidViewModel

を使う機会は以前より減っています。


まとめると、

class SettingsViewModel(application: Application)
    : AndroidViewModel(application)

  1. application: Application でApplicationインスタンスを受け取る
  2. AndroidViewModel(application) で親クラスにそのApplicationを渡す
  3. 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 で生成できるようになった、ということです。

タイトルとURLをコピーしました