GitHub 開源程式碼:連結
前言
在現代 Android 開發中,依賴注入是一個非常重要的概念,它可以幫助我們更好地管理程式中的依賴關係,提高程式的可維護性和可測試性。Hilt 是 Google 推出的一個依賴注入框架,它基於 Dagger 2,並對其進行了擴展,使得在 Android 應用中使用依賴注入變得更加簡單和方便。今天,我們將透過一個具體的 Android 範例專案「Android-DI-Hilt-Demo」來學習 Hilt 的基本使用。
這個範例專案採用了多種現代 Android 開發技術和最佳實踐,其技術架構如下:
在開始之前,請確保你的開發環境滿足以下要求:
讓我們先來了解一下專案的結構:
app/src/main/java/com/hkt/hiltdemo/
├── HiltDemoApplication.kt # 應用程式入口點
├── MainActivity.kt # 主活動
├── data/ # 資料層
│ ├── AppDatabase.kt # Room 資料庫設定
│ ├── User.kt # 使用者資料模型
│ ├── UserDao.kt # 資料庫訪問介面
│ └── UserRepository.kt # 資料存取倉庫
├── di/ # 依賴注入
│ └── DatabaseModule.kt # 資料庫相關依賴配置
└── ui/ # UI 層
├── UserScreen.kt # 使用者介面
└── UserViewModel.kt # 視圖模型
在 Android 應用中使用 Hilt,首先需要在應用程式的入口點進行初始化。在我們的專案中,HiltDemoApplication.kt
是應用程式的入口點:
package com.hkt.hiltdemo
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class HiltDemoApplication : Application()
這裡使用了 @HiltAndroidApp
註解來標記這個類,Hilt 會自動生成必要的代碼來處理依賴注入的初始化工作。
在需要使用依賴注入的 Activity 中,我們可以使用 @AndroidEntryPoint
註解來注入所需的依賴。雖然在我們的範例中沒有直接展示 @AndroidEntryPoint
的使用,但通常會在 Activity 類上添加這個註解,例如:
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
// ...
}
在 MVVM 架構中,ViewModel 是一個重要的組件,Hilt 也提供了方便的方式來注入 ViewModel。在 UserViewModel.kt
中,我們可以看到如下代碼:
package com.hkt.hiltdemo.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.hkt.hiltdemo.data.User
import com.hkt.hiltdemo.data.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
val users: StateFlow<List<User>> = userRepository.getAllUsers()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)
fun addUser(name: String) {
if (name.isBlank()) return
viewModelScope.launch {
userRepository.insertUser(User(name = name))
}
}
}
這裡使用了 @HiltViewModel
註解來標記這個 ViewModel 類,並通過 @Inject
註解的構造函數來注入 UserRepository
。
在這個專案中,我們使用 Room 來進行本地資料存儲。Room 是 Android 官方推薦的一個 SQL 庫,它提供了一個抽象層,使得在 Android 中使用 SQLite 變得更加簡單。
首先,我們需要定義資料表的結構。在 User.kt
中,我們定義了一個 User
資料模型:
package com.hkt.hiltdemo.data
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val name: String
)
這裡使用了 @Entity
註解來標記這個類是一個 Room 實體,並指定了資料表的名稱為 users
。
接下來,我們需要定義一個 DAO(Data Access Object)來進行資料操作。在 UserDao.kt
中,我們可以定義一些增刪改查的方法:
// 假設 UserDao.kt 的部分代碼
interface UserDao {
@Insert
suspend fun insertUser(user: User)
@Query("SELECT * FROM users")
fun getAllUsers(): Flow<List<User>>
}
為了更好地管理資料訪問,我們通常會創建一個 Repository 類。在 UserRepository.kt
中,我們可以看到如下代碼:
package com.hkt.hiltdemo.data
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class UserRepository @Inject constructor(
private val userDao: UserDao
) {
fun getAllUsers(): Flow<List<User>> = userDao.getAllUsers()
suspend fun insertUser(user: User) {
userDao.insertUser(user)
}
}
這個 UserRepository
類使用了 @Singleton
註解,表示它是一個單例類,並通過 @Inject
註解的構造函數來注入 UserDao
。
在這個專案中,我們採用了 MVVM 架構。MVVM 架構將 UI 層和資料層分離,使得程式的結構更加清晰,也更方便進行單元測試。
前面已經介紹了 UserViewModel
,它負責處理 UI 層和資料層之間的交互。通過使用 LiveData 或 StateFlow,ViewModel 可以將資料的變化通知到 UI 層。
在 UserScreen.kt
中,我們可以看到使用 Jetpack Compose 構建的使用者介面:
package com.hkt.hiltdemo.ui
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@Composable
fun UserScreen(
viewModel: UserViewModel = hiltViewModel()
) {
val users by viewModel.users.collectAsState()
var userName by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
// 新增用戶區域
OutlinedTextField(
value = userName,
onValueChange = { userName = it },
label = { Text("使用者名稱") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = {
viewModel.addUser(userName)
userName = ""
},
modifier = Modifier.align(Alignment.End)
) {
Text("新增")
}
Spacer(modifier = Modifier.height(16.dp))
// 用戶列表
LazyColumn(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(users) { user ->
Card(
modifier = Modifier.fillMaxWidth()
) {
Text(
text = user.name,
modifier = Modifier.padding(16.dp)
)
}
}
}
}
}
這個介面使用了 hiltViewModel()
函數來獲取 UserViewModel
的實例,並通過 collectAsState()
函數來觀察 users
的變化,實現了響應式 UI 更新。
透過這個範例專案,我們學習了 Hilt 依賴注入的基本使用,包括初始化應用程式、注入 Activity 和 ViewModel;了解了 Room 資料庫的實現,包括定義資料表、資料庫訪問介面和資料存取倉庫;還看到了 MVVM 架構在 Android 開發中的應用。希望這篇文章能幫助你更好地理解和使用 Hilt 進行 Android 開發。