Get in touch
or send us a question?
CONTACT

Hướng dẫn sử dụng Hilt trong Android: Quản lý Dependency Injection đơn giản và hiệu quả

1. Giới thiệu về Dependency Injection (DI) và Hilt

Dependency Injection (DI) là một kỹ thuật lập trình giúp quản lý các phụ thuộc (dependencies) giữa các lớp. DI mang lại nhiều lợi ích như:

  • Tăng tính linh hoạt của mã nguồn.
  • Dễ dàng bảo trì và mở rộng.
  • Hỗ trợ tốt hơn cho việc viết unit test.

Hilt là một thư viện DI được Google phát triển dựa trên Dagger, được tối ưu hóa cho Android. Hilt giúp tự động hóa việc tạo và quản lý các dependencies, đồng thời tích hợp chặt chẽ với vòng đời của các thành phần Android như Activity, Fragment, ViewModel,…


2. Cài đặt Hilt

Bước 1: Thêm dependencies vào build.gradle

Đầu tiên, bạn cần thêm các plugin và dependencies cần thiết vào tệp build.gradle của module:

// build.gradle (Module)
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("kotlin-kapt")
    id("dagger.hilt.android.plugin") // Thêm plugin Hilt
}

dependencies {
    implementation("com.google.dagger:hilt-android:2.51.1")
    kapt("com.google.dagger:hilt-android-compiler:2.51.1")

    // Hilt cho ViewModel
    implementation("androidx.hilt:hilt-lifecycle-viewmodel:1.0.0")
    kapt("androidx.hilt:hilt-compiler:1.0.0")
}

kapt {
    correctErrorTypes = true
}

Bước 2: Tạo lớp Application

Tạo một lớp kế thừa từ Application và thêm annotation @HiltAndroidApp để Hilt có thể khởi tạo:

@HiltAndroidApp
class MyApp : Application()

Bước 3: Cập nhật tệp AndroidManifest.xml

Khai báo lớp Application trong tệp AndroidManifest.xml:

<application
    android:name=".MyApp"
    ...>
</application>

3. Ví dụ cơ bản: Inject Dependency vào Activity

Định nghĩa Dependency

Tạo một lớp UserRepository và sử dụng annotation @Inject để Hilt biết cách khởi tạo nó:

class UserRepository @Inject constructor() {
    fun getUser() = "John Doe"
}

Inject vào Activity

Sử dụng annotation @AndroidEntryPoint để cho phép Hilt inject dependencies vào Activity:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var userRepository: UserRepository

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val user = userRepository.getUser()
        Toast.makeText(this, "User: $user", Toast.LENGTH_SHORT).show()
    }
}

4. Sử dụng Module để cung cấp Dependency phức tạp

Ví dụ: Cung cấp Retrofit Instance

Để cung cấp một instance của Retrofit, bạn cần tạo một @Module và sử dụng annotation @Provides:

@Module
@InstallIn(SingletonComponent::class) // Scope: Application-level
object NetworkModule {

    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Provides
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }
}

Inject Retrofit vào Repository

Inject ApiService vào UserRepository:

class UserRepository @Inject constructor(
    private val apiService: ApiService // Inject Retrofit Service
) {
    suspend fun fetchUser() = apiService.getUser()
}

5. Inject vào ViewModel

Hilt hỗ trợ inject dependencies trực tiếp vào ViewModel với annotation @HiltViewModel:

@HiltViewModel
class UserViewModel @Inject constructor(
    private val userRepository: UserRepository
) : ViewModel() {

    private val _user = MutableStateFlow<String?>(null)
    val user: StateFlow<String?> = _user

    fun loadUser() {
        viewModelScope.launch {
            _user.value = userRepository.fetchUser()
        }
    }
}

Sử dụng ViewModel trong Activity/Fragment

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel.loadUser()
        viewModel.user.observe(this) { user ->
            Toast.makeText(this, "User: $user", Toast.LENGTH_SHORT).show()
        }
    }
}

6. Scopes và vòng đời trong Hilt

Hilt quản lý scope của dependencies dựa trên vòng đời của các thành phần Android:

  • @Singleton: Dependency tồn tại xuyên suốt ứng dụng.
  • @ActivityScoped: Dependency sống cùng Activity.
  • @FragmentScoped: Dependency sống cùng Fragment.

Ví dụ:

@Module
@InstallIn(ActivityComponent::class) // Scope: Activity-level
object ActivityModule {

    @Provides
    @ActivityScoped // Sống cùng Activity
    fun provideAnalytics(): AnalyticsService = FirebaseAnalytics()
}

7. Kết luận

Hilt giúp đơn giản hóa việc quản lý dependencies trong Android với các lợi ích:

  • Giảm thiểu boilerplate code.
  • Tích hợp dễ dàng với các thành phần Android và thư viện Jetpack như Room, Retrofit, ViewModel.
  • Hỗ trợ tốt hơn cho việc viết mã sạch và dễ bảo trì.

Hãy thử áp dụng Hilt vào dự án của bạn để tận hưởng những lợi ích mà nó mang lại!