Dagger / Hilt: khái niệm, DI và ứng dụng

Tác giả: IT Sectr Đã đăng: 2026-05-03 Thời gian đọc: 9 phút

Dagger là framework tiêm phụ thuộc cho Java và Kotlin, tạo mã DI tại thời điểm biên dịch thông qua xử lý chú thích. Hilt là lớp bao bọc trên Dagger dành cho Android, đơn giản hóa việc thiết lập thành phần và quản lý vòng đời. Theo Google, 2025, Hilt được sử dụng trong hơn 70% ứng dụng Android từ Top-100 Google Play, hỗ trợ Activity, Fragment, ViewModel và Service thông qua các thành phần được xác định trước. Cả hai framework đều cung cấp xác minh đồ thị phụ thuộc tại thời điểm biên dịch, loại bỏ lỗi tiêm trong thời gian chạy.

Những điểm chính

  • Dagger — framework DI thời điểm biên dịch với tạo mã qua chú thích @Module, @Provides, @Component
  • Hilt — lớp bao bọc Android đơn giản hóa Dagger qua @HiltAndroidApp, @AndroidEntryPoint, @HiltViewModel
  • Component — đồ thị phụ thuộc kết nối Module với mục tiêu Inject qua phương thức proxy
  • Scope — @Singleton, @ViewModelScoped, @ActivityScoped quản lý vòng đời của đối tượng được tiêm
  • Hilt hỗ trợ dự án đa mô-đun qua @InstallIn cho đồ thị phụ thuộc cách ly

Dagger / Hilt là gì?

Dagger là framework tiêm phụ thuộc với tạo mã tại thời điểm biên dịch. Được phát triển ban đầu tại Square và sau đó chuyển giao cho Google, Dagger sử dụng bộ xử lý chú thích Java APT để phân tích đồ thị phụ thuộc và tạo các lớp factory. Không giống như DI thời gian chạy (Guice, Koin), Dagger không sử dụng phản chiếu — tất cả mã được tạo tại thời điểm biên dịch, đảm bảo hiệu suất tối đa khi chạy và phát hiện lỗi tại thời điểm xây dựng.

Hilt là thư viện của Google được xây dựng trên Dagger và tối ưu hóa cho Android. Hilt cung cấp các thành phần được xác định trước tương ứng với vòng đời thành phần Android: @SingletonComponent cho Application, @ActivityComponent cho Activity, @FragmentComponent cho Fragment, @ViewModelComponent cho ViewModel. Điều này loại bỏ cấu hình Component và Module thông thường cần thiết trong Dagger thuần túy. Hilt cũng tự động tạo đồ thị phụ thuộc cho mỗi thành phần Android qua @AndroidEntryPoint.

Theo Google I/O 2024, Hilt là giải pháp được khuyến nghị cho DI trong các ứng dụng Android viết bằng Kotlin. Các thư viện Jetpack (Navigation, Room, WorkManager) có tích hợp sẵn với Hilt qua @HiltViewModel và @HiltWorker. Trong các dự án không sử dụng Android (thư viện Java/Kotlin thuần túy, ứng dụng máy chủ), Dagger thuần túy được sử dụng mà không có lớp bao bọc Hilt.

Vấn đề của tiêm phụ thuộc thủ công

Không có framework DI, nhà phát triển tạo đối tượng thủ công qua constructor hoặc factory, truyền phụ thuộc dọc theo chuỗi. Mỗi yêu cầu mới đồng nghĩa với việc thay đổi chữ ký của tất cả constructor trong chuỗi. Dagger tự động hóa quy trình này: bạn chỉ cần khai báo kiểu cần thiết (@Inject constructor) và Dagger tạo đồ thị phụ thuộc, giải quyết tất cả các kiểu lồng nhau. Khi phụ thuộc thay đổi, Dagger tự động cập nhật mã đã tạo — không thể mắc lỗi trong chuỗi.

Nguyên tắc tiêm phụ thuộc

Tiêm phụ thuộc là mẫu thiết kế trong đó một đối tượng nhận các phụ thuộc từ bên ngoài thay vì tự tạo chúng. DI triển khai nguyên tắc Đảo ngược điều khiển (IoC): một lớp không chịu trách nhiệm tạo phụ thuộc của chính nó mà khai báo chúng qua constructor, phương thức hoặc trường. Tiêm qua constructor được coi là ưu tiên nhất vì nó đảm bảo đối tượng được tạo ở trạng thái hợp lệ.

Loại tiêmCú pháp DaggerKhi nào sử dụng
Constructor injection@Inject constructorPhương pháp chính — cho tất cả lớp tùy chỉnh
Field injection@Inject lateinit varChỉ cho thành phần Android (Activity, Fragment)
Method injection@Inject fun bind()Cho khởi tạo sau khi tạo

Lợi ích của DI thời điểm biên dịch

Các lợi ích chính của DI bao gồm khả năng kiểm thử (phụ thuộc có thể được thay thế bằng đối tượng mock), liên kết lỏng lẻo (lớp phụ thuộc vào interface, không phải triển khai) và quản lý vòng đời đối tượng rõ ràng qua scope. Dagger tự động đảm bảo rằng một đối tượng được tạo một lần trong scope của nó và bị hủy khi thoát khỏi scope.

Kiến trúc Dagger: Component, Module, Provides

Component là phần tử trung tâm của đồ thị phụ thuộc Dagger. Nó là interface được chú thích với @Component mô tả cầu nối giữa Module và mục tiêu tiêm. Dagger tạo triển khai Component (ví dụ DaggerAppComponent) tại thời điểm biên dịch. Component xác định kiểu nào có sẵn để tiêm qua phương thức trừu tượng trả về kiểu cần thiết hoặc qua phương thức inject chấp nhận đối tượng cho tiêm trường.

kotlin
// Module: cung cấp các phụ thuộc mà Dagger không thể tự tạo
@Module
class NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    @Provides
    @Singleton
    fun provideApiService(client: OkHttpClient): ApiService {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Component: kết nối Module và mục tiêu Injection
@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
    fun getApiService(): ApiService
}

@Module là lớp chứa các phương thức với @Provides trả về thể hiện của phụ thuộc. Module được sử dụng cho các kiểu mà Dagger không thể tạo tự động: thư viện bên thứ ba (OkHttp, Retrofit), đối tượng có tham số constructor, interface có lựa chọn triển khai. @Binds là giải pháp thay thế cho @Provides trong trường hợp phương thức trả về interface và chấp nhận một triển khai duy nhất: Dagger tạo ép kiểu trực tiếp mà không gọi phương thức.

@Scope xác định vòng đời của đối tượng trong đồ thị phụ thuộc. @Singleton — đối tượng được tạo một lần cho toàn bộ ứng dụng. @ActivityScoped — đối tượng tồn tại khi Activity tồn tại. @FragmentScoped — khi Fragment tồn tại. Không có scope, Dagger tạo thể hiện mới mỗi lần tiêm. @Reusable — scope cho đối tượng không nhất thiết phải là singleton nhưng việc tạo tốn kém — Dagger có thể lưu cache thể hiện nhưng không đảm bảo.

Hilt cho Android: @HiltAndroidApp và @AndroidEntryPoint

Hilt đơn giản hóa cấu hình Dagger cho Android thông qua các thành phần được xác định trước và tạo đồ thị cơ sở tự động. Chú thích @HiltAndroidApp trên lớp Application kích hoạt tạo thành phần Hilt. Không có chú thích này, Hilt không hoạt động — nó bắt buộc cho mọi ứng dụng Android sử dụng Hilt. @HiltAndroidApp tạo thành phần cha SingletonComponent, từ đó tất cả các thành phần khác của ứng dụng kế thừa.

kotlin
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject lateinit var apiService: ApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // apiService đã được tiêm trước khi gọi onCreate
    }
}

@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    @Provides
    @Singleton
    fun provideDatabase(@ApplicationContext ctx: Context): AppDatabase {
        return Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db").build()
    }
}

@AndroidEntryPoint là chú thích cho Activity, Fragment, Service, BroadcastReceiver và View. Nó tạo thành phần Hilt cho mỗi kiểu: @AndroidEntryPoint trên Activity tạo ActivityComponent kế thừa từ SingletonComponent. Thành phần con tự động nhận tất cả phụ thuộc của cha. Tiêm trường với @Inject lateinit var chỉ khả dụng trong các lớp được chú thích @AndroidEntryPoint — trong lớp thông thường, tiêm constructor được sử dụng.

@InstallIn chỉ định thành phần Hilt mà mô-đun được cài đặt vào. NetworkModule với @InstallIn(SingletonComponent::class) khả dụng trong toàn bộ ứng dụng. Module với @InstallIn(ActivityComponent::class) chỉ khả dụng trong Activity. Điều này cách ly đồ thị phụ thuộc: các mô-đun cụ thể của Activity không hiển thị trong Fragment và ViewModel, ngăn chặn việc sử dụng ngẫu nhiên phụ thuộc không hợp lệ. @ApplicationContext là bộ định tính tích hợp của Hilt để lấy Context ứng dụng.

Qualifier: @Named và bộ định tính tùy chỉnh

Khi cần tiêm hai triển khai khác nhau của cùng một interface, bộ định tính được sử dụng. Hilt hỗ trợ @Named cho định danh chuỗi và chú thích tùy chỉnh với @Qualifier. Ví dụ, @Named("baseUrl") và @Named("imageBaseUrl") cho các cấu hình chuỗi khác nhau. Bộ định tính tùy chỉnh được ưu tiên hơn @Named nhờ kiểm tra tại thời điểm biên dịch — tên chuỗi không chính xác sẽ không được phát hiện cho đến thời gian chạy.

Hilt ViewModel: @HiltViewModel và @Inject constructor

@HiltViewModel là chú thích thay thế factory thủ công ViewModelProvider.Factory. Một lớp được chú thích @HiltViewModel với @Inject constructor tự động nhận tất cả phụ thuộc qua Dagger. Hilt tạo ViewModelFactory được sử dụng bởi Jetpack ViewModelProvider. Không có Hilt, nhà phát triển phải viết factory thủ công, truyền từng tham số từ Activity hoặc fragment.

kotlin
@HiltViewModel
class MainViewModel
    @Inject constructor(
        private val apiService: ApiService,
        private val database: AppDatabase
    ) : ViewModel() {

    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()

    fun loadUsers() {
        viewModelScope.launch {
            _users.value = apiService.getUsers()
        }
    }
}

// Trong Activity — Hilt tự động tạo ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

ViewModelScoped là scope Hilt cho phụ thuộc tồn tại khi ViewModel tồn tại. Nếu hai ViewModel cùng kiểu tiêm cùng một phụ thuộc @ViewModelScoped, mỗi cái nhận thể hiện riêng. Điều này phân biệt @ViewModelScoped với @ActivityScoped, nơi một Activity nhận một thể hiện cho tất cả fragment. Cho phụ thuộc cụ thể của ViewModel (ví dụ SavedStateHandle), @HiltViewModel được sử dụng với @Inject constructor(savedStateHandle: SavedStateHandle).

Hilt hỗ trợ tiêm hỗ trợ qua thư viện Hilt Extensions. Tiêm hỗ trợ cho phép truyền tham số vào constructor tại thời điểm tiêm khi một số phụ thuộc chỉ được biết tại thời gian chạy (ví dụ ID người dùng từ intent). Cho tiêm hỗ trợ, @AssistedInject được sử dụng kết hợp với tham số @Assisted. Hilt tạo AssistedFactory có thể được tiêm theo cách tiêu chuẩn.

Dagger vs Hilt: so sánh và di chuyển

Dagger thuần túy yêu cầu tạo thủ công Component, định nghĩa scope và cấu hình tiêm vào mỗi thành phần Android. Nhà phát triển tạo AppComponent, ActivityComponent, FragmentComponent và quản lý mối quan hệ của chúng qua @Subcomponent. Cách tiếp cận này mang lại kiểm soát tối đa nhưng yêu cầu lượng mã soạn sẵn đáng kể. Dagger được sử dụng trong các dự án lớn yêu cầu kiến trúc DI không chuẩn hoặc trong các dự án Java/Kotlin không phải Android.

Hilt tự động hóa mã soạn sẵn: một @HiltAndroidApp, một @AndroidEntryPoint cho mỗi thành phần, scope được xác định trước. Google khuyến nghị Hilt cho tất cả dự án Android mới. Di chuyển từ Dagger sang Hilt bao gồm thay thế Component bằng @InstallIn, thay thế @Subcomponent bằng thành phần Hilt được xác định trước và thay thế factory thủ công ViewModelProvider.Factory bằng @HiltViewModel. Hầu hết các lớp @Module được di chuyển bằng cách thêm @InstallIn mà không thay đổi phương thức @Provides.

Đặc điểmDaggerHilt
Thiết lậpThủ công: Component, Subcomponent, BuilderTự động: @HiltAndroidApp, @AndroidEntryPoint
Thành phần AndroidKhông xác định trước12+ thành phần tích hợp
ViewModelFactory thủ công@HiltViewModel + @Inject constructor
Đa mô-đunQua @Component(dependencies)Qua @InstallIn + tổng hợp
Độ phức tạpCao — cần kinh nghiệmThấp — trực quan dễ hiểu
Linh hoạtTối đaTiêu chuẩn (bao phủ 95% kịch bản)

Giới hạn của Hilt: thư viện chỉ hỗ trợ Android (không phù hợp cho dự án Java thuần phía máy chủ), áp đặt cấu trúc thành phần nhất định (khó ghi đè) và thêm phụ thuộc vào android.hilt:hilt-navigation-compose cho dự án Jetpack Compose. Cho ứng dụng Compose, Hilt cung cấp @HiltViewModel có thể truy cập trong Composable qua hiltViewModel() — không cần cung cấp ViewModel thủ công từ Activity.

Câu hỏi thường gặp

Sự khác biệt giữa Dagger và Hilt là gì?

Dagger là framework DI cơ bản tại thời điểm biên dịch với cấu hình thủ công Component và Module. Hilt là lớp bao bọc Android tự động hóa việc tạo thành phần và tích hợp với vòng đời của Activity, Fragment, ViewModel, Service và BroadcastReceiver.

Tại sao cần @HiltAndroidApp?

@HiltAndroidApp kích hoạt tạo thành phần Hilt cho Application. Không có chú thích này, Hilt không thể tạo SingletonComponent cơ sở mà tất cả ActivityComponent, FragmentComponent và ViewModelComponent kế thừa. Chú thích này bắt buộc cho mọi dự án Hilt.

Hilt hoạt động với Jetpack Navigation như thế nào?

Hilt Navigation cung cấp @HiltViewModel cho ViewModel trong NavBackStackEntry và hiltNavGraphViewModels() để giới hạn phạm vi ViewModel trong đồ thị điều hướng. Thư viện android.hilt:hilt-navigation-fragment tự động tạo ViewModel cho mỗi NavBackStackEntry.

Làm thế nào để tiêm Context trong Hilt?

Sử dụng @ApplicationContext cho context ứng dụng hoặc @ActivityContext cho context Activity. Hilt cung cấp các bộ định tính này tích hợp trong thư viện android.hilt:hilt-android. @ActivityContext chỉ khả dụng trong các mô-đun được cài đặt trong ActivityComponent.

@Binds là gì và khi nào sử dụng?

@Binds là giải pháp thay thế hiệu quả cho @Provides khi phương thức chấp nhận chính xác một tham số và trả về kiểu của nó dưới dạng interface. @Binds tạo ép kiểu trực tiếp mà không gọi phương thức, giảm lượng mã được tạo và cải thiện hiệu suất tiêm.

Tổng kết

  • Dagger — framework DI thời điểm biên dịch với chú thích @Module, @Provides, @Component và tạo mã qua APT
  • Hilt — lớp bao bọc Android trên Dagger với @HiltAndroidApp, @AndroidEntryPoint, @InstallIn và thành phần xác định trước
  • Component quản lý đồ thị phụ thuộc, Module cung cấp lớp bên thứ ba, Provides cung cấp factory đối tượng
  • Scope (@Singleton, @ViewModelScoped, @ActivityScoped) xác định vòng đời đối tượng trong đồ thị Dagger
  • @HiltViewModel tự động hóa tạo ViewModel, loại bỏ factory thủ công ViewModelProvider.Factory
  • @InstallIn cách ly mô-đun theo thành phần, ngăn rò rỉ phụ thuộc giữa các lớp ứng dụng
  • Hilt được Google khuyến nghị cho tất cả dự án Android mới, Dagger cho kiến trúc không phải Android và tùy chỉnh

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm