Dagger คือเฟรมเวิร์กการฉีดพึ่งพาสำหรับ Java และ Kotlin ที่สร้างโค้ด DI ในเวลาคอมไพล์ผ่านการประมวลผลคำอธิบายประกอบ Hilt คือชั้นครอบบน Dagger สำหรับ Android ที่ทำให้การตั้งค่าคอมโพเนนต์และการจัดการวงจรชีวิตง่ายขึ้น ตามข้อมูลจาก Google, 2025 มีการใช้ Hilt ในมากกว่า 70% ของแอป Android จาก Google Play Top-100 โดยรองรับ Activity, Fragment, ViewModel และ Service ผ่านคอมโพเนนต์ที่กำหนดไว้ล่วงหน้า เฟรมเวิร์กทั้งสองให้การตรวจสอบกราฟพึ่งพาในเวลาคอมไพล์ ซึ่งกำจัดข้อผิดพลาดการฉีดในรันไทม์
ประเด็นสำคัญ
Dagger คือเฟรมเวิร์กการฉีดพึ่งพาที่มีการสร้างโค้ดในเวลาคอมไพล์ พัฒนาครั้งแรกที่ Square และส่งต่อให้ Google ในภายหลัง Dagger ใช้โปรเซสเซอร์คำอธิบายประกอบ Java APT เพื่อวิเคราะห์กราฟพึ่งพาและสร้างคลาสโรงงาน แตกต่างจาก DI ในรันไทม์ (Guice, Koin) ตรงที่ Dagger ไม่ใช้รีเฟลกชัน — โค้ดทั้งหมดถูกสร้างขึ้นในเวลาคอมไพล์ ซึ่งรับประกันประสิทธิภาพสูงสุดในรันไทม์และการตรวจจับข้อผิดพลาดในเวลาบิวด์
Hilt คือไลบรารีของ Google ที่สร้างบน Dagger และปรับให้เหมาะสมสำหรับ Android Hilt มีคอมโพเนนต์ที่กำหนดไว้ล่วงหน้าซึ่งสอดคล้องกับวงจรชีวิตของคอมโพเนนต์ Android: @SingletonComponent สำหรับ Application, @ActivityComponent สำหรับ Activity, @FragmentComponent สำหรับ Fragment, @ViewModelComponent สำหรับ Viewmoel ซึ่งช่วยลดการกำหนดค่า Component และ Module ตามปกติที่จำเป็นใน Dagger บริสุทธิ์ Hilt ยังสร้างกราฟพึ่งพาสำหรับแต่ละคอมโพเนนต์ Android โดยอัตโนมัติผ่าน @AndroidEntryPoint
ตามข้อมูลจาก Google I/O 2024 Hilt คือโซลูชันที่แนะนำสำหรับ DI ในแอปพลิเคชัน Android ที่เขียนด้วย Kotlin ไลบรารี Jetpack (Navigation, Room, WorkManager) มีการรวมเข้ากับ Hilt ในตัวผ่าน @HiltViewModel และ @HiltWorker ในโปรเจกต์ที่ไม่ได้ใช้ Android (ไลบรารี Java/Kotlin บริสุทธิ์, แอปพลิเคชันเซิร์ฟเวอร์) จะใช้ Dagger บริสุทธิ์โดยไม่มีชั้นครอบ Hilt
หากไม่มีเฟรมเวิร์ก DI นักพัฒนาจะสร้างออบเจ็กต์ด้วยตนเองผ่านคอนสตรัคเตอร์หรือโรงงาน โดยส่งต่อพึ่งพาไปตามสายโซ่ ความต้องการใหม่แต่ละอย่างหมายถึงการเปลี่ยนแปลงลายเซ็นของคอนสตรัคเตอร์ทั้งหมดในสายโซ่ Dagger ทำให้กระบวนการนี้เป็นอัตโนมัติ: คุณเพียงแค่ประกาศว่าต้องการประเภทใด (@Inject constructor) และ Dagger จะสร้างกราฟพึ่งพา แก้ไขประเภทที่ซ้อนกันทั้งหมด เมื่อพึ่งพาเปลี่ยนแปลง Dagger จะอัปเดตโค้ดที่สร้างขึ้นโดยอัตโนมัติ — เป็นไปไม่ได้ที่จะทำผิดพลาดในสายโซ่
การฉีดพึ่งพา คือรูปแบบที่ออบเจ็กต์ได้รับการพึ่งพาจากภายนอกแทนที่จะสร้างขึ้นเอง DI ใช้หลักการ Inversion of Control (IoC): คลาสไม่รับผิดชอบในการสร้างพึ่งพาของตัวเอง แต่ประกาศผ่านคอนสตรัคเตอร์ เมธอด หรือฟิลด์ การฉีดผ่านคอนสตรัคเตอร์ถือเป็นวิธีที่ต้องการมากที่สุดเพราะรับประกันว่าออบเจ็กต์ถูกสร้างขึ้นในสถานะที่ถูกต้อง
| ประเภทการฉีด | ไวยากรณ์ Dagger | เมื่อใดควรใช้ |
|---|---|---|
| Constructor injection | @Inject constructor | วิธีหลัก — สำหรับคลาสที่กำหนดเองทั้งหมด |
| Field injection | @Inject lateinit var | เฉพาะคอมโพเนนต์ Android (Activity, Fragment) |
| Method injection | @Inject fun bind() | สำหรับการเริ่มต้นหลังการสร้าง |
ข้อดีหลักของ DI ได้แก่ ความสามารถในการทดสอบ (พึ่งพาสามารถถูกแทนที่ด้วยออบเจ็กต์ mock), การเชื่อมโยงแบบหลวม (คลาสพึ่งพาอินเทอร์เฟซ ไม่ใช่การนำไปใช้), และการจัดการวงจรชีวิตของออบเจ็กต์อย่างชัดเจนผ่านสโคป Dagger รับประกันโดยอัตโนมัติว่าออบเจ็กต์ถูกสร้างขึ้นครั้งเดียวภายในสโคปของมันและถูกทำลายเมื่อออกจากสโคป
Component คือองค์ประกอบหลักของกราฟพึ่งพาของ Dagger มันคืออินเทอร์เฟซที่กำกับด้วย @Component ซึ่งอธิบายสะพานระหว่าง Module และเป้าหมายการฉีด Dagger สร้างการนำไปใช้ของ Component (เช่น DaggerAppComponent) ในเวลาคอมไพล์ Component กำหนดว่าประเภทใดบ้างที่พร้อมสำหรับการฉีดผ่านเมธอดนามธรรมที่ส่งคืนประเภทที่ต้องการ หรือผ่านเมธอด inject ที่รับออบเจ็กต์สำหรับการฉีดฟิลด์
// Module: ให้พึ่งพาที่ Dagger ไม่สามารถสร้างได้เอง
@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: เชื่อมต่อ Module และเป้าหมาย Injection
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module คือคลาสที่มีเมธอดพร้อม @Provides ซึ่งส่งคืนอินสแตนซ์ของพึ่งพา Module ใช้สำหรับประเภทที่ Dagger ไม่สามารถสร้างได้โดยอัตโนมัติ: ไลบรารีบุคคลที่สาม (OkHttp, Retrofit), ออบเจ็กต์ที่มีพารามิเตอร์คอนสตรัคเตอร์, อินเทอร์เฟซที่มีการเลือกการนำไปใช้ @Binds เป็นทางเลือกแทน @Provides สำหรับกรณีที่เมธอดส่งคืนอินเทอร์เฟซและรับการนำไปใช้เดียว: Dagger สร้างแคสต์โดยตรงโดยไม่เรียกเมธอด
@Scope กำหนดอายุการใช้งานของออบเจ็กต์ในกราฟพึ่งพา @Singleton — ออบเจ็กต์ถูกสร้างขึ้นครั้งเดียวสำหรับทั้งแอปพลิเคชัน @ActivityScoped — ออบเจ็กต์มีชีวิตอยู่ตราบเท่าที่ Activity มีชีวิต @FragmentScoped — ตราบเท่าที่ Fragment มีชีวิต หากไม่มีสโคป Dagger จะสร้างอินสแตนซ์ใหม่ทุกครั้งที่มีการฉีด @Reusable — สโคปสำหรับออบเจ็กต์ที่ไม่จำเป็นต้องเป็นซิงเกิลตันแต่การสร้างมีต้นทุนสูง — Dagger อาจแคชอินสแตนซ์แต่ไม่รับประกัน
Hilt ทำให้การกำหนดค่า Dagger สำหรับ Android ง่ายขึ้นผ่านคอมโพเนนต์ที่กำหนดไว้ล่วงหน้าและการสร้างกราฟพื้นฐานอัตโนมัติ คำอธิบายประกอบ @HiltAndroidApp บนคลาส Application เริ่มต้นการสร้างคอมโพเนนต์ Hilt หากไม่มีคำอธิบายประกอบนี้ Hilt จะไม่ทำงาน — มันบังคับสำหรับแอปพลิเคชัน Android ใดๆ ที่ใช้ Hilt @HiltAndroidApp สร้างคอมโพเนนต์หลัก SingletonComponent ซึ่งคอมโพเนนต์อื่นๆ ทั้งหมดของแอปพลิเคชันสืบทอด
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// apiService ถูกฉีดแล้วก่อนการเรียก 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 คือคำอธิบายประกอบสำหรับ Activity, Fragment, Service, BroadcastReceiver และ View มันสร้างคอมโพเนนต์ Hilt สำหรับแต่ละประเภท: @AndroidEntryPoint บน Activity สร้าง ActivityComponent ที่สืบทอดจาก SingletonComponent คอมโพเนนต์ลูกได้รับพึ่งพาทั้งหมดของพ่อแม่โดยอัตโนมัติ การฉีดฟิลด์ด้วย @Inject lateinit var ใช้ได้เฉพาะในคลาสที่กำกับด้วย @AndroidEntryPoint — ในคลาสปกติจะใช้การฉีดคอนสตรัคเตอร์
@InstallIn ระบุว่าคอมโพเนนต์ Hilt ใดที่โมดูลถูกติดตั้ง NetworkModule ที่มี @InstallIn(SingletonComponent::class) ใช้ได้ทั่วทั้งแอปพลิเคชัน Module ที่มี @InstallIn(ActivityComponent::class) ใช้ได้เฉพาะใน Activity เท่านั้น ซึ่งแยกกราฟพึ่งพา: โมดูลเฉพาะ Activity จะไม่ปรากฏใน Fragment และ ViewModel ป้องกันการใช้พึ่งพาที่ไม่ถูกต้องโดยไม่ได้ตั้งใจ @ApplicationContext คือตัวระบุคุณภาพในตัวของ Hilt สำหรับรับ Context ของแอปพลิเคชัน
เมื่อจำเป็นต้องฉีดการนำไปใช้สองแบบที่แตกต่างกันของอินเทอร์เฟซเดียวกัน จะใช้ตัวระบุคุณภาพ Hilt รองรับ @Named สำหรับตัวระบุสตริงและคำอธิบายประกอบที่กำหนดเองด้วย @Qualifier ตัวอย่างเช่น @Named("baseUrl") และ @Named("imageBaseUrl") สำหรับการกำหนดค่าสตริงที่แตกต่างกัน ตัวระบุคุณภาพที่กำหนดเองดีกว่า @Named เนื่องจากการตรวจสอบในเวลาคอมไพล์ — ชื่อสตริงที่ไม่ถูกต้องจะไม่ถูกตรวจจับจนกว่ารันไทม์
@HiltViewModel คือคำอธิบายประกอบที่แทนที่ ViewModelProvider.Factory แบบแมนนวล คลาสที่กำกับด้วย @HiltViewModel ที่มี @Inject constructor จะได้รับพึ่งพาทั้งหมดโดยอัตโนมัติผ่าน Dagger Hilt สร้าง ViewModelFactory ที่ใช้โดย Jetpack ViewModelProvider หากไม่มี Hilt นักพัฒนาจะต้องเขียนโรงงานด้วยตนเอง โดยส่งพารามิเตอร์แต่ละตัวจาก Activity หรือ fragment
@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()
}
}
}
// ใน Activity — Hilt สร้าง ViewModel โดยอัตโนมัติ
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
ViewModelScoped คือสโคปของ Hilt สำหรับพึ่งพาที่มีชีวิตอยู่ตราบเท่าที่ ViewModel มีชีวิต ถ้า ViewModel สองตัวที่มีประเภทเดียวกันฉีดพึ่งพา @ViewModelScoped เดียวกัน แต่ละตัวจะได้รับอินสแตนซ์ของตัวเอง ซึ่งแยกความแตกต่าง @ViewModelScoped ออกจาก @ActivityScoped ที่ Activity หนึ่งตัวได้รับอินสแตนซ์เดียวสำหรับทั้งหมด fragment สำหรับพึ่งพาเฉพาะ ViewModel (เช่น SavedStateHandle) จะใช้ @HiltViewModel กับ @Inject constructor(savedStateHandle: SavedStateHandle)
Hilt รองรับ การฉีดแบบช่วยเหลือ ผ่านไลบรารี Hilt Extensions การฉีดแบบช่วยเหลือช่วยให้ส่งพารามิเตอร์ไปยังคอนสตรัคเตอร์ในเวลาที่ฉีดเมื่อพึ่งพาบางอย่างรู้จักเฉพาะในรันไทม์ (เช่น ID ผู้ใช้จาก intent) สำหรับการฉีดแบบช่วยเหลือ ใช้ @AssistedInject ร่วมกับพารามิเตอร์ @Assisted Hilt สร้าง AssistedFactory ที่สามารถฉีดได้ด้วยวิธีมาตรฐาน
Dagger บริสุทธิ์ ต้องการการสร้าง Component ด้วยตนเอง การกำหนดสโคป และการกำหนดค่าการฉีดในแต่ละคอมโพเนนต์ Android นักพัฒนาสร้าง AppComponent, ActivityComponent, FragmentComponent และจัดการความสัมพันธ์ผ่าน @Subcomponent วิธีการนี้ให้การควบคุมสูงสุดแต่ต้องใช้โค้ดโครงสร้างพื้นฐานจำนวนมาก Dagger ใช้ในโปรเจกต์ขนาดใหญ่ที่ต้องการสถาปัตยกรรม DI ที่ไม่ได้มาตรฐาน หรือในโปรเจกต์ Java/Kotlin ที่ไม่ใช่ Android
Hilt ทำให้โค้ดโครงสร้างพื้นฐานเป็นอัตโนมัติ: หนึ่ง @HiltAndroidApp, หนึ่ง @AndroidEntryPoint สำหรับแต่ละคอมโพเนนต์, สโคปที่กำหนดไว้ล่วงหน้า Google แนะนำ Hilt สำหรับโปรเจกต์ Android ใหม่ทั้งหมด การย้ายระบบจาก Dagger ไปยัง Hilt รวมถึงการแทนที่ Component ด้วย @InstallIn, การแทนที่ @Subcomponent ด้วยคอมโพเนนต์ Hilt ที่กำหนดไว้ล่วงหน้า และการแทนที่ ViewModelProvider.Factory แบบแมนนวลด้วย @HiltViewModel คลาส @Module ส่วนใหญ่ถูกย้ายโดยการเพิ่ม @InstallIn โดยไม่เปลี่ยนเมธอด @Provides
| คุณลักษณะ | Dagger | Hilt |
|---|---|---|
| การตั้งค่า | ด้วยตนเอง: Component, Subcomponent, Builder | อัตโนมัติ: @HiltAndroidApp, @AndroidEntryPoint |
| คอมโพเนนต์ Android | ไม่มีการกำหนดไว้ล่วงหน้า | 12+ คอมโพเนนต์ในตัว |
| ViewModel | โรงงานด้วยตนเอง | @HiltViewModel + @Inject constructor |
| หลายโมดูล | ผ่าน @Component(dependencies) | ผ่าน @InstallIn + การรวม |
| ความซับซ้อน | สูง — ต้องใช้ประสบการณ์ | ต่ำ — เข้าใจได้โดยสัญชาตญาณ |
| ความยืดหยุ่น | สูงสุด | มาตรฐาน (ครอบคลุม 95% ของสถานการณ์) |
ข้อจำกัดของ Hilt: ไลบรารีรองรับเฉพาะ Android (ไม่เหมาะสำหรับโปรเจกต์ Java ฝั่งเซิร์ฟเวอร์บริสุทธิ์), บังคับโครงสร้างคอมโพเนนต์บางอย่าง (ยากที่จะแทนที่), และเพิ่มการพึ่งพา android.hilt:hilt-navigation-compose สำหรับโปรเจกต์ Jetpack Compose สำหรับแอปพลิเคชัน Compose Hilt มี @HiltViewModel ที่เข้าถึงได้ใน Composable ผ่าน hiltViewModel() — โดยไม่ต้องส่ง ViewModel ด้วยตนเองจาก Activity
คำถามที่พบบ่อย
Dagger คือเฟรมเวิร์ก DI พื้นฐานแบบคอมไพล์ไทม์ที่มีการกำหนดค่า Component และ Module ด้วยตนเอง Hilt คือชั้นครอบ Android ที่ทำให้การสร้างคอมโพเนนต์และการรวมกับวงจรชีวิตของ Activity, Fragment, ViewModel, Service และ BroadcastReceiver เป็นอัตโนมัติ
@HiltAndroidApp เปิดใช้งานการสร้างคอมโพเนนต์ Hilt สำหรับ Application หากไม่มีคำอธิบายประกอบนี้ Hilt จะไม่สามารถสร้าง SingletonComponent พื้นฐานที่ ActivityComponent, FragmentComponent และ ViewModelComponent ทั้งหมดสืบทอดได้ คำอธิบายประกอบนี้บังคับสำหรับโปรเจกต์ Hilt ใดๆ
Hilt Navigation มี @HiltViewModel สำหรับ ViewModel ใน NavBackStackEntry และ hiltNavGraphViewModels() สำหรับกำหนดสโคป ViewModel ภายในกราฟนำทาง ไลบรารี android.hilt:hilt-navigation-fragment สร้าง ViewModel โดยอัตโนมัติสำหรับแต่ละ NavBackStackEntry
ใช้ @ApplicationContext สำหรับบริบทของแอปพลิเคชัน หรือ @ActivityContext สำหรับบริบทของ Activity Hilt มีตัวระบุคุณภาพเหล่านี้ในตัวในไลบรารี android.hilt:hilt-android @ActivityContext ใช้ได้เฉพาะในโมดูลที่ติดตั้งใน ActivityComponent เท่านั้น
@Binds เป็นทางเลือกที่มีประสิทธิภาพแทน @Provides เมื่อเมธอดรับพารามิเตอร์หนึ่งตัวพอดีและส่งคืนประเภทของมันเป็นอินเทอร์เฟซ @Binds สร้างแคสต์โดยตรงโดยไม่เรียกเมธอด ลดปริมาณโค้ดที่สร้างขึ้นและปรับปรุงประสิทธิภาพการฉีด
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ