Android / Programming

Hiltを使って動的な値を渡しつつDIしたい!【短文解説・コピペOK】

普段のHiltでのDIは下記のように書きますが、これだと動的な値を渡すことができません
今回は、実行時に動的に設定される値をコンストラクタに渡しつつDIする方法をご紹介します

@Module
@InstallIn(SingletonComponent::class)
object Class01Module {
    @Provides
    fun providesString(): String = "静的な値" // 動的な値を設定できない
}

class Class01 @Inject constructor(
    staticValue: String
) {
  ...
}

やりたいこと

Hiltを使ったDIで引数を渡したい

注意

これから紹介する方法はViewModelには適用できません
ViewModelの方法をお探しの方はこちらを参考にされてください

準備

Hiltを使うため、こちらを参考に依存関係を記載してください

方法

全体的な流れは下記のとおりです

1. 動的な値を受け取るクラスのコンストラクタと格納するパラメーターにアノテーション
2. 専用インターフェースを作成しアノテーション
3. インターフェースに抽象メソッドを設定
4. インターフェースの実装

各説明の最後には全体像も載せています

1. 「@AssistedInject」、「@Assisted」を設定

動的な値を受け取るクラスのコンストラクタに「@AssistedInject」、動的な値を格納するパラメーターに「@Assisted」を設定します

動的な値を受け取るクラス: ClassAAA
動的な値を格納するパラメーター: dynamicValue

class ClassAAA @AssistedInject constructor(
  @Assisted dynamicValue: String,
) {
  ...
}

2. インターフェースの作成とアノテーション

動的な値を受け取るクラスを作成するためのインターフェースを作成します
インターフェースには「@AssistedFactory」を設定します

ClassAAA作成のためのインターフェース: ClassAAAFactory

@AssistedFactory
interface ClassAAAFactory {
  // 中身は次の手順で設定します
}

3. 抽象メソッドの作成

動的な値を格納するパラメーターを受け取り、そのクラスを返す抽象メソッドを作成します

動的な値を格納するパラメーター: dynamicValue
動的な値を受け取るクラス: ClassAAA

@AssistedFactory
interface ClassAAAFactory {
  fun create(dynamicValue: String): ClassAAA
}

4. インターフェースの実装

インターフェースを実装し、Hilt(Dagger)との紐づけを行います

インターフェースの実装: setupClassAAA(dynamicValue: String)

@HiltViewModel
class MainActivityViewModel @Inject constructor(
  private val serverRepository: ServerRepository,
) {
  @Inject lateinit var classAAAFactory: ClassAAAFactory
  
  private fun setupClassAAA(dynamicValue: String): ClassAAA {
    return classAAAFactory.create(dynamicValue)
  }
  
  private fun getDataFromServer() {
    viewModelScope.launch {
      try {
        val data = serverRepository.getData()
        val classAAA = setupClassAAA(data)
      } catch (e: Exception) {
        ...
      }
    }
  }
}

全体像

視線が泳ぐ場合は番号順にクラスや関数等を読み込むことをお勧めします

// ClassAAA.kt
class ClassAAA @AssistedInject constructor(
  @Assisted dynamicValue: String, // 1: dynamicValueには臨機応変に値を代入したい
) {
  ...
}

// MainActivityViewModel.kt
@HiltViewModel
class MainActivityViewModel @Inject constructor(
  private val serverRepository: ServerRepository,
) {
  @Inject lateinit var classAAAFactory: ClassAAAFactory
  
  // 4: インターフェースの実装
  private fun setupClassAAA(dynamicValue: String): ClassAAA {
    return classAAAFactory.create(dynamicValue)
  }
  
  private fun getDataFromServer() {
    viewModelScope.launch {
      try {
        val data = serverRepository.getData() // 5: サーバーからStringを取得
        val classAAA = setupClassAAA(data) // 6: ClassAAAを動的値dataで作成
      } catch (e: Exception) {
        ...
      }
    }
  }
}

@AssistedFactory
interface ClassAAAFactory {
  // 2: 最終目的はClassAAAの作成のため返却型はClassAAA
  // 3: パラメーターにClassAAAの「@Assisted」から下をコピペ
  fun create(dynamicValue: String): ClassAAA
}

おわりに

アノテーションにもある Assisted Injection とは、依存関係の注入(DI)のパターンのようです
今回の紹介内容は公式を参考にしていますので、こちらもご覧ください

アノテーションについては過去の記事で紹介していますので、お時間ありましたら見てやってください

疲れてきたり、難しい内容だと、目が泳いだり、同じ行を何回も読んだりするタイプです

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です