• トップ
  • ブログ一覧
  • Androidアプリ開発でHiltライブラリ使ってみた!
  • Androidアプリ開発でHiltライブラリ使ってみた!

    たか(エンジニア)たか(エンジニア)
    2023.11.29

    IT技術

    はじめに

    初めまして! 東京でモバイルエンジニアとして働いている たか です!

    今回初めてブログを書くということで何について書こうかな〜と考えていたところ

    そういえば開発でよく「DI」という用語をしばしば耳にするけど詳しく知らないなと思ったので

    Androidアプリ開発で使用されている「Hilt」の導入と利用方法について調べてみようと思います!


    Hiltを使用するメリット

    Androidアプリ開発で使用されるDIライブラリにはHiltとDaggerの2種類あります。
    HiltとDaggerの特徴は以下になります!

    Daggerは柔軟性が高く、細かいカスタマイズが必要な場合に適していますが、設定が複雑です。
    一方のHiltはDaggerの強力な機能を保ちながら、Androidアプリ開発におけるDIの設定と管理を簡素化したものです。

    Daggerの機能を保ちながらより簡単にDIを利用できるように設計されているのはHiltの大きなメリットと言えます!

    また、Android開発でHiltを使用することで主に4つのメリットがあります。

    1. テストの容易さ: DIを使うと、テスト中にモックやスタブなどの代替オブジェクトを注入できるため、単体テストが容易になります。
    2. 再利用性と保守性の向上: 依存関係を外部から注入することで、コードはより汎用的で再利用しやすくなります。
    3. 疎結合: 各コンポーネントが他のコンポーネントの具体的な実装に依存しないため、コンポーネント間の緩やかな結合(疎結合)が実現します。
    4. コードの可読性と管理の容易さ: 依存関係が明確になるため、コードが読みやすく、管理しやすくなります。

    今回は疎結合の向上性に注目してViewModelでReposiotryインスタンスを直接生成しているコードから依存関係を外部から注入するような処理に変更し、密結合から疎結合にしたいと思います!


    Android StudioにHiltライブラリを導入

    ではさっそくAndroid StuidoにHIltライブライを導入してみます!

    まずプロジェクトレベルのbuild.gradleにプラグインを追加します。

    1plugins {
    2  ...
    3  id("com.google.dagger.hilt.android") version "2.44" apply false
    4}

    次にGradle プラグインを適用し、モジュールレベルのbuild.gradleに依存関係を追加します。

    1plugins {
    2  kotlin("kapt")
    3  id("com.google.dagger.hilt.android")
    4}
    5
    6android {
    7  ...
    8}
    9
    10dependencies {
    11  implementation("com.google.dagger:hilt-android:2.44")
    12  kapt("com.google.dagger:hilt-android-compiler:2.44")
    13}

    最後にHiltではJava8の機能を使用しているので、Javaのバージョンを8以降に設定しましょう!
    今回はJavaVersion.VERSION_1_8を指定しています。

    1 android {
    2  ...
    3  compileOptions {
    4    sourceCompatibility = JavaVersion.VERSION_1_8
    5    targetCompatibility = JavaVersion.VERSION_1_8
    6  }
    7}

    ここまで追加できたらAndroid Studioの右上に表示されているSync Nowを押しましょう! 

    以上でHiltライブラリの導入は完了です!

    ※注意点ですが、Hiltライブラリとデータバイディング両方を使用する場合はAndroid Studio 4.0 以降が必要です。


    Hiltの導入と依存性注入の設定

    初めにアプリケーションクラスに@HiltAndroidAppアノテーションを適用し、Hiltの依存性注入システムの初期化を行います。

    これによりHiltがアプリケーション全体で依存性を管理するようになります!

    1@HiltAndroidApp
    2class MyApplicataion : Application() {
    3
    4    ・・・
    5}

    次にHiltに対してそのコンポーネントで依存性注入を行うことを指示するためにAndroidクラスにAndroidEntryPointアノテーションを適用します。

    @AndroidfEntryPointはHiltがフラグメント内での依存性(例えばViewModelやRepositoryなど)を注入するための「入口」として機能します。

    そのため、設定することでHiltは対象のコンポーネントに依存性を自動的に注入されます!

    依存性を注入するSearchViewModelはSearchFragmentで使用されているため、こちらのフラグメントに@AndroidEntiryPointアノテーションを設定します!

    1@AndroidEntryPoint
    2class SearchFragment : Fragment() {
    3
    4    private val viewModel: SearchViewModel by activityViewModels()
    5
    6   ・・・
    7
    8}


    ViewModelに依存関係を注入

    検索したいワードでAPIからデータを取得する処理にHiltを導入したいと思います!

    現在のSearchViewModelを確認するとSearchItemsRepositoryのインスタンスを直接生成しているため密結合になっています。

    そのため、SearchItemsRepositoryの実装が変更された時、SearchViewModelにも直接影響を及ぼします。

    またSearchItemsRepositoryのモックやスタブをSearchViewModelに注入することが難しくなり単体テストの実施が困難になるといった問題があります。

    1class SearchViewModel : ViewModel() {
    2     val searchText = MutableLiveData<String>()
    3
    4    ・・・
    5
    6    //SearchImtesRepositoryインスタンスを直接生成
    7    private val searchListItems = SearchItemsRepository()
    8
    9    fun onSearchButtonClick() {
    10        viewModelScope.launch {
    11            try {
    12                searchText.value?.let {
    13                    val searchItems =
    14                        searchListItems.refreshSearchItems(it)
    15                    _itemList.postValue(searchItems)
    16                }
    17            } catch (e: Exception) {
    18                _itemList.postValue(emptyList())
    19            }
    20        }
    21    }


    こちらの問題を解消するためにHiltを使用してSearchViewModelにSearchItemsRepositoryを注入して疎結合にしたいと思います!

    まずSearchItemsRepositoryに@Injectコンストラクタを追加します。

    これにより、Hiltがこのリポジトリのインスタンスを生成できるようになります。

    1class SearchItemsRepository @Inject constructor() : SearchRepository {
    2
    3    override suspend fun refreshSearchItems(searchWord: String): List<SearchItem> {
    4        return withContext(Dispatchers.IO) {
    5
    6・・・
    7    }
    8}


    次にSearchItemsRepositoryのインスタンスを提供するためのHiltモジュールを作成します。

    Hiltモジュールを作成することで、DIシステムにSearchItemsRepositoryをどのように提供するかを指示することができます!

    1@Module
    2@InstallIn(ViewModelComponent::class)
    3object RepositoryModule {
    4
    5    @Provides
    6    fun provideSearchItemsRepository(): SearchItemsRepository = SearchItemsRepository()
    7}


    最後にSearchItemsRepositoryの依存関係をSearchViewModelに注入するためにコンストラクタインジェクションを使用して注入します!

    これにより、SearchViewModelにSearchItemsRepositoryインスタンスを直接生成することはなくなり疎結合になります!

    1@HiltViewModel
    2class SearchViewModel @Inject constructor(
    3    private val searchListItems: SearchItemsRepository
    4): ViewModel() {
    5    val searchText = MutableLiveData<String>()
    6
    7    ・・・
    8
    9    fun onSearchButtonClick() {
    10        viewModelScope.launch {
    11            try {
    12                searchText.value?.let {
    13                    val searchItems =
    14                        searchListItems.refreshSearchItems(it)
    15                    _itemList.postValue(searchItems)
    16                }
    17            } catch (e: Exception) {
    18                _itemList.postValue(emptyList())
    19            }
    20        }
    21    }
    22}

    以上の設定で、SearchViewModelはSearchItemsRepositoryのインスタンスを自動的に注入されるようになりました!

    ※Hiltについてより詳しく知りたい方は公式ドキュメントをご確認ください!


    さいごに

    今回はHiltの導入から依存性注入の設定まで書いてみました。ブログを書くことで依存性注入の重要性について理解することができました!

    また、テストコードを書く際にも、Test用のHiltライブラリを使用して実装できるみたいなので別の機会にテストコードに関するブログも書いてみたいと思います!

    今回の記事が皆さんのAndroid開発に少しでもお役に立てれば幸いです。次回も、役立つ情報をお届けする予定ですので、ご期待ください!

    たか(エンジニア)

    たか(エンジニア)

    おすすめ記事