Android HiltをNew projectから最速セットアップ（2021/07）

# はじめに
この記事は、developer.android.comの[Hilt を使用した依存関係の注入](https://developer.android.com/training/dependency-injection/hilt-android?hl=ja)を参照してのセットアップになります。
developer.android.comの内容は古いので、daggerの公式も参照してください。

https://dagger.dev/hilt/

# 概要
AndroidでHiltを使おうと、[Hilt を使用した依存関係の注入](https://developer.android.com/training/dependency-injection/hilt-android?hl=ja) を参照したら、ドキュメントが古いためエラいハマったので、New Project→Basic Activityから最速でHiltをセットアップするための手順書になります。

特に言及のないところは公式ドキュメント通りで大丈夫かと思います。

# 環境
- Android Studio
    - 4.2.2
- 言語
    - Kotlin

## 依存関係を追加する
`classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'`ではなく、`classpath 'com.google.dagger:hilt-android-gradle-plugin:2.37'` を追加します。

kotlin_versionも、1.5.20にすると以下のようなエラーに悩まされるので、1.5.10にします。

```
public final class MainActivity extends androidx.appcompat.app.AppCompatActivity {
             ^
  Expected @AndroidEntryPoint to have a value. Did you forget to apply the Gradle Plugin? (dagger.hilt.android.plugin)
  See https://dagger.dev/hilt/gradle-setup.html
  [Hilt] Processing did not complete. See error above for details.
```

プロジェクトのルート build.gradle ファイル

```gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    ext.kotlin_version = "1.5.10"
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.2"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.37'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
```

app/build.gradle ファイルも、com.google.daggerの各種バージョンを2.37にして追加します。
プラグインの書き方は`apply`ではなく`plugins`に列挙する形になっているので、その形式に合わせます。

```gradle
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

```

```gradle
dependencies {
    // Hilt
    implementation "com.google.dagger:hilt-android:2.37"
    kapt "com.google.dagger:hilt-android-compiler:2.37"
}
```

## Hilt アプリケーション クラス

New→Kotlin Class/Fileでファイルを追加します。

```kotlin
import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class ExampleApplication: Application() {
}
```

AndroidManifest.xmlにもapplication要素にandroid:nameを追加するように編集します。

```xml
    <application
        android:name=".ExampleApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplicationHilt">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/Theme.MyApplicationHilt.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
```


### Android クラスに依存関係を注入する
公式ドキュメントでは、ExampleActivityを作成していますが、以下は最初からあるMainActivityに実装する例です。

```kotlin
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var analytics: AnalyticsAdapter
```

## Hilt バインディングを定義する
New→Kotlin Class/FileでAnalyticsAdapterファイルを追加します。

```kotlin
import javax.inject.Inject

class AnalyticsAdapter @Inject constructor(
    private val service: AnalyticsService
) {
}
```

# Hilt モジュール

## @Binds を使用してインターフェース インスタンスを注入する
New→Kotlin Class/FileでAnalyticsServiceファイルを追加します。

```kotlin
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import javax.inject.Inject

interface AnalyticsService {
    fun analyticsMethods()
}

// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
class AnalyticsServiceImpl @Inject constructor(
) : AnalyticsService {
    override fun analyticsMethods() {
        TODO("Not yet implemented")
    }
}

@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {

    @Binds
    abstract fun bindAnalyticsService(
        analyticsServiceImpl: AnalyticsServiceImpl
    ): AnalyticsService
}
```

以上でビルドが通り、MainActivityからAnalyticsAdapterとAnalyticsServiceが利用できるようになるかと思います。

# Hilt と Jetpack の統合(ViewModel & Fragment)

[Hilt と Jetpack の統合](https://developer.android.com/training/dependency-injection/hilt-jetpack?hl=ja)もドキュメントが古いので、こちらも2021/07現在の環境でセットアップします。
以下はFragmentに対してのViewModelでのセットアップになります。

## Hilt を使用して ViewModel オブジェクトを注入する

まずは、New→Fragment→Fragment (With ViewModel)で `ExampleFragment`を作っておきます。
app/build.gradleファイルは、 

- `implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'` → `implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'`
- `kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha01'` → `kapt 'androidx.hilt:hilt-compiler:1.0.0'` 


にします。

```gradle
dependencies {
    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
    // When using Kotlin.
    kapt 'androidx.hilt:hilt-compiler:1.0.0'
}
```

ViewModelInjectは`HiltViewModel`に置き換えられているので、置き換えます。
ExampleRepositoryがconstructorに必要なので用意しておきます。
ExampleRepositoryの中身の例です。

```kotlin
import javax.inject.Inject

class ExampleRepository @Inject constructor() {
    fun get(): String {
        return "get"
    }
}
```

ViewModelの例です。

```kotlin
@HiltViewModel
class ExampleViewModel @Inject constructor(
    private val repository: ExampleRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    private val _text = MutableLiveData<String>().apply {
        value = repository.get()
    }
    val text: LiveData<String> = _text
}
```

そのViewModelを使うFragmentの例です。

```kotlin
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class ExampleFragment : Fragment() {

    companion object {
        fun newInstance() = ExampleFragment()
    }

    private val viewModel: ExampleViewModel by viewModels()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        viewModel.text.observe(viewLifecycleOwner, Observer {
            Log.d("ExampleFragment",it)
        })
        return inflater.inflate(R.layout.example_fragment, container, false)
    }
}
```

以上、駆け足ですが、2021/07時点でのAndroid Hiltセットアップの例でした。