# Dependency Injection บน Android: Hilt vs Koin - คู่มือฉบับสมบูรณ์และคำถามสัมภาษณ์ 2026 > เปรียบเทียบ Hilt และ Koin สำหรับ dependency injection บน Android พร้อมตัวอย่างโค้ดจริง benchmark ประสิทธิภาพ และคำถามสัมภาษณ์งาน อัปเดตสำหรับ Hilt 2.57 และ Koin 4.2 - Published: 2026-05-24 - Updated: 2026-05-24 - Author: SharpSkill - Tags: android, kotlin, hilt, koin, dependency-injection, dagger - Reading time: 9 min --- Dependency injection เป็นรูปแบบการออกแบบที่กำหนดวิธีที่ class รับ dependency ของตนเองแทนที่จะสร้างขึ้นมาเอง ในระบบนิเวศ Android ปี 2026 มี framework สองตัวที่ครองตลาด: [Hilt](https://dagger.dev/hilt/) (โซลูชัน compile-time จาก Google ที่สร้างบน Dagger) เวอร์ชัน 2.57.1 และ [Koin](https://insert-koin.io/) (service locator น้ำหนักเบาที่ทำงาน ณ runtime) เวอร์ชัน 4.2.1 การเลือก framework ที่เหมาะสมมีผลกระทบต่อเวลา build ประสิทธิภาพการเริ่มต้นแอป ความสามารถในการทดสอบ และความเร็วในการ onboard สมาชิกใหม่ในทีม > **DI แบบ Compile-Time vs Runtime** > > Hilt ตรวจสอบ dependency graph ทั้งหมดในระหว่างการคอมไพล์ และทำให้ build ล้มเหลวหากขาด binding ใดก็ตาม ส่วน Koin จะ resolve dependency ณ runtime ผ่าน Kotlin DSL โดยจะตรวจพบข้อผิดพลาดการกำหนดค่าเฉพาะเมื่อ code path ที่เกี่ยวข้องถูกเรียกใช้งาน ความแตกต่างพื้นฐานนี้เป็นตัวขับเคลื่อน trade-off ทั้งหมดระหว่างสอง framework ## กลไกการทำงานของ Hilt เบื้องหลัง Hilt สร้าง component ของ Dagger ที่ถูก map กับ class ต่าง ๆ ในวงจรชีวิต Android Annotation `@HiltAndroidApp` จะทริกเกอร์การสร้างโค้ดอัตโนมัติในขั้นตอนคอมไพล์ โดยสร้าง factory และ provider สำหรับทุก binding ที่ประกาศไว้ KSP (Kotlin Symbol Processing) เข้ามาแทนที่ KAPT ในฐานะ annotation processor ที่แนะนำตั้งแต่ Hilt 2.48 ช่วยลดเวลาประมวลผล annotation ลงประมาณครึ่งหนึ่ง การตั้งค่า Hilt โดยทั่วไปประกอบด้วยสามส่วน: module ที่ประกาศ binding, entry point (Activity, Fragment หรือ ViewModel) และ class Application ที่ถูกกำกับด้วย `@HiltAndroidApp` ```kotlin // AppModule.kt @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides @Singleton fun provideRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() } @Provides @Singleton fun provideUserRepository( retrofit: Retrofit ): UserRepository { return UserRepositoryImpl(retrofit.create(UserApi::class.java)) } } ``` ```kotlin // UserViewModel.kt @HiltViewModel class UserViewModel @Inject constructor( private val userRepository: UserRepository ) : ViewModel() { private val _users = MutableStateFlow>(emptyList()) val users: StateFlow> = _users.asStateFlow() fun loadUsers() { viewModelScope.launch { _users.value = userRepository.getUsers() } } } ``` คอมไพเลอร์จะตรวจสอบว่า `UserRepository` มี provider ที่ถูกต้องก่อนที่แอปจะรัน หาก binding ขาดหาย จะปรากฏเป็น build error ไม่ใช่ runtime crash ## การตั้งค่า Koin ด้วยแนวทาง Kotlin DSL Koin ใช้แนวทางตรงกันข้าม: ไม่มีการสร้างโค้ดอัตโนมัติ ไม่มี annotation processing Dependency ถูกประกาศด้วย Kotlin ล้วนผ่าน DSL และ framework จะ resolve ณ runtime ผ่าน service registry ส่วนกลาง Koin 4.2 เปิดตัว lazy modules สำหรับการโหลดแบบขนานตอนเริ่มต้นแอป และ engine `CoreResolverV2` ใหม่ที่ปรับปรุงการ resolve scope ให้เร็วขึ้น ```kotlin // AppModule.kt val appModule = module { // Retrofit instance แบบ singleton single { Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() } // Repository แบบ singleton ที่ bind กับ interface single { UserRepositoryImpl(get().create(UserApi::class.java)) } // ViewModel พร้อม repository ที่ถูก inject viewModel { UserViewModel(get()) } } ``` ```kotlin // MyApplication.kt class MyApplication : Application() { override fun onCreate() { super.onCreate() startKoin { androidContext(this@MyApplication) modules(appModule) } } } ``` DSL ของ Koin อ่านได้อย่างเป็นธรรมชาติและไม่ต้องการ plugin annotation processing ในการกำหนดค่า Gradle สิ่งที่ต้องแลกคือ: ข้อผิดพลาดในการเขียน `get()` resolution จะปรากฏเฉพาะ ณ runtime เมื่อจุด injection นั้นถูกเรียกใช้งาน ## Benchmark ประสิทธิภาพ: เวลา Build vs ต้นทุน Runtime คำถามเรื่องประสิทธิภาพเกิดขึ้นเกือบทุกครั้งในการ[สัมภาษณ์งาน Android](/technologies/android/interview-questions/android-dependency-injection) ตัวเลขที่เป็นรูปธรรมมีความหมายมากกว่าความคิดเห็น | ตัวชี้วัด | Hilt 2.57 (KSP) | Koin 4.2 | |-----------|-----------------|----------| | Overhead จาก clean build | +8-15 วินาที (KSP code generation) | ~0 วินาที (ไม่มี code generation) | | Overhead จาก incremental build | +2-4 วินาที | ~0 วินาที | | เริ่มต้นแอป (50 binding) | ~0 ms (โค้ดถูกสร้างไว้ล่วงหน้า) | ~5-15 ms (resolve graph) | | เริ่มต้นแอป (500 binding) | ~0 ms | ~30-80 ms | | ผลกระทบต่อขนาด APK | +200-400 KB (generated code) | +100 KB (runtime library) | | การตรวจจับ binding ที่ขาด | Error ตอนคอมไพล์ | Crash ตอน runtime | สำหรับโปรเจกต์ที่มี binding น้อยกว่า 100 รายการ overhead ของ Koin ณ runtime แทบจะสังเกตไม่ได้ เมื่อเกิน 300 binding ต้นทุนการเริ่มต้นจะเริ่มวัดได้ แม้ว่า lazy modules ใน Koin 4.2 จะช่วยบรรเทาปัญหานี้ด้วยการโหลด module แบบขนาน > **KSP vs KAPT** > > การเปลี่ยนจาก KAPT เป็น KSP สำหรับการประมวลผล Hilt ช่วยลดเวลา annotation processing ได้ 40-60% โปรเจกต์ใดก็ตามที่ยังใช้ KAPT กับ Hilt ควรย้ายไปใช้ KSP ทันที Hilt รองรับ KSP ตั้งแต่เวอร์ชัน 2.48 ## กลยุทธ์การทดสอบสำหรับแต่ละ Framework ความสามารถในการทดสอบคือจุดที่ความแตกต่างทางสถาปัตยกรรมแสดงผลลัพธ์จริง Hilt มี `@TestInstallIn` สำหรับสลับ module production ด้วย test double ณ compile-time ส่วน Koin มี `loadKoinModules()` สำหรับ override definition ณ runtime ```kotlin // Module ทดสอบ Hilt — แทนที่ binding ของ AppModule ในการทดสอบ @Module @TestInstallIn( components = [SingletonComponent::class], replaces = [AppModule::class] ) object FakeAppModule { @Provides @Singleton fun provideUserRepository(): UserRepository { return FakeUserRepository() // Test double ในหน่วยความจำ } } ``` ```kotlin // การทดสอบ Koin — override ณ runtime class UserViewModelTest : KoinTest { @Before fun setUp() { startKoin { modules( module { single { FakeUserRepository() } viewModel { UserViewModel(get()) } } ) } } @Test fun `loads users from repository`() { val viewModel: UserViewModel = get() viewModel.loadUsers() assertEquals(3, viewModel.users.value.size) } @After fun tearDown() { stopKoin() } } ``` การทดสอบด้วย Hilt จะตรวจจับการกำหนดค่า test module ที่ผิดพลาดได้ตั้งแต่ตอนคอมไพล์ ส่วนการทดสอบด้วย Koin ต้องการการจัดการ lifecycle อย่างรอบคอบ (`startKoin`/`stopKoin`) แต่ให้ความยืดหยุ่นมากกว่าสำหรับการ override module บางส่วน ## สถาปัตยกรรม Multi-Module: การขยาย DI ข้ามฟีเจอร์ โปรเจกต์ Android ขนาดใหญ่แบ่งฟีเจอร์ออกเป็น Gradle module ต่าง ๆ Framework DI ต้องรองรับขอบเขตระหว่าง module อย่างชัดเจน สำหรับ Hilt แต่ละ feature module จะประกาศ `@Module` ของตัวเองพร้อม annotation `@InstallIn` จากนั้น Hilt จะรวม module ทั้งหมดเข้าเป็นลำดับชั้น component เดียวในขั้นตอนคอมไพล์ [สถาปัตยกรรม MVVM](/blog/android/mvvm-vs-mvi-architecture) ทำงานร่วมกับ scoped component ของ Hilt ได้อย่างเป็นธรรมชาติ ```kotlin // feature-payments/PaymentModule.kt @Module @InstallIn(ViewModelComponent::class) object PaymentModule { @Provides fun providePaymentGateway( retrofit: Retrofit // จัดเตรียมโดย module :app ): PaymentGateway { return StripePaymentGateway(retrofit.create(PaymentApi::class.java)) } } ``` สำหรับ Koin แต่ละ feature module จะ export การประกาศ `module {}` ของตัวเอง และ app module จะโหลดทั้งหมดตอนเริ่มต้น Lazy modules ใน Koin 4.2 ช่วยให้โหลด feature module แบบ lazy ได้จนกว่าจะมีการเข้าถึงครั้งแรก ```kotlin // feature-payments/paymentModule.kt val paymentModule = module { factory { StripePaymentGateway(get().create(PaymentApi::class.java)) } } // app/MyApplication.kt — โหลดทุก feature module startKoin { modules(appModule, paymentModule, analyticsModule) } ``` Hilt บังคับการมองเห็น dependency ผ่าน component scope ในขณะที่ Koin อาศัยข้อตกลงร่วมกัน — module ใดก็ตามสามารถเข้าถึง definition ของ module อื่นได้ ซึ่งให้ความยืดหยุ่นแต่อาจนำไปสู่ coupling แบบแฝง ## ข้อพิจารณาเรื่อง Kotlin Multiplatform Koin รองรับ [Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html) (KMP) แบบ native DSL `module {}` เดียวกันใช้งานได้บน Android, iOS, Desktop และ Web target ส่วน Hilt รองรับเฉพาะ Android เท่านั้น เนื่องจากพึ่งพา component lifecycle เฉพาะ Android และ annotation processor ของ Dagger สำหรับโปรเจกต์ที่ต้องการรองรับหลายแพลตฟอร์ม Koin เป็นตัวเลือกเดียวที่เป็นไปได้ระหว่างสองตัวนี้ Module business logic ที่ใช้ร่วมกันสามารถประกาศ dependency ครั้งเดียวแล้ว inject ข้ามทุก target ได้ ## คำถามสัมภาษณ์ที่พบบ่อยเกี่ยวกับ DI บน Android คำถามเหล่านี้ปรากฏอย่างสม่ำเสมอใน[การสัมภาษณ์งานเทคนิค Android](/technologies/android) แต่ละคำตอบจะกระชับและมุ่งเน้นสิ่งที่ผู้สัมภาษณ์คาดหวัง **ถ: ความแตกต่างระหว่าง dependency injection แบบ compile-time และ runtime คืออะไร?** Compile-time DI (Hilt/Dagger) สร้างโค้ด injection ในระหว่างขั้นตอน build คอมไพเลอร์จะตรวจสอบ dependency graph ทั้งหมดเพื่อจับ binding ที่ขาดก่อน runtime ส่วน Runtime DI (Koin) จะ resolve dependency เมื่อถูกร้องขอครั้งแรก โดยใช้รูปแบบ service registry Compile-time DI ให้การเริ่มต้นแอปที่เร็วกว่าแต่ build ช้ากว่า ขณะที่ runtime DI ไม่มี overhead ในการ build แต่เลื่อนการตรวจจับข้อผิดพลาดออกไป **ถ: Hilt มี component scope อะไรบ้าง และ map กับ lifecycle ของ Android อย่างไร?** `SingletonComponent` มีอยู่ตลอดอายุการใช้งานแอป `ActivityRetainedComponent` อยู่รอดผ่านการเปลี่ยนแปลง configuration `ViewModelComponent` มี scope ตาม lifecycle ของ ViewModel ส่วน `ActivityComponent`, `FragmentComponent` และ `ViewComponent` จะตามวงจรชีวิตของ Android lifecycle owner ที่เกี่ยวข้อง สามารถสร้าง custom scope เพื่อขยายลำดับชั้นนี้ได้ **ถ: Koin จัดการการ inject ViewModel ใน Jetpack Compose อย่างไร?** Koin มี `koinViewModel()` เป็นฟังก์ชัน Composable ที่สร้างหรือดึง ViewModel ที่มี scope ตาม `ViewModelStoreOwner` ที่ใกล้ที่สุด ตั้งแต่ Koin 4.2 เป็นต้นมา `koinNavViewModel()` จะ scope ViewModel ตาม entry ใน navigation graph เมื่อใช้[การนำทาง Jetpack Compose](/blog/android/jetpack-compose-interview-questions) **ถ: เมื่อไหร่ที่ Koin จะเป็นตัวเลือกที่ดีกว่า Hilt?** Koin เหมาะสมกว่าสำหรับโปรเจกต์ KMP (Hilt รองรับเฉพาะ Android) แอปขนาดเล็กถึงกลางที่เวลา build สำคัญกว่าเวลาเริ่มต้น และทีมที่ชอบ Kotlin DSL แบบชัดเจนมากกว่าการกำหนดค่าด้วย annotation โปรเจกต์ prototyping และ proof-of-concept ก็ได้ประโยชน์จากการตั้งค่า Koin ที่น้อยมาก > **ข้อผิดพลาดที่พบบ่อยในการสัมภาษณ์** > > การเรียก Koin ว่า "dependency injection framework" นั้นไม่ถูกต้องในทางเทคนิค Koin เป็น service locator — dependency ถูกดึงมาผ่าน `get()` แทนที่จะถูกส่งเข้ามาผ่าน constructor injection ผู้สัมภาษณ์ที่เข้าใจความแตกต่างนี้จะคาดหวังให้ผู้สมัครยอมรับจุดนี้ Hilt/Dagger ทำ dependency injection อย่างแท้จริงผ่าน constructor call ที่ถูกสร้างขึ้นอัตโนมัติ ## กรอบการตัดสินใจ: เลือกระหว่าง Hilt และ Koin | ปัจจัย | Hilt | Koin | |--------|------|------| | การตรวจสอบ graph | Compile-time | Runtime | | ผลกระทบต่อเวลา build | สูงกว่า (KSP codegen) | ไม่มี | | ต้นทุนการเริ่มต้น | เกือบเป็นศูนย์ | เพิ่มตามขนาด graph | | ความยากในการเรียนรู้ | สูงกว่า (แนวคิด Dagger) | ต่ำ (Kotlin DSL ล้วน) | | รองรับ KMP | ไม่ | ใช่ | | การรวมกับ Jetpack | ลึก (ทางการจาก Google) | ดี (ชุมชน) | | ความสามารถในการขยายทีม | แข็งแกร่งกว่า (scope ถูกบังคับ) | ยืดหยุ่น (อิงตามข้อตกลง) | | การทดสอบ | `@TestInstallIn` (compile-safe) | `loadKoinModules` (runtime) | ไม่มี framework ใดที่เหนือกว่าในทุกกรณี การเลือกขึ้นอยู่กับขนาดโปรเจกต์ ประสบการณ์ของทีม และแพลตฟอร์มเป้าหมาย ## สรุป - Hilt 2.57 พร้อม KSP มอบการตรวจสอบ dependency graph ณ compile-time และต้นทุนการเริ่มต้นเกือบเป็นศูนย์ เป็นตัวเลือกที่แข็งแกร่งกว่าสำหรับโปรเจกต์ Android แพลตฟอร์มเดียวขนาดใหญ่ที่ต้องการความน่าเชื่อถือสูง - Koin 4.2 พร้อม lazy modules และ `CoreResolverV2` ลดช่องว่างด้านประสิทธิภาพการเริ่มต้น พร้อมรักษาความไม่มี overhead ในการ build และความเข้ากันได้กับ KMP อย่างเต็มที่ - ทั้งสอง framework รองรับสถาปัตยกรรม multi-module แต่ Hilt บังคับขอบเขต scope ณ compile-time ขณะที่ Koin อาศัยข้อตกลงร่วมกันของทีม - สำหรับ[การเตรียมตัวสัมภาษณ์งาน Android](/technologies/android) การเข้าใจ trade-off ระหว่าง compile-time กับ runtime และความสามารถในการอธิบายว่าแต่ละ framework เหมาะสมเมื่อไหร่ถือเป็นพื้นฐานที่คาดหวัง - การย้ายระหว่าง framework ไม่ใช่เรื่องง่าย การเลือกครั้งแรกควรคำนึงถึงแนวทางแพลตฟอร์มและการขยายตัวในระยะยาวของโปรเจกต์ --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/android/android-dependency-injection-hilt-vs-koin