Dagger + ViewModelの基本編 + 実例編

この記事はDaggerとJetpackのViewModelをある程度知っている前提で進んでいきます😃

基本編

一緒にDagger + ViewModelを使うのはツラミがあります。それは、ViewModelのインスタンス生成はViewModelProviderを介して行う必要があるためです。

例えば、次のコードは間違っています。

class MainViewModel @Inject constructor(...): ViewModel()

class MainActivity {
    @Inject lateinit var viewModel: MainViewModel

    ...
}

この書き方だとMainViewModelはDagger内で自動的にインスタンス生成されてしまうので、ViewModelProviderを介してくれません。よって次のように書く必要があります。

class MainViewModel(...): ViewModel()

@Module
class MainActivityModule {
    @Provides
    fun provideMainViewModel(...) : MainViewModel {
        // ViewModelProviderを使ってインスタンスを生成する
        return ViewModelProviders.of(...).get(MainViewModel::class.java)
    }
}

class MainActivity {
    @Inject lateinit var viewModel: MainViewModel

    ...
}

@Providesを使いインスタンス生成の方法を明示的に記述します。これで、ViewModelProviderを介してMainViewModelインスタンスを生成をすることが出来ます。

また、ViewModelを直接注入せずに、ViewModelProvider.Factoryを注入し、ViewModelのインスタンス生成はActivity(or Fragment)に任せる方法があります。 このパターンのときは、activity-ktx(or fragment-ktx)に追加された拡張関数と組み合わせるといい感じに書けます。

class MainViewModel(...): ViewModel()
or
class MainViewModel @Inject constructor(...): ViewModel()

@Module
class MainActivityModule {
    @Provides
    fun provideViewModelFactory(...) : ViewModelProvider.Factory {
        // ここでMainViewModelを生成するFactoryを定義する
        return object: ViewModelProvider.Factory {
            ...
        }
    }
}

class MainActivity : AppCompatActivity() {
    // ViewModelではなく、Factoryを注入するのがポイント
    @Inject lateinit var factory: ViewModelProvider.Factory

    // MainViewModelインスタンスの生成はActivity側で行う
    // activity-ktxで定義されている拡張関数を使う
    private val viewModel: MainViewModel by viewModels { factory }

    ...
}

activity-ktxに定義されているviewModels拡張関数を使ってMainViewModelインスタンスを生成します。これでViewModelのライフサイクルを保ちつつ、Daggerで依存を解決することが出来ます。

次に、この2パターンのどちらの書き方がいいかを考えていきます。

実例編

@Providesを使うパターンと、Factoryを使うパターンは良いところ、悪いところがそれぞれあるので、好きな方を選べばいいと思います。

両アプローチともに、ソースコード、ノウハウが出ているので参考リンクを張っておきます。


plaid


Android ViewModel and FactoryProvider: good way to manage it with Dagger Multibindings


Activity-Ktx + Dagger Example


個人的には一番最後の自分のパターンを押したいところですが、上記のパターンはそれぞれメリット/デメリットがあると思うので、プロジェクトによって使い分けるのがよいと思います。

まとめ

Written by
あんどろいどでぃべろっぱぁー🍎