
みなさまこんにちは〜!
メモリアインクのすだです。
今回はViewModelへのDIにおいて、FactoryまたはHilt(DIライブラリ)を使うやり方をわかりやすく解説していきます。
この記事を読んでわかること…
・ViewModelにおけるFactoryとは?
・Hiltを使ってViewModelにDI(依存性注入)する方法
DI(依存性注入)の基本やViewModelついては、それぞれ以下の記事で解説しておりますので、ぜひ一緒にご覧ください。
とは?基本的な考え方と必要性についてわかりやすく解説!-300x158.png)
とは?基本的な考え方と必要性についてわかりやすく解説!-300x158.png)


環境
- Kotlin (ver 1.9.0)
- Android Studio (Giraffe | 2022.3.1 Patch 3)
ViewModelに依存性を注入したい理由とは
Androidアプリ開発で ViewModel
は、UIに表示するデータの管理役です。
しかし、以下のような処理をViewModelが直接やるのは望ましくありません。
- ネットワーク通信(API呼び出し)
- データベースアクセス
- 設定やログの処理
こういった処理は Repository
などの別のクラスに分けて、
ViewModelには「UIロジックだけを任せる」ようにするのが理想です。
そのために必要なのが、「依存性注入(DI)」です。
ViewModelにおける依存性注入の方法2つ解説
ViewModelに依存(例:Repository)を注入する方法は大きく分けて 2通り あります。
① Factoryを使った手動DI(自作のFactoryで依存を渡す)
② Hiltを使う(DIライブラリに依存の生成と注入を任せる)
それぞれ順番に説明していきます。
ViewModelFactory を使った手動DIの書き方
プログラミングにおける Factory(ファクトリー) とは、オブジェクトの生成方法(作り方)を定義するクラスや関数のことです。
特定のクラスを生成するために必要な処理や引数をひとまとめにしておくことで、
呼び出し側は その作り方を意識することなく、必要なインスタンスを取得できるようになります。
引数なしの ViewModel の場合
class UserViewModel : ViewModel() {
fun getUserName(): String = "山田太郎"
}
このような引数のない ViewModel であれば、以下のように ViewModelProvider
を使ってインスタンスを取得できます。
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this)[UserViewModel::class.java]
println(viewModel.getUserName())
}
}
引数ありの ViewModel の場合
class UserViewModel(private val repository: UserRepository) : ViewModel() {
fun getUserName(): String = repository.fetchUser()
}
一方 このように引数があるViewModel の場合、ViewModelProvider(this)
では直接インスタンスを生成できません。
なぜならViewModelProvider
は デフォルトでは引数なしのコンストラクタしか扱えないためです。
そのため、引数ありの ViewModel を使う場合は、
専用の Factory クラスを作成し、それを ViewModelProvider
に渡す必要があります。
class UserViewModelFactory(
private val repository: UserRepository
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return UserViewModel(repository) as T
}
}
create()
は、ViewModelProvider
によって自動的に呼ばれる関数です。modelClass
には、呼び出し元で指定された ViewModel のクラス型が渡されます(この例では UserViewModel::class.java
)。
※ as T
は、「UserViewModel は T 型(=ViewModelのサブクラス)として返します」と明示している、型キャストの記述
→つまりは、この関数の中で UserViewModel(repository)
を生成して返しています。
そして呼び出し側で以下のように書きます。
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val factory = UserViewModelFactory(UserRepository())
viewModel = ViewModelProvider(this, factory).get(UserViewModel::class.java)
println(viewModel.getUserName())
}
}
UserViewModelFactory
のインスタンスを生成して、ViewModelProvider
の第二引数に渡すことで、ViewModelProvider
は その Factory の中で定義された処理に従って ViewModel を生成します。
つまり、ViewModelProvider
は ViewModelの引数を自動的に把握しているわけではなく、
「Factoryがどうやって作るか」を実行するだけとなります。
これがFactoryを使ったViewModelの手動DIです。
Hiltを使った自動DIの書き方
一方、Hilt(DIライブラリ)を使用すると、Hiltが内部でFactoryの機能を実行してくれます。
そのため、Factoryクラスを作ることなく自動でDIの仕組みを実現することができます。
Hiltの基本的な知識については、以下の記事にまとめておりますのでぜひご覧ください。
のやり方をわかりやすく解説!-300x158.png)
のやり方をわかりやすく解説!-300x158.png)
まずは、Repositoryクラスのコンストラクタに@inject
を付与します。▼
class UserRepository @Inject constructor() {
fun fetchUser(): String = "Hilt太郎"
}
そして、ViewModelクラスのコンストラクタにも@inject
を付与しつつ、
クラス名上に@HiltViewModel
を付与します。▼
@HiltViewModel
class UserViewModel @Inject constructor(
private val repository: UserRepository
) : ViewModel() {
fun getUserName(): String = repository.fetchUser()
}
@HiltViewModel
は「このクラスはHiltに管理されるViewModelです」とHiltに教えるためのアノテーションです。
これを使うことで、HiltはそのViewModelに依存関係(例:Repositoryなど)を自動で注入できるようになります。
最後に、呼び出し側のコンポーネントにアノテーションをつけて(ここではActivityなので@AndroidEntryPoint
)、by viewModels
を使ってViewModelのインスタンスを取得します。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
println(viewModel.getUserName())
}
}
by viewModels()
は、Kotlinの委譲プロパティを使った構文です。
これは Android Jetpack が提供する拡張関数で、内部的には ViewModelProvider
を使ってViewModelを取得しています。
Hiltを使っている場合は、ViewModelProvider(this).get(...)
ではなく、by viewModels()
を使うことで Hilt の ViewModelFactory が正しく使われ、依存性注入が機能します。
(ViewModelProvider(this).get(...)
の書き方だと、Hilt専用の ViewModelFactory(HiltViewModelFactory)が使われない。そのためHiltが提供する依存注入が実行されず、エラーや未初期化の状態になる)
まとめ
おつかれさまでした。いかがでしたでしょうか!
ViewModelに、Repositoryなどの依存オブジェクトを渡す設計は、よくあるパターンです。
毎回Factoryクラスを作成するのが負担に感じる場合は、Hiltの導入をぜひ検討してみてください。



技術者としてのキャリアパスを次のレベルへと進めたい皆様、
<未経験からIT・Webエンジニアを目指すなら【ユニゾンキャリア】>
自分の市場価値をさらに向上させてみませんか?
それではまた次回の記事でお会いしましょう!
コメント