Jetpack ComposeでHiltを使ってみた!
Overview
Hiltとは公式によると...
Hilt は、Android アプリで依存性を注入するための推奨されるソリューションであり、Compose とシームレスに連携します。
ViewModel セクションに記載されている viewModel() 関数は、Hilt が @HiltViewModel アノテーションを使用して構築する ViewModel を自動的に使用します。Hilt の ViewModel 統合に関するドキュメントが提供されています。
🧐依存性を注入するとは何か?
分かりやすく言うと、上書きすることです。今回はダミーのデータをListに入れて、それを表示するだけです。これパッケージを使ってやっています。
summary
まずは、パッケージをbuild.gradleに追加しましょう!
Hiltを追加
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.junichi.hilttutorial'
compileSdk 33
defaultConfig {
applicationId "com.junichi.hilttutorial"
minSdk 24
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.3.2'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.5.1'
implementation platform('androidx.compose:compose-bom:2022.10.00')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
// Hiltの依存関係を追加
implementation 'androidx.hilt:hilt-navigation-fragment:1.0.0'
}
📁ディレクトリ構成
MVVMらしいコードになっていたので、ファイルを分けました。このようになっております。
それぞれのファイルの解説をしていきます。
データを扱うdata class。こちらにダミーデータで使うデータ型のメンバー変数を定義します。
モデル
package com.junichi.hilttutorial.model
data class User(
val id: Long,
val name: String,
val email: String
)
依存性の注入(DI)して、データの永続化とまでは、いかないけどダミーのデータを入れるシングルトンのクラスを作ります。シングルトンだから、同じインスタンを生成しないんでしょうね。この辺がいまだにわからない?
コンストラクタを使うので、初期値というか、インスタンスが呼び出されたら、最初に実行される処理があって、その中に、DIする関数がありましてこれが実行されて、View側に表示するダミーのデータを入れます。
一応初期値ですね...
DI
package com.junichi.hilttutorial.repository
import com.junichi.hilttutorial.model.User
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class UserRepository @Inject constructor() {
// getUserが依存しているgetUsersを、UserRepositoryのコンストラクタで注入する
fun getUser(id: Long): User? {
return getUsers().find { it.id == id }
}
// getUserを使用して、依存性の注入をする
fun getUsers(): List<User> {
return listOf(
User(id = 123, name = "James Bond", "jamesbond@007.com"),
User(id = 345, name = "Batman", "batman@cave.com"),
User(id = 999, name = "Arya Stark", "arya@winterfell.com")
)
}
}
View側に、データを保存するよと通知をする必要があります。MVVMの設計だと、VM(View Model)をここで、使います。レポジトリのロジックを呼び出して状態を渡しているだけ。
ViewModel
package com.junichi.hilttutorial.viewModel
import androidx.lifecycle.ViewModel
import com.junichi.hilttutorial.model.User
import com.junichi.hilttutorial.repository.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
@HiltViewModel
class HomeScreenViewModel @Inject constructor(val userRepository: UserRepository) : ViewModel() {
private val _users = MutableStateFlow(userRepository.getUsers())
val users: StateFlow<List<User>> = _users
}
ダミーデータを表示するComposable関数であるHomeScreen
を作成します。
アプリのUI
package com.junichi.hilttutorial
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.junichi.hilttutorial.repository.UserRepository
import com.junichi.hilttutorial.viewModel.HomeScreenViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen() {
// インスタンを返す関数を呼び出す
val vm: HomeScreenViewModel = hiltViewModel()
// インスタンを返す関数を代入した変数を使用する
val users by vm.users.collectAsState()
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "Hiltを使ってみた?") }
)
}
// LazyColumnで、依存性の注入をしたダミーのユーザーデータを表示する
) { paddingValues ->
LazyColumn(modifier = Modifier.padding(paddingValues)) {
items(users) { user ->
Text(text = "ID: ${user.id}")
Spacer(modifier = Modifier.padding(8.dp))
Text(text = "お名前: ${user.name}")
Spacer(modifier = Modifier.padding(8.dp))
Text(text = "メールアドレス: ${user.email}")
Spacer(modifier = Modifier.padding(8.dp))
}
}
}
}
// この関数は、HomeScreenViewModelのインスタンスを返す
fun hiltViewModel(): HomeScreenViewModel {
return HomeScreenViewModel(UserRepository())
}
MainActivity.ktでHomeScreenをimportして、ビルドしたときにエミュレーターに表示できるようにします。
アプリを実行するコード
package com.junichi.hilttutorial
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.junichi.hilttutorial.ui.theme.HiltTutorialTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
HiltTutorialTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
HomeScreen()
}
}
}
}
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
HiltTutorialTheme {
HomeScreen()
}
}
ビルドに成功するとこのような画面が表示されます。
thoughts
今回は、Androidのネイティブアプリで、依存性の注入なるものをやってみました。できているかは疑問ですが😅
なんでこれをやろうかなと思ったかというと、API通信をするチュートリアルをやるときに、Hiltも使って機能実装をしていたので、学習する必要が出てきたのでやってみました。
今回参考にしたMediumの記事
このままやっても動きません!
こちらがGithubのサンプルコードです
全体のコードが見れます。
Discussion