はじめに
公式のドキュメントでは、onCreate()
内でViewModel
をリクエストすることが推奨されています。
通常は、アクティビティ オブジェクトの onCreate() メソッドが最初に呼び出されたときに、ViewModel をリクエストします。
onCreate()
でViewModel
を初期化する場合は次のようになるでしょうか。
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
...
class ExampleActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel: ExampleModel by viewModels()
setContent {
...
}
}
}
onCreate()でViewModelを初期化する場合の問題
通常の使用用途であれば、ライフサイクルに従ってonCreate()
でViewModel
を初期化すればよいのですが、もし、onResume()
やonPause()
などでViewModel
に実装されたロジックを呼び出したい場合はどうすればよいのでしょうか?
もちろん、onCreate()
内でval viewModel: ExampleViewModel by viewModels()
を実行してしまっているため、他のメソッドでは呼び出すことができません。
それでは、onCreate()
の外側でby viewModels()
を呼び出してアクティビティ内でいつでもViewModel
を使えるようにすればよいのでしょうか?
class ExampleActivity : ComponentActivity() {
val viewModel: ExampleViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
...
}
}
これはアンチパターンな気がします。公式では、onCreate()
の中でリクエストすべきと書かれているため、ライフサイクルに沿わない初期化方法はあまり良くないでしょう。
lateinitとhiltViewModel()を使って解決する
そこで、lateinit
を使ってonCreate()
の外側でViewModel
を定義し、onCreate()
で呼び出されているsetContent()
内でhiltViewModel()
を呼び出すことで安全にViewModel
を初期化する方法を考えました。
class NfcActivity : ComponentActivity() {
lateinit var pendingIntent: PendingIntent
lateinit var nfcAdapter: NfcAdapter
lateinit var viewModel: ExampleModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
setContent {
viewModel = hiltViewModel()
val uiState by viewModel.uiState.collectAsState()
...
}
}
...
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
if (intent != null && viewModel != null) {
viewModel.processIntent(intent)
}
}
}
これにより、上記のようにアクティビティに定義されたonNewIntent()
内でも安全にViewModel
のメソッドを呼び出すことができるようになりました。そして、その処理の結果によりuiState
を変更させ、コンポーザブルに表示する値等も変更できるようになります。
hiltViewModel()
などを呼び出すために必要なHilt
の設定方法などについては、こちらのnowinandroid
のリポジトリを参照してください。