Hilt เป็นไลบรารีทรัพยากร Dependency สำหรับการแทรกใน Android ซึ่งช่วยลดโค้ดสำเร็จรูปของการแทรกทรัพยากร Dependency ด้วยตนเองในโปรเจ็กต์ การแทรกทรัพยากร Dependency ด้วยตนเองกำหนดให้คุณต้องสร้าง ทุกคลาสและการอ้างอิงด้วยตนเอง รวมถึงใช้คอนเทนเนอร์เพื่อนำกลับมาใช้ซ้ำและ จัดการทรัพยากร Dependency
Hilt มีวิธีมาตรฐานในการใช้ DI ในแอปพลิเคชันโดยการจัดเตรียม คอนเทนเนอร์สำหรับทุกคลาส Android ในโปรเจ็กต์และจัดการวงจร โดยอัตโนมัติ Hilt สร้างขึ้นบนไลบรารี DI ยอดนิยมอย่าง Dagger เพื่อใช้ประโยชน์จาก ความถูกต้องขณะคอมไพล์ ประสิทธิภาพขณะรันไทม์ ความสามารถในการปรับขนาด และการรองรับ Android Studio ที่ Dagger มีให้ ดูข้อมูลเพิ่มเติมได้ที่ Hilt และ Dagger
คู่มือนี้อธิบายแนวคิดพื้นฐานของ Hilt และคอนเทนเนอร์ที่สร้างขึ้น นอกจากนี้ ยังมีตัวอย่างวิธีบูตสแตรปแอปที่มีอยู่ให้ใช้ Hilt
การเพิ่มทรัพยากร Dependency
ก่อนอื่น ให้เพิ่มปลั๊กอิน hilt-android-gradle-plugin ลงในรูทของไฟล์ build.gradle ของโปรเจ็กต์
Kotlin
plugins { ... id("com.google.dagger.hilt.android") version "2.57.1" apply false }
ดึงดูด
plugins { ... id 'com.google.dagger.hilt.android' version '2.57.1' apply false }
จากนั้นใช้ปลั๊กอิน Gradle และเพิ่มทรัพยากร Dependency เหล่านี้ในไฟล์
app/build.gradle
Kotlin
plugins { id("com.google.devtools.ksp") id("com.google.dagger.hilt.android") } android { ... } dependencies { implementation("com.google.dagger:hilt-android:2.57.1") ksp("com.google.dagger:hilt-android-compiler:2.57.1") }
ดึงดูด
... plugins { id 'com.google.devtools.ksp' id 'com.google.dagger.hilt.android' } android { ... } dependencies { implementation "com.google.dagger:hilt-android:2.57.1" ksp "com.google.dagger:hilt-compiler:2.57.1" }
หากต้องการตรวจสอบว่าโปรเจ็กต์ได้รับการกำหนดค่าสำหรับ Java 17 ซึ่งจำเป็นสำหรับ
Jetpack Compose และ Hilt เวอร์ชันต่างๆ ให้เพิ่มข้อมูลต่อไปนี้ลงในไฟล์ app/build.gradle
Kotlin
android { ... compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } }
ดึงดูด
android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } }
คลาสแอปพลิเคชัน Hilt
แอปทั้งหมดที่ใช้ Hilt ต้องมีคลาส
Application ที่มีคำอธิบายประกอบด้วย
@HiltAndroidApp
@HiltAndroidApp จะทริกเกอร์การสร้างโค้ดของ Hilt ซึ่งรวมถึงคลาสพื้นฐานสำหรับ แอปพลิเคชันของคุณที่ทำหน้าที่เป็นคอนเทนเนอร์ทรัพยากร Dependency ระดับแอปพลิเคชัน
@HiltAndroidApp class ExampleApplication : Application() { ... }
คอมโพเนนต์ Hilt ที่สร้างขึ้นนี้จะเชื่อมต่อกับวงจรของออบเจ็กต์ Application
และจัดเตรียมการอ้างอิงให้กับออบเจ็กต์ นอกจากนี้ ยังเป็นคอมโพเนนต์หลักของแอป ซึ่งหมายความว่าคอมโพเนนต์อื่นๆ สามารถเข้าถึงการอ้างอิงที่คอมโพเนนต์นี้มีให้ได้
แทรกการอ้างอิงในคลาส Android
เมื่อตั้งค่า Hilt ในคลาส Application และมีคอมโพเนนต์ระดับแอปพลิเคชัน แล้ว Hilt จะสามารถระบุทรัพยากร Dependency ไปยังคลาส Android อื่นๆ ที่มีคำอธิบายประกอบ @AndroidEntryPoint ได้
@AndroidEntryPoint class ExampleActivity : ComponentActivity() { ... }
ปัจจุบัน Hilt รองรับคลาส Android ต่อไปนี้
Application(โดยใช้@HiltAndroidApp)ViewModel(โดยใช้@HiltViewModel)ActivityServiceBroadcastReceiver
ใน Compose คุณไม่จำเป็นต้องใส่คำอธิบายประกอบใน Composable แต่ละรายการ แต่ให้
ใส่คำอธิบายประกอบรูท ComponentActivity ด้วย @AndroidEntryPoint แทน ซึ่งทำหน้าที่เป็นจุดแรกเข้า DI เดียวสำหรับลำดับชั้น UI ทั้งหมด คุณจึงเข้าถึง ViewModel ที่แทรกด้วย Hilt ได้โดยตรงภายในฟังก์ชันที่ประกอบกันได้
@AndroidEntryPoint จะสร้างคอมโพเนนต์ Hilt แต่ละรายการสำหรับคลาส Android แต่ละรายการในโปรเจ็กต์ คอมโพเนนต์เหล่านี้รับการอ้างอิงจากคลาสระดับบนสุดที่เกี่ยวข้องได้ตามที่อธิบายไว้ในลำดับชั้นของคอมโพเนนต์
หากต้องการรับการอ้างอิงจากคอมโพเนนต์ ให้ใช้คำอธิบายประกอบ @Inject เพื่อทำการ
แทรกฟิลด์
@AndroidEntryPoint class ExampleActivity : ComponentActivity() { @Inject lateinit var analytics: AnalyticsAdapter ... }
คลาสที่ Hilt แทรกอาจมีคลาสฐานอื่นๆ ที่ใช้การแทรกด้วย
คลาสเหล่านั้นไม่จำเป็นต้องมีคำอธิบายประกอบ @AndroidEntryPoint หากเป็นคลาส
นามธรรม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Lifecycle Callback ของวงจรใดที่ระบบจะแทรกลงในคลาส Android ได้ที่อายุการใช้งานของคอมโพเนนต์
กำหนดการเชื่อมโยง Hilt
หากต้องการทำการแทรกฟิลด์ Hilt ต้องทราบวิธีจัดเตรียมอินสแตนซ์ของ การอ้างอิงที่จำเป็นจากคอมโพเนนต์ที่เกี่ยวข้อง การเชื่อมโยงมีข้อมูลที่จำเป็นต่อการระบุอินสแตนซ์ของประเภทเป็นทรัพยากร Dependency
วิธีหนึ่งในการระบุข้อมูลการเชื่อมโยงให้กับ Hilt คือการแทรกผ่านตัวสร้าง ใช้@Injectคำอธิบายประกอบในตัวสร้างของคลาสเพื่อบอก Hilt ว่าจะจัดเตรียมอินสแตนซ์ของคลาสนั้นอย่างไร
class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
พารามิเตอร์ของเครื่องมือสร้างที่มีคำอธิบายประกอบของคลาสคือทรัพยากร Dependency ของคลาสนั้น ในตัวอย่างนี้ AnalyticsAdapter มี AnalyticsService เป็น การขึ้นต่อกัน ดังนั้น Hilt จึงต้องรู้วิธีจัดเตรียมอินสแตนซ์ของ
AnalyticsService ด้วย
โมดูล Hilt
บางครั้งระบบอาจไม่สามารถแทรกประเภทในตัวสร้างได้ ซึ่งอาจเกิดขึ้นได้จากหลายสาเหตุ เช่น คุณไม่สามารถแทรกอินเทอร์เฟซผ่านตัวสร้างได้ นอกจากนี้ คุณยัง ไม่สามารถแทรกประเภทที่คุณไม่ได้เป็นเจ้าของผ่านตัวสร้าง เช่น คลาสจาก ไลบรารีภายนอก ในกรณีเหล่านี้ คุณสามารถระบุข้อมูลการเชื่อมโยงให้กับ Hilt ได้โดยใช้โมดูล Hilt
โมดูล Hilt คือคลาสที่มีคำอธิบายประกอบ @Module ซึ่งจะให้คำสั่งแก่ Hilt เกี่ยวกับวิธีสร้างอินสแตนซ์ของประเภทที่ระบุผ่านการแทรกผ่านตัวสร้างไม่ได้ เช่น อินเทอร์เฟซหรือคลาสของบุคคลที่สาม นอกจากนี้ คุณยังต้อง
ใส่คำอธิบายประกอบทุกโมดูลด้วย @InstallIn เพื่อบอก Hilt ว่าจะใช้หรือติดตั้งโมดูลแต่ละโมดูลในคลาส Android ใด
ทรัพยากร Dependency ที่คุณระบุในโมดูล Hilt จะพร้อมใช้งานในคอมโพเนนต์ที่สร้างขึ้นทั้งหมดซึ่งเชื่อมโยงกับคลาส Android ที่คุณติดตั้งโมดูล Hilt
แทรกอินสแตนซ์ของอินเทอร์เฟซด้วย @Binds
ลองดูAnalyticsServiceตัวอย่าง หาก AnalyticsService เป็นอินเทอร์เฟซ
คุณจะแทรกเครื่องมือสร้างไม่ได้ แต่ให้ระบุข้อมูลการเชื่อมโยงกับ Hilt โดยการสร้างฟังก์ชันแบบนามธรรมที่ใส่คำอธิบายประกอบด้วย @Binds ภายในโมดูล Hilt
คำอธิบายประกอบ @Binds จะบอก Hilt ว่าควรใช้การติดตั้งใช้งานใดเมื่อต้องการ
ระบุอินสแตนซ์ของอินเทอร์เฟซ
ฟังก์ชันที่เพิ่มคำอธิบายประกอบจะให้ข้อมูลต่อไปนี้แก่ Hilt
- ประเภทการแสดงผลของฟังก์ชันจะบอก Hilt ว่าฟังก์ชันมีอินเทอร์เฟซใด อินสแตนซ์
- พารามิเตอร์ฟังก์ชันจะบอก Hilt ว่าควรใช้การติดตั้งใช้งานใด
interface AnalyticsService { fun analyticsMethods() } // Constructor-injected, because Hilt needs to know how to // provide instances of AnalyticsServiceImpl, too. class AnalyticsServiceImpl @Inject constructor( ... ) : AnalyticsService { ... } @Module @InstallIn(ActivityComponent::class) abstract class AnalyticsModule { @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService }
โมดูล Hilt AnalyticsModule มีคำอธิบายประกอบด้วย
@InstallIn(ActivityComponent.class) เนื่องจากคุณต้องการให้ Hilt แทรกการอ้างอิงนั้นลงใน ExampleActivity คำอธิบายประกอบนี้หมายความว่า
การอ้างอิงทั้งหมดใน AnalyticsModule จะพร้อมใช้งานในกิจกรรมทั้งหมดของแอป
แทรกอินสแตนซ์ด้วย @Provides
อินเทอร์เฟซไม่ใช่กรณีเดียวที่คุณไม่สามารถแทรกประเภทตัวสร้างได้
นอกจากนี้ คุณยังใช้การแทรกผ่านตัวสร้างไม่ได้หากไม่ได้เป็นเจ้าของคลาส เนื่องจากคลาสดังกล่าวมาจากไลบรารีภายนอก (เช่น คลาส Retrofit, OkHttpClient หรือฐานข้อมูล Room) หรือหากต้องสร้างอินสแตนซ์ด้วยรูปแบบตัวสร้าง
ลองดูตัวอย่างก่อนหน้า หากคุณไม่ได้เป็นเจ้าของAnalyticsService
คลาสโดยตรง คุณสามารถบอก Hilt วิธีจัดหาอินสแตนซ์ของประเภทนี้ได้โดยการสร้างฟังก์ชันภายในโมดูล Hilt และใส่คำอธิบายประกอบฟังก์ชันนั้นด้วย @Provides
ฟังก์ชันที่เพิ่มคำอธิบายประกอบจะให้ข้อมูลต่อไปนี้แก่ Hilt
- ประเภทการแสดงผลของฟังก์ชันจะบอก Hilt ว่าฟังก์ชันนี้มีอินสแตนซ์ ประเภทใด
- พารามิเตอร์ของฟังก์ชันจะบอก Hilt ถึงการอ้างอิงของประเภทที่เกี่ยวข้อง
- เนื้อหาของฟังก์ชันจะบอก Hilt ถึงวิธีจัดเตรียมอินสแตนซ์ของประเภทที่เกี่ยวข้อง Hilt จะเรียกใช้เนื้อหาของฟังก์ชันทุกครั้งที่ต้องระบุอินสแตนซ์ของประเภทนั้น
@Module @InstallIn(ActivityComponent::class) object AnalyticsModule { @Provides fun provideAnalyticsService( // Potential dependencies of this type ): AnalyticsService { return Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService::class.java) } }
ระบุการเชื่อมโยงหลายรายการสำหรับประเภทเดียวกัน
ในกรณีที่คุณต้องการให้ Hilt มีการใช้งานประเภทเดียวกันที่แตกต่างกันเป็น Dependency คุณต้องระบุ Binding หลายรายการให้ Hilt คุณสามารถ กำหนดการเชื่อมโยงหลายรายการสำหรับประเภทเดียวกันได้ด้วยตัวระบุ
ตัวระบุคือคำอธิบายประกอบที่คุณใช้เพื่อระบุการเชื่อมโยงที่เฉพาะเจาะจงสำหรับ ประเภทเมื่อประเภทนั้นมีการเชื่อมโยงหลายรายการที่กำหนดไว้
ลองดูตัวอย่าง หากต้องการสกัดกั้นการโทรไปยัง AnalyticsService คุณ
สามารถใช้ออบเจ็กต์ OkHttpClient ที่มี
อินเทอร์เซ็ปเตอร์ สำหรับ
บริการอื่นๆ คุณอาจต้องสกัดกั้นการโทรด้วยวิธีอื่น ในกรณีนี้ คุณต้องบอก Hilt ว่าจะจัดเตรียมการติดตั้งใช้งาน OkHttpClient ที่แตกต่างกัน 2 รายการได้อย่างไร
ก่อนอื่น ให้กำหนดตัวระบุที่คุณจะใช้เพื่อประกอบคำอธิบายประกอบของเมธอด @Binds หรือ
@Provides ดังนี้
@Qualifier @Retention(AnnotationRetention.BINARY) annotation class AuthInterceptorOkHttpClient @Qualifier @Retention(AnnotationRetention.BINARY) annotation class OtherInterceptorOkHttpClient
จากนั้น Hilt ต้องทราบวิธีจัดเตรียมอินสแตนซ์ของประเภทที่สอดคล้อง
กับตัวระบุแต่ละตัว ในกรณีนี้ คุณสามารถใช้โมดูล Hilt กับ @Provides ได้
ทั้ง 2 วิธีมีประเภทการแสดงผลเหมือนกัน แต่ตัวระบุจะติดป้ายกำกับเป็น 2 การเชื่อมโยงที่แตกต่างกัน ดังนี้
@Module @InstallIn(SingletonComponent::class) object NetworkModule { @AuthInterceptorOkHttpClient @Provides fun provideAuthInterceptorOkHttpClient( authInterceptor: AuthInterceptor ): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(authInterceptor) .build() } @OtherInterceptorOkHttpClient @Provides fun provideOtherInterceptorOkHttpClient( otherInterceptor: OtherInterceptor ): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(otherInterceptor) .build() } }
คุณสามารถแทรกประเภทที่ต้องการได้โดยการใส่คำอธิบายประกอบฟิลด์หรือ พารามิเตอร์ด้วยตัวระบุที่เกี่ยวข้อง
// As a dependency of another class. @Module @InstallIn(ActivityComponent::class) object AnalyticsModule { @Provides fun provideAnalyticsService( @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient ): AnalyticsService { return Retrofit.Builder() .baseUrl("https://example.com") .client(okHttpClient) .build() .create(AnalyticsService::class.java) } } // As a dependency of a constructor-injected class. class ExampleServiceImpl @Inject constructor( @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient ) : ... // At field injection. @AndroidEntryPoint class ExampleActivity: ComponentActivity() { @AuthInterceptorOkHttpClient @Inject lateinit var okHttpClient: OkHttpClient }
แนวทางปฏิบัติแนะนำคือ หากคุณเพิ่มตัวระบุให้กับประเภทหนึ่ง ให้เพิ่มตัวระบุให้กับวิธีทั้งหมด ที่เป็นไปได้ในการระบุการอ้างอิงนั้น การปล่อยให้การใช้งานพื้นฐานหรือทั่วไปไม่มีตัวระบุอาจทำให้เกิดข้อผิดพลาดและอาจส่งผลให้ Hilt แทรกทรัพยากร Dependency ที่ไม่ถูกต้อง
ตัวระบุที่กำหนดไว้ล่วงหน้าใน Hilt
Hilt มีตัวคัดกรองที่กำหนดไว้ล่วงหน้าบางรายการ ตัวอย่างเช่น เนื่องจากคุณอาจต้องใช้คลาส
Contextจากแอปพลิเคชันหรือกิจกรรม Hilt จึงมีตัวระบุ
@ApplicationContextและ @ActivityContext
สมมติว่าคลาส AnalyticsAdapter จากตัวอย่างต้องการบริบทของกิจกรรม โค้ดต่อไปนี้แสดงวิธีระบุบริบทกิจกรรม
ให้กับ AnalyticsAdapter
class AnalyticsAdapter @Inject constructor( @ActivityContext private val context: Context, private val service: AnalyticsService ) { ... }
ดูการเชื่อมโยงที่กำหนดไว้ล่วงหน้าอื่นๆ ที่มีใน Hilt ได้ที่การเชื่อมโยงเริ่มต้นของคอมโพเนนต์
คอมโพเนนต์ที่สร้างขึ้นสำหรับคลาส Android
สำหรับคลาส Android แต่ละคลาสที่คุณสามารถทำการแทรกฟิลด์ได้ จะมี คอมโพเนนต์ Hilt ที่เชื่อมโยงซึ่งคุณอ้างอิงได้ในคำอธิบายประกอบ @InstallIn
คอมโพเนนต์ Hilt แต่ละรายการมีหน้าที่ในการแทรกการเชื่อมโยงลงในคลาส Android ที่เกี่ยวข้อง
ตัวอย่างก่อนหน้านี้แสดงให้เห็นการใช้ ActivityComponent ในโมดูล Hilt
Hilt มีคอมโพเนนต์ต่อไปนี้
| คอมโพเนนต์ Hilt | Injector สำหรับ |
|---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
ไม่มี |
ViewModelComponent |
ViewModel |
ActivityComponent |
Activity |
ServiceComponent |
Service |
อายุการใช้งานของคอมโพเนนต์
Hilt จะสร้างและทำลายอินสแตนซ์ของคลาสคอมโพเนนต์ที่สร้างขึ้นโดยอัตโนมัติ ตามวงจรของคลาส Android ที่เกี่ยวข้อง
| คอมโพเนนต์ที่สร้างขึ้น | สร้างเมื่อ: | ทำลายแล้วที่ |
|---|---|---|
SingletonComponent |
Application#onCreate() |
Application ถูกทำลาย |
ActivityRetainedComponent |
Activity#onCreate() |
Activity#onDestroy() |
ViewModelComponent |
สร้างViewModelแล้ว |
ViewModel ทำลาย |
ActivityComponent |
Activity#onCreate() |
Activity#onDestroy() |
ServiceComponent |
Service#onCreate() |
Service#onDestroy() |
ขอบเขตของคอมโพเนนต์
โดยค่าเริ่มต้น การเชื่อมโยงทั้งหมดใน Hilt จะไม่มีขอบเขต ซึ่งหมายความว่าทุกครั้งที่ แอปขอการเชื่อมโยง Hilt จะสร้างอินสแตนซ์ใหม่ของประเภทที่จำเป็น
ในตัวอย่างนี้ ทุกครั้งที่ Hilt ระบุ AnalyticsAdapter เป็นทรัพยากร Dependency ถึง
ประเภทอื่นหรือผ่านการแทรกฟิลด์ (ดังใน ExampleActivity) Hilt จะระบุ
อินสแตนซ์ใหม่ของ AnalyticsAdapter
อย่างไรก็ตาม Hilt ยังอนุญาตให้กำหนดขอบเขตการเชื่อมโยงกับคอมโพเนนต์ที่เฉพาะเจาะจงได้ด้วย Hilt จะสร้างการเชื่อมโยงที่กำหนดขอบเขตเพียงครั้งเดียวต่ออินสแตนซ์ของคอมโพเนนต์ที่ การเชื่อมโยงกำหนดขอบเขตไว้ และคำขอทั้งหมดสำหรับการเชื่อมโยงนั้นจะใช้อินสแตนซ์เดียวกัน
ตารางด้านล่างแสดงคำอธิบายประกอบขอบเขตสำหรับคอมโพเนนต์ที่สร้างขึ้นแต่ละรายการ
| ชั้นเรียน Android | คอมโพเนนต์ที่สร้างขึ้น | ขอบเขต |
|---|---|---|
Application |
SingletonComponent |
@Singleton |
Activity |
ActivityRetainedComponent |
@ActivityRetainedScoped |
ViewModel |
ViewModelComponent |
@ViewModelScoped |
Activity |
ActivityComponent |
@ActivityScoped |
Service |
ServiceComponent |
@ServiceScoped |
ในตัวอย่าง หากคุณกำหนดขอบเขต AnalyticsAdapter ให้กับ ActivityComponent
โดยใช้ @ActivityScoped Hilt จะจัดเตรียมอินสแตนซ์เดียวกันของ AnalyticsAdapter
ตลอดอายุการใช้งานของกิจกรรมที่เกี่ยวข้อง
@ActivityScoped class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
สมมติว่า AnalyticsService มีสถานะภายในที่ต้องใช้
อินสแตนซ์เดียวกันทุกครั้ง ไม่ใช่แค่ใน ExampleActivity แต่ทุกที่ใน
แอป ในกรณีนี้ การกำหนดขอบเขต AnalyticsService ให้กับ
SingletonComponent จะเหมาะสม ผลลัพธ์คือเมื่อใดก็ตามที่คอมโพเนนต์ต้องการ
จัดเตรียมอินสแตนซ์ของ AnalyticsService คอมโพเนนต์จะจัดเตรียมอินสแตนซ์เดียวกันทุกครั้ง
ตัวอย่างต่อไปนี้แสดงวิธีกำหนดขอบเขตการเชื่อมโยงกับคอมโพเนนต์ในโมดูล Hilt
ขอบเขตของการเชื่อมโยงต้องตรงกับขอบเขตของคอมโพเนนต์ที่ติดตั้ง ดังนั้นในตัวอย่างนี้ คุณต้องติดตั้ง AnalyticsService ใน SingletonComponent แทน ActivityComponent
// If AnalyticsService is an interface. @Module @InstallIn(SingletonComponent::class) abstract class AnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService } // If you don't own AnalyticsService. @Module @InstallIn(SingletonComponent::class) object AnalyticsModule { @Singleton @Provides fun provideAnalyticsService(): AnalyticsService { return Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService::class.java) } }
ดูข้อมูลเพิ่มเติมเกี่ยวกับขอบเขตของคอมโพเนนต์ Hilt ได้ที่การกำหนดขอบเขตใน Android และ Hilt
ลำดับชั้นของคอมโพเนนต์
การติดตั้งโมดูลลงในคอมโพเนนต์จะช่วยให้เข้าถึงการเชื่อมโยงของโมดูลได้ในฐานะทรัพยากร Dependency ของการเชื่อมโยงอื่นๆ ในคอมโพเนนต์นั้นหรือในคอมโพเนนต์ย่อยใดๆ ที่อยู่ใต้คอมโพเนนต์นั้นในลำดับชั้นของคอมโพเนนต์
การเชื่อมโยงเริ่มต้นของคอมโพเนนต์
คอมโพเนนต์ Hilt แต่ละรายการมาพร้อมกับการเชื่อมโยงเริ่มต้นชุดหนึ่งที่ Hilt สามารถแทรกเป็น การอ้างอิงในการเชื่อมโยงที่กำหนดเองของคุณเองได้ โปรดทราบว่าการเชื่อมโยงเหล่านี้สอดคล้องกับประเภทกิจกรรมทั่วไป และไม่ได้สอดคล้องกับคลาสย่อยใดๆ โดยเฉพาะ เนื่องจาก Hilt ใช้คําจํากัดความคอมโพเนนต์กิจกรรมเดียวเพื่อแทรกกิจกรรมทั้งหมด แต่ละกิจกรรมจะมีอินสแตนซ์ของคอมโพเนนต์นี้ที่แตกต่างกัน
| คอมโพเนนต์ Android | การเชื่อมโยงเริ่มต้น |
|---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
Application |
ViewModelComponent |
SavedStateHandle |
ActivityComponent |
Application, Activity |
ServiceComponent |
Application, Service |
การเชื่อมโยงบริบทของแอปพลิเคชันยังใช้ได้โดยใช้ @ApplicationContext ด้วย
เช่น
class AnalyticsServiceImpl @Inject constructor( @ApplicationContext context: Context ) : AnalyticsService { ... } // The Application binding is available without qualifiers. class AnalyticsServiceImpl @Inject constructor( application: Application ) : AnalyticsService { ... }
การเชื่อมโยงบริบทของกิจกรรมยังใช้ได้โดยใช้ @ActivityContext เช่น
class AnalyticsAdapter @Inject constructor( @ActivityContext context: Context ) { ... } // The Activity binding is available without qualifiers. class AnalyticsAdapter @Inject constructor( activity: ComponentActivity ) { ... }
แทรกการอ้างอิงในคลาสที่ Hilt ไม่รองรับ
ใน Compose รูปแบบมาตรฐานคือการแทรกทรัพยากร Dependency ลงใน @HiltViewModel โดยใช้การแทรกผ่านตัวสร้าง จากนั้นใช้ hiltViewModel() ภายใน Composable เพื่อเข้าถึง ViewModel แม้ว่า Hilt จะรองรับคลาส Android ที่พบบ่อยที่สุด แต่คุณก็อาจยังพบคลาสที่ไม่รองรับซึ่งคุณต้องทำการแทรกฟิลด์
ในกรณีดังกล่าว คุณสามารถสร้างจุดแรกเข้าได้โดยใช้@EntryPoint
คำอธิบายประกอบ จุดแรกเข้าคือขอบเขตระหว่างโค้ดที่จัดการโดย Hilt
กับโค้ดที่ไม่ได้จัดการ ซึ่งเป็นจุดที่โค้ดเข้าสู่กราฟของ
ออบเจ็กต์ที่ Hilt จัดการเป็นครั้งแรก Entry Point ช่วยให้ Hilt ใช้โค้ดที่ Hilt ไม่ได้จัดการเพื่อระบุทรัพยากร Dependency ภายในกราฟ Dependency ได้
เช่น Hilt ไม่รองรับผู้ให้บริการเนื้อหาโดยตรง หากต้องการให้ Content Provider ใช้ Hilt เพื่อรับการอ้างอิงบางอย่าง คุณต้องกำหนดอินเทอร์เฟซที่มีคำอธิบายประกอบ @EntryPoint สำหรับการเชื่อมโยงแต่ละประเภทที่คุณต้องการ และรวมตัวระบุ จากนั้นเพิ่ม @InstallIn เพื่อระบุคอมโพเนนต์ที่จะ
ติดตั้งจุดแรกเข้าดังนี้
class ExampleContentProvider : ContentProvider() { @EntryPoint @InstallIn(SingletonComponent::class) interface ExampleContentProviderEntryPoint { fun analyticsService(): AnalyticsService } ... }
หากต้องการเข้าถึงจุดแรกเข้า ให้ใช้เมธอดแบบคงที่ที่เหมาะสมจาก
EntryPointAccessors พารามิเตอร์ควรเป็นอินสแตนซ์ของคอมโพเนนต์หรือ
@AndroidEntryPointออบเจ็กต์ที่ทำหน้าที่เป็นที่เก็บคอมโพเนนต์ ตรวจสอบว่าคอมโพเนนต์ที่คุณส่งเป็นพารามิเตอร์และเมธอด EntryPointAccessors static ตรงกับคลาส Android ในคำอธิบายประกอบ @InstallIn ในอินเทอร์เฟซ @EntryPoint
class ExampleContentProvider: ContentProvider() { ... override fun query(...): Cursor { val appContext = context?.applicationContext ?: throw IllegalStateException() val hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java) val analyticsService = hiltEntryPoint.analyticsService() ... } }
ในตัวอย่างนี้ คุณต้องใช้ ApplicationContext เพื่อดึงข้อมูลจุดแรกเข้า
เนื่องจากมีการติดตั้งจุดแรกเข้าใน SingletonComponent หากต้องการดึงข้อมูลการเชื่อมโยงที่อยู่ใน ActivityComponent คุณจะต้องใช้ ActivityContext แทน
Hilt and Dagger
Hilt เป็นคลังที่แนะนำอย่างเป็นทางการสำหรับการจัดการทรัพยากร Dependency ใน Android โดยมีวิธีที่ได้มาตรฐาน มีความเห็น และมีประสิทธิภาพในการติดตั้งใช้งาน การแทรกทรัพยากร Dependency ในแอปพลิเคชัน ซึ่งได้รับการเพิ่มประสิทธิภาพสำหรับ Jetpack Compose และสถาปัตยกรรมแบบกิจกรรมเดียวโดยเฉพาะ
เป้าหมายของ Hilt มีดังนี้
- เพื่อสร้างชุดคอมโพเนนต์และขอบเขตมาตรฐานเพื่ออำนวยความสะดวกในการตั้งค่า ความสามารถในการอ่าน และการแชร์โค้ดระหว่างแอป
- เพื่อให้การจัดสรรการเชื่อมโยงต่างๆ ให้กับบิลด์ประเภทต่างๆ เป็นเรื่องง่าย เช่น การทดสอบ การแก้ไขข้อบกพร่อง หรือการเผยแพร่
เนื่องจากระบบปฏิบัติการ Android สร้างอินสแตนซ์ของคลาสเฟรมเวิร์กของตัวเองจำนวนมาก การใช้ Dagger ในแอป Android จึงกำหนดให้คุณต้องเขียนโค้ดบอยเลอร์เพลตจำนวนมาก Hilt ช่วยลดโค้ด Boilerplate ที่เกี่ยวข้องกับ การใช้ Dagger ในแอปพลิเคชัน Android Hilt จะสร้างและ จัดเตรียมสิ่งต่อไปนี้โดยอัตโนมัติ
- คอมโพเนนต์สำหรับการผสานรวมคลาสเฟรมเวิร์ก Android กับ Dagger ซึ่งคุณ จะต้องสร้างด้วยตนเอง
- คำอธิบายประกอบขอบเขตเพื่อใช้กับคอมโพเนนต์ที่ Hilt สร้างขึ้น โดยอัตโนมัติ
- การเชื่อมโยงที่กำหนดไว้ล่วงหน้าเพื่อแสดงคลาส Android เช่น
ApplicationหรือActivity - ตัวระบุที่กำหนดไว้ล่วงหน้าเพื่อแสดง
@ApplicationContextและ@ActivityContext
โค้ด Dagger และ Hilt สามารถอยู่ร่วมกันในฐานของโค้ดเดียวกันได้ อย่างไรก็ตาม ในกรณีส่วนใหญ่ การใช้ Hilt เพื่อจัดการการใช้งาน Dagger ทั้งหมดใน Android จะดีที่สุด หากต้องการย้ายข้อมูล โปรเจ็กต์ที่ใช้ Dagger ไปยัง Hilt โปรดดูคู่มือ การย้ายข้อมูล
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Hilt ได้ที่แหล่งข้อมูลเพิ่มเติมต่อไปนี้
ตัวอย่าง
บล็อก
- การแทรกทรัพยากร Dependency ใน Android ด้วย Hilt
- การกำหนดขอบเขตใน Android และ Hilt
- การเพิ่มคอมโพเนนต์ลงในลำดับชั้นของ Hilt
- การย้ายข้อมูลแอป Google I/O ไปยัง Hilt