Hilt เป็นไลบรารี Dependency Injection สําหรับ Android ที่ช่วยลดการเขียนโค้ดซ้ำๆ ในการทำ Dependency Injection ด้วยตนเองในโปรเจ็กต์ การทำการแทรก Dependency ด้วยตนเองจะทำให้คุณต้องสร้างคลาสและ Dependency ของคลาสแต่ละรายการด้วยตนเอง รวมถึงใช้คอนเทนเนอร์เพื่อนำ Dependency กลับมาใช้ซ้ำและจัดการ Dependency
Hilt มีวิธีมาตรฐานในการใช้ DI ในแอปพลิเคชันโดยจัดหาคอนเทนเนอร์สำหรับคลาส Android ทุกคลาสในโปรเจ็กต์และจัดการวงจรชีวิตของคลาสเหล่านั้นโดยอัตโนมัติ Hilt สร้างขึ้นจากไลบรารี DI ที่ได้รับความนิยมอย่าง Dagger เพื่อให้ได้รับประโยชน์จากความถูกต้องของเวลาคอมไพล์ ประสิทธิภาพรันไทม์ ความสามารถในการปรับขนาด และการรองรับ Android Studio ที่ Dagger มีให้ ดูข้อมูลเพิ่มเติมได้ที่ด้ามจับและกริช
คู่มือนี้จะอธิบายแนวคิดพื้นฐานของ Hilt และคอนเทนเนอร์ที่สร้างขึ้น และยังมีการสาธิตวิธีเปิดเครื่องแอปที่มีอยู่เพื่อใช้งาน Hilt
การเพิ่มทรัพยากร Dependency
ก่อนอื่น ให้เพิ่มปลั๊กอิน hilt-android-gradle-plugin
ลงในไฟล์ build.gradle
รูทของโปรเจ็กต์ โดยทำดังนี้
ดึงดูด
plugins { ... id 'com.google.dagger.hilt.android' version '2.51.1' apply false }
Kotlin
plugins { ... id("com.google.dagger.hilt.android") version "2.51.1" apply false }
จากนั้นใช้ปลั๊กอิน Gradle และเพิ่ม Dependency ต่อไปนี้ในไฟล์ app/build.gradle
ดึงดูด
... plugins { id 'kotlin-kapt' id 'com.google.dagger.hilt.android' } android { ... } dependencies { implementation "com.google.dagger:hilt-android:2.51.1" kapt "com.google.dagger:hilt-compiler:2.51.1" } // Allow references to generated code kapt { correctErrorTypes true }
Kotlin
plugins { id("kotlin-kapt") id("com.google.dagger.hilt.android") } android { ... } dependencies { implementation("com.google.dagger:hilt-android:2.51.1") kapt("com.google.dagger:hilt-android-compiler:2.51.1") } // Allow references to generated code kapt { correctErrorTypes = true }
Hilt ใช้ฟีเจอร์ Java 8 หากต้องการเปิดใช้ Java 8 ในโปรเจ็กต์ ให้เพิ่มโค้ดต่อไปนี้ลงในไฟล์ app/build.gradle
Groovy
android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
Kotlin
android { ... compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } }
คลาสแอปพลิเคชัน Hilt
แอปทั้งหมดที่ใช้ Hilt ต้องมีคลาส Application
ที่มีคำอธิบายประกอบด้วย @HiltAndroidApp
@HiltAndroidApp
จะทริกเกอร์การสร้างโค้ดของ Hilt รวมถึงคลาสพื้นฐานสำหรับแอปพลิเคชันที่ทำหน้าที่เป็นคอนเทนเนอร์ทรัพยากร Dependency ระดับแอปพลิเคชัน
Kotlin
@HiltAndroidApp class ExampleApplication : Application() { ... }
Java
@HiltAndroidApp public class ExampleApplication extends Application { ... }
คอมโพเนนต์ Hilt ที่สร้างขึ้นนี้จะแนบกับวงจรการใช้งานของออบเจ็กต์ Application
และให้ทรัพยากร Dependency ไปยังวัตถุดังกล่าว นอกจากนี้ ยังเป็นคอมโพเนนต์หลักของแอป ซึ่งหมายความว่าคอมโพเนนต์อื่นๆ สามารถเข้าถึงข้อกําหนดเบื้องต้นที่คอมโพเนนต์หลักมีให้
แทรกทรัพยากร Dependency ลงในคลาส Android
เมื่อตั้งค่า Hilt ในคลาส Application
และมีคอมโพเนนต์ระดับแอปพลิเคชันแล้ว Hilt จะให้ทรัพยากรแก่คลาส Android อื่นๆ ที่มีคำอธิบายประกอบ @AndroidEntryPoint
ดังนี้
Kotlin
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { ... }
Java
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { ... }
ปัจจุบัน Hilt สนับสนุนคลาส Android ต่อไปนี้
Application
(โดยใช้@HiltAndroidApp
)ViewModel
(โดยใช้@HiltViewModel
)Activity
Fragment
View
Service
BroadcastReceiver
หากใส่คำอธิบายประกอบในคลาส Android ด้วย @AndroidEntryPoint
คุณจะต้องใส่คำอธิบายประกอบคลาส Android ที่ขึ้นอยู่กับคลาสดังกล่าวด้วย เช่น ถ้าคุณใส่คำอธิบายประกอบในส่วนย่อย คุณจะต้องใส่คำอธิบายประกอบให้กับกิจกรรมที่ใช้ส่วนย่อยนั้นด้วย
@AndroidEntryPoint
จะสร้างคอมโพเนนต์ Hilt แต่ละรายการสําหรับคลาส Android แต่ละคลาสในโปรเจ็กต์ คอมโพเนนต์เหล่านี้จะรับทรัพยากร Dependency จากคลาสระดับบนที่เกี่ยวข้องตามที่อธิบายไว้ในลำดับชั้นของคอมโพเนนต์ได้
หากต้องการรับทรัพยากร Dependency จากคอมโพเนนต์ ให้ใช้คำอธิบายประกอบ @Inject
เพื่อดำเนินการแทรกช่อง
Kotlin
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var analytics: AnalyticsAdapter ... }
Java
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { @Inject AnalyticsAdapter analytics; ... }
คลาสที่ Hilt ฉีดอาจมีคลาสพื้นฐานอื่นๆ ที่ใช้การฉีดด้วย
คลาสเหล่านั้นไม่จำเป็นต้องมีคำอธิบายประกอบ @AndroidEntryPoint
หากเป็นคลาสนามธรรม
ดูข้อมูลเพิ่มเติมเกี่ยวกับรอบการเรียกกลับของวงจรที่คลาส Android ได้รับการแทรก ดูอายุของคอมโพเนนต์
กำหนดการเชื่อมโยง Hilt
Hilt จำเป็นต้องทราบวิธีระบุอินสแตนซ์ของข้อกำหนดที่จำเป็นจากคอมโพเนนต์ที่เกี่ยวข้องเพื่อทำการแทรกฟิลด์ การเชื่อมโยงประกอบด้วยข้อมูลที่จำเป็นในการระบุอินสแตนซ์ของประเภทหนึ่งๆ เพื่อเป็นข้อมูลอ้างอิง
การฉีดคอนสตรัคเตอร์เป็นวิธีหนึ่งในการให้ข้อมูลการเชื่อมโยงกับ Hilt ใช้แอตทริบิวต์ @Inject
ในคอนสตรัคเตอร์ของคลาสเพื่อบอก Hilt ว่าจะจัดหาอินสแตนซ์ของคลาสนั้นอย่างไร
Kotlin
class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
Java
public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
พารามิเตอร์ของคอนสตรัคเตอร์ที่มีคำอธิบายประกอบของคลาสคือสิ่งที่ต้องพึ่งพาของคลาสนั้น ในตัวอย่างนี้ AnalyticsAdapter
ขึ้นต่อกันกับ AnalyticsService
ดังนั้น Hilt จึงต้องรู้วิธีระบุอินสแตนซ์ของ AnalyticsService
ด้วย
โมดูล Hilt
บางครั้งระบบจะฉีดประเภทไม่ได้ ปัญหานี้อาจเกิดขึ้นได้จากหลายสาเหตุ เช่น คุณไม่สามารถสร้างอินเทอร์เฟซได้ นอกจากนี้ คุณยังไม่สามารถแทรกประเภทที่คุณไม่ได้เป็นเจ้าของลงในตัวสร้าง เช่น คลาสจากไลบรารีภายนอก ในกรณีเหล่านี้ คุณสามารถระบุข้อมูลการเชื่อมโยงให้กับ Hilt ได้โดยใช้โมดูล Hilt
โมดูล Hilt คือคลาสที่มีคำอธิบายประกอบ @Module
เช่นเดียวกับโมดูล Dagger ข้อมูลนี้จะบอก Hilt ว่าจะจัดหาอินสแตนซ์ของบางประเภทอย่างไร คุณต้องกำกับเนื้อหาโมดูล Hilt ด้วย @InstallIn
เพื่อบอก Hilt ว่าจะใช้หรือติดตั้งโมดูลแต่ละรายการในคลาส Android ใด ซึ่งแตกต่างจากโมดูล Dagger
ทรัพยากร Dependency ที่คุณระบุในโมดูล Hilt จะพร้อมใช้งานในคอมโพเนนต์ที่สร้างขึ้นทั้งหมดซึ่งเชื่อมโยงกับคลาส Android ที่คุณติดตั้งโมดูล Hilt
แทรกอินสแตนซ์อินเทอร์เฟซด้วย @Binds
ลองดูตัวอย่าง AnalyticsService
หาก AnalyticsService
เป็นอินเทอร์เฟซ
คุณจะไม่สามารถแทรกโค้ดดังกล่าวได้ ให้ Hilt ทราบข้อมูลการเชื่อมโยงแทนโดยสร้างฟังก์ชันนามธรรมที่มีคำอธิบายประกอบ @Binds
ภายในโมดูล Hilt
คําอธิบายประกอบ @Binds
จะบอก Hilt ว่าต้องใช้การติดตั้งใช้งานใดเมื่อต้องจัดหาอินสแตนซ์ของอินเทอร์เฟซ
ฟังก์ชันที่มีคำอธิบายประกอบจะให้ข้อมูลต่อไปนี้แก่ Hilt
- ประเภทผลลัพธ์ของฟังก์ชันจะบอก Hilt ว่าฟังก์ชันมีอินเทอร์เฟซประเภทใด
- พารามิเตอร์ฟังก์ชันจะบอกให้ Hilt ทราบว่าควรติดตั้งใช้งานแบบใด
Kotlin
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 }
Java
public interface AnalyticsService { void analyticsMethods(); } // Constructor-injected, because Hilt needs to know how to // provide instances of AnalyticsServiceImpl, too. public class AnalyticsServiceImpl implements AnalyticsService { ... @Inject AnalyticsServiceImpl(...) { ... } } @Module @InstallIn(ActivityComponent.class) public abstract class AnalyticsModule { @Binds public abstract AnalyticsService bindAnalyticsService( AnalyticsServiceImpl analyticsServiceImpl ); }
โมดูล Hilt AnalyticsModule
มีคำอธิบายประกอบด้วย @InstallIn(ActivityComponent.class)
เนื่องจากคุณต้องการให้ Hilt แทรกการอ้างอิงดังกล่าวลงใน ExampleActivity
คำอธิบายประกอบนี้หมายความว่าทรัพยากร Dependency ทั้งหมดใน AnalyticsModule
มีอยู่ในกิจกรรมทั้งหมดของแอป
แทรกอินสแตนซ์ด้วย @Provides
อินเทอร์เฟซไม่ใช่กรณีเดียวที่คุณไม่สามารถแทรกประเภทลงในตัวสร้าง
นอกจากนี้ คุณยังใช้การฉีดคอนสตรัคเตอร์ไม่ได้หากไม่ได้เป็นเจ้าของคลาสเนื่องจากคลาสนั้นมาจากไลบรารีภายนอก (คลาสอย่าง Retrofit, OkHttpClient
หรือฐานข้อมูล Room) หรือหากต้องสร้างอินสแตนซ์ด้วยรูปแบบตัวสร้าง
ลองดูตัวอย่างก่อนหน้านี้ หากคุณไม่ได้เป็นเจ้าของAnalyticsService
คลาสโดยตรง คุณสามารถบอก Hilt ว่าจะระบุอินสแตนซ์ของประเภทนี้อย่างไรโดยการสร้างฟังก์ชันภายในโมดูล Hilt และกำกับเนื้อหาฟังก์ชันนั้นด้วย @Provides
ฟังก์ชันที่มีคำอธิบายประกอบจะส่งข้อมูลต่อไปนี้ไปยัง Hilt
- ประเภทผลลัพธ์ของฟังก์ชันจะบอก Hilt ว่าฟังก์ชันสร้างอินสแตนซ์ประเภทใด
- พารามิเตอร์ฟังก์ชันจะบอก Hilt เกี่ยวกับข้อกําหนดของประเภทที่เกี่ยวข้อง
- เนื้อความของฟังก์ชันจะบอก Hilt ว่าจะระบุอินสแตนซ์ของประเภทที่เกี่ยวข้องได้อย่างไร Hilt จะเรียกใช้เนื้อหาฟังก์ชันทุกครั้งที่ต้องระบุอินสแตนซ์ของประเภทนั้น
Kotlin
@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) } }
Java
@Module @InstallIn(ActivityComponent.class) public class AnalyticsModule { @Provides public static AnalyticsService provideAnalyticsService( // Potential dependencies of this type ) { return new Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService.class); } }
ระบุการเชื่อมโยงหลายรายการสำหรับประเภทเดียวกัน
ในกรณีที่ต้องการให้ Hilt ใช้งานประเภทเดียวกันกับ Dependency ที่แตกต่างกัน คุณต้องให้การเชื่อมโยงกับ Hilt หลายรายการ คุณกำหนดการเชื่อมโยงหลายรายการสำหรับประเภทเดียวกันได้โดยใช้ตัวระบุ
ตัวระบุคือคำอธิบายประกอบที่คุณใช้เพื่อระบุการเชื่อมโยงที่เฉพาะเจาะจงสำหรับประเภทหนึ่งๆ เมื่อประเภทนั้นมีการเชื่อมโยงหลายรายการที่กําหนดไว้
พิจารณาตัวอย่างต่อไปนี้ หากต้องการสกัดกั้นการเรียกไปยัง AnalyticsService
ให้ใช้ออบเจ็กต์ OkHttpClient
ที่มีตัวตัดข้อความ สำหรับบริการอื่นๆ คุณอาจต้องขัดจังหวะการโทรด้วยวิธีอื่น ในกรณีนี้ คุณต้องบอก Hilt ว่าจะให้การติดตั้งใช้งาน OkHttpClient
2 แบบได้อย่างไร
ก่อนอื่น ให้กําหนดตัวคําจํากัดที่คุณจะใช้ในการกำกับเนื้อหาวิธีการ @Binds
หรือ
@Provides
ดังนี้
Kotlin
@Qualifier @Retention(AnnotationRetention.BINARY) annotation class AuthInterceptorOkHttpClient @Qualifier @Retention(AnnotationRetention.BINARY) annotation class OtherInterceptorOkHttpClient
Java
@Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface AuthInterceptorOkHttpClient {} @Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface OtherInterceptorOkHttpClient {}
จากนั้น Hilt จะต้องทราบวิธีระบุอินสแตนซ์ของประเภทที่สอดคล้องกับตัวระบุแต่ละรายการ ในกรณีนี้ คุณสามารถใช้โมดูล Hilt กับ @Provides
ได้
ทั้ง 2 วิธีมีประเภทการแสดงผลเหมือนกัน แต่ตัวระบุจะติดป้ายกำกับว่าเป็นการเชื่อมโยง 2 แบบที่แตกต่างกัน ดังนี้
Kotlin
@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() } }
Java
@Module @InstallIn(ActivityComponent.class) public class NetworkModule { @AuthInterceptorOkHttpClient @Provides public static OkHttpClient provideAuthInterceptorOkHttpClient( AuthInterceptor authInterceptor ) { return new OkHttpClient.Builder() .addInterceptor(authInterceptor) .build(); } @OtherInterceptorOkHttpClient @Provides public static OkHttpClient provideOtherInterceptorOkHttpClient( OtherInterceptor otherInterceptor ) { return new OkHttpClient.Builder() .addInterceptor(otherInterceptor) .build(); } }
คุณสามารถแทรกประเภทที่ต้องการได้โดยการกำกับเนื้อหาในช่องหรือพารามิเตอร์ด้วยตัวระบุที่เกี่ยวข้อง ดังนี้
Kotlin
// 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: AppCompatActivity() { @AuthInterceptorOkHttpClient @Inject lateinit var okHttpClient: OkHttpClient }
Java
// As a dependency of another class. @Module @InstallIn(ActivityComponent.class) public class AnalyticsModule { @Provides public static AnalyticsService provideAnalyticsService( @AuthInterceptorOkHttpClient OkHttpClient okHttpClient ) { return new Retrofit.Builder() .baseUrl("https://example.com") .client(okHttpClient) .build() .create(AnalyticsService.class); } } // As a dependency of a constructor-injected class. public class ExampleServiceImpl ... { private final OkHttpClient okHttpClient; @Inject ExampleServiceImpl(@AuthInterceptorOkHttpClient OkHttpClient okHttpClient) { this.okHttpClient = okHttpClient; } } // At field injection. @AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { @AuthInterceptorOkHttpClient @Inject OkHttpClient okHttpClient; ... }
แนวทางปฏิบัติแนะนำคือ หากคุณเพิ่มตัวระบุลงในประเภทหนึ่ง ให้เพิ่มตัวระบุในทุกวิธีที่เป็นไปได้เพื่อระบุข้อมูลดังกล่าว การไม่ใส่ตัวระบุในการใช้งานพื้นฐานหรือทั่วไปมีแนวโน้มที่จะเกิดข้อผิดพลาดและอาจส่งผลให้ Hilt แทรกพึ่งพาที่ไม่ถูกต้อง
ตัวระบุที่กําหนดไว้ล่วงหน้าใน Hilt
Hilt มีตัวคําจํากัดที่กําหนดไว้ล่วงหน้า ตัวอย่างเช่น คุณอาจต้องใช้คลาส Context
จากแอปพลิเคชันหรือกิจกรรม Hilt จะระบุตัวระบุ @ApplicationContext
และ @ActivityContext
สมมติว่าคลาส AnalyticsAdapter
จากตัวอย่างต้องการบริบทของกิจกรรม โค้ดต่อไปนี้แสดงวิธีระบุบริบทกิจกรรมให้กับ AnalyticsAdapter
Kotlin
class AnalyticsAdapter @Inject constructor( @ActivityContext private val context: Context, private val service: AnalyticsService ) { ... }
Java
public class AnalyticsAdapter { private final Context context; private final AnalyticsService service; @Inject AnalyticsAdapter( @ActivityContext Context context, AnalyticsService service ) { this.context = context; this.service = service; } }
สำหรับการเชื่อมโยงที่กำหนดไว้ล่วงหน้าอื่นๆ ที่มีอยู่ใน Hilt โปรดดูการเชื่อมโยงเริ่มต้นของคอมโพเนนต์
คอมโพเนนต์ที่สร้างขึ้นสำหรับคลาส Android
สำหรับคลาส Android แต่ละคลาสที่คุณทำการแทรกลงในช่องได้ จะมีคอมโพเนนต์ Hilt ที่เชื่อมโยงซึ่งคุณอ้างอิงในแอตทริบิวต์ @InstallIn
ได้
คอมโพเนนต์ Hilt แต่ละรายการมีหน้าที่รับผิดชอบในการแทรกการเชื่อมโยงลงในคลาส Android ที่เกี่ยวข้อง
ตัวอย่างก่อนหน้านี้แสดงการใช้ ActivityComponent
ในโมดูล Hilt
Hilt มีคอมโพเนนต์ต่อไปนี้
คอมโพเนนต์ Hilt | Injector สำหรับ |
---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
ไม่มี |
ViewModelComponent |
ViewModel |
ActivityComponent |
Activity |
FragmentComponent |
Fragment |
ViewComponent |
View |
ViewWithFragmentComponent |
View กำกับเนื้อหาด้วย @WithFragmentBindings |
ServiceComponent |
Service |
อายุการใช้งานของคอมโพเนนต์
Hilt จะสร้างและทำลายอินสแตนซ์ของคลาสคอมโพเนนต์ที่สร้างขึ้นโดยอัตโนมัติตามวงจรชีวิตของคลาส Android ที่เกี่ยวข้อง
คอมโพเนนต์ที่สร้างขึ้น | สร้างเมื่อ: | ทำลายแล้วที่ |
---|---|---|
SingletonComponent |
Application#onCreate() |
Application ทำลายแล้ว |
ActivityRetainedComponent |
Activity#onCreate() |
Activity#onDestroy() |
ViewModelComponent |
สร้างแล้ว ViewModel รายการ |
ViewModel ทำลายแล้ว |
ActivityComponent |
Activity#onCreate() |
Activity#onDestroy() |
FragmentComponent |
Fragment#onAttach() |
Fragment#onDestroy() |
ViewComponent |
View#super() |
View ทำลายแล้ว |
ViewWithFragmentComponent |
View#super() |
View ทำลายแล้ว |
ServiceComponent |
Service#onCreate() |
Service#onDestroy() |
ขอบเขตของคอมโพเนนต์
โดยค่าเริ่มต้น การเชื่อมโยงทั้งหมดใน Hilt จะไม่มีขอบเขต ซึ่งหมายความว่าทุกครั้งที่แอปของคุณขอการเชื่อมโยง Hilt จะสร้างอินสแตนซ์ใหม่ของประเภทที่จำเป็น
ในตัวอย่างนี้ Hilt จะจัดเตรียม AnalyticsAdapter
เป็น Dependency ให้กับประเภทอื่นหรือผ่าน Field Injection (เช่น ใน ExampleActivity
) ทุกครั้งAnalyticsAdapter
อย่างไรก็ตาม Hilt ยังอนุญาตให้กําหนดขอบเขตการเชื่อมโยงไปยังคอมโพเนนต์ที่เฉพาะเจาะจงได้ด้วย Hilt จะสร้างการเชื่อมโยงที่กำหนดขอบเขตเพียง 1 ครั้งต่ออินสแตนซ์ของคอมโพเนนต์ที่มีขอบเขตการเชื่อมโยง และคำขอทั้งหมดสำหรับการเชื่อมโยงดังกล่าวจะใช้อินสแตนซ์เดียวกัน
ตารางด้านล่างแสดงคำอธิบายประกอบขอบเขตสำหรับคอมโพเนนต์ที่สร้างขึ้นแต่ละรายการ
คลาส Android | คอมโพเนนต์ที่สร้างขึ้น | ขอบเขต |
---|---|---|
Application |
SingletonComponent |
@Singleton |
Activity |
ActivityRetainedComponent |
@ActivityRetainedScoped |
ViewModel |
ViewModelComponent |
@ViewModelScoped |
Activity |
ActivityComponent |
@ActivityScoped |
Fragment |
FragmentComponent |
@FragmentScoped |
View |
ViewComponent |
@ViewScoped |
View กำกับเนื้อหาด้วย @WithFragmentBindings |
ViewWithFragmentComponent |
@ViewScoped |
Service |
ServiceComponent |
@ServiceScoped |
ในตัวอย่างนี้ หากคุณกําหนดขอบเขต AnalyticsAdapter
ให้กับ ActivityComponent
ใช้ @ActivityScoped
Hilt จะจัดเตรียมอินสแตนซ์ AnalyticsAdapter
เดียวกันตลอดอายุการใช้งานของกิจกรรมที่เกี่ยวข้อง
Kotlin
@ActivityScoped class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
Java
@ActivityScoped public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
สมมติว่า AnalyticsService
มีสถานะภายในที่กำหนดให้ต้องใช้อินสแตนซ์เดียวกันทุกครั้ง ไม่ใช่แค่ใน ExampleActivity
แต่ในทุกที่ในแอป ในกรณีนี้ การกําหนดขอบเขต AnalyticsService
ให้กับ SingletonComponent
นั้นเหมาะสม ผลที่ได้คือเมื่อใดก็ตามที่คอมโพเนนต์ต้องระบุอินสแตนซ์ของ AnalyticsService
ก็จะระบุอินสแตนซ์เดียวกันทุกครั้ง
ตัวอย่างต่อไปนี้แสดงวิธีกําหนดขอบเขตการเชื่อมโยงกับคอมโพเนนต์ในไฟล์ hilt.mod ขอบเขตของการเชื่อมโยงต้องตรงกับขอบเขตของคอมโพเนนต์ที่ติดตั้ง ดังนั้นในตัวอย่างนี้ คุณต้องติดตั้ง AnalyticsService
ใน SingletonComponent
แทน ActivityComponent
Kotlin
// 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) } }
Java
// If AnalyticsService is an interface. @Module @InstallIn(SingletonComponent.class) public abstract class AnalyticsModule { @Singleton @Binds public abstract AnalyticsService bindAnalyticsService( AnalyticsServiceImpl analyticsServiceImpl ); } // If you don't own AnalyticsService. @Module @InstallIn(SingletonComponent.class) public class AnalyticsModule { @Singleton @Provides public static AnalyticsService provideAnalyticsService() { return new Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService.class); } }
ดูข้อมูลเพิ่มเติมเกี่ยวกับขอบเขตคอมโพเนนต์ Hilt ได้ที่การกำหนดขอบเขตใน Android และ Hilt
ลําดับชั้นของคอมโพเนนต์
การติดตั้งโมดูลลงในคอมโพเนนต์ช่วยให้เข้าถึงการเชื่อมโยงได้ในฐานะที่เป็นการพึ่งพาการเชื่อมโยงอื่นๆ ในคอมโพเนนต์นั้นหรือในคอมโพเนนต์ย่อยที่อยู่ด้านล่างในลําดับชั้นของคอมโพเนนต์
การเชื่อมโยงเริ่มต้นของคอมโพเนนต์
คอมโพเนนต์ Hilt แต่ละรายการมาพร้อมกับชุดการเชื่อมโยงเริ่มต้นที่ Hilt สามารถแทรกเป็นข้อกําหนดไว้ในการเชื่อมโยงที่กําหนดเองของคุณ โปรดทราบว่าการเชื่อมโยงเหล่านี้สอดคล้องกับกิจกรรมทั่วไปและประเภทแฟรกเมนต์ ไม่ใช่คลาสย่อยที่เฉพาะเจาะจงใดๆ เนื่องจาก Hilt ใช้คําจํากัดความคอมโพเนนต์กิจกรรมรายการเดียวเพื่อแทรกกิจกรรมทั้งหมด แต่ละกิจกรรมจะมีอินสแตนซ์ของคอมโพเนนต์นี้แตกต่างกัน
คอมโพเนนต์ Android | การเชื่อมโยงเริ่มต้น |
---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
Application |
ViewModelComponent |
SavedStateHandle |
ActivityComponent |
Application , Activity |
FragmentComponent |
Application , Activity , Fragment |
ViewComponent |
Application , Activity , View |
ViewWithFragmentComponent |
Application , Activity , Fragment , View |
ServiceComponent |
Application , Service |
การเชื่อมโยงบริบทแอปพลิเคชันยังใช้ @ApplicationContext
ได้อีกด้วย
เช่น
Kotlin
class AnalyticsServiceImpl @Inject constructor( @ApplicationContext context: Context ) : AnalyticsService { ... } // The Application binding is available without qualifiers. class AnalyticsServiceImpl @Inject constructor( application: Application ) : AnalyticsService { ... }
Java
public class AnalyticsServiceImpl implements AnalyticsService { private final Context context; @Inject AnalyticsAdapter(@ApplicationContext Context context) { this.context = context; } } // The Application binding is available without qualifiers. public class AnalyticsServiceImpl implements AnalyticsService { private final Application application; @Inject AnalyticsAdapter(Application application) { this.application = application; } }
การเชื่อมโยงบริบทของกิจกรรมยังพร้อมใช้งานโดยใช้ @ActivityContext
ด้วย เช่น
Kotlin
class AnalyticsAdapter @Inject constructor( @ActivityContext context: Context ) { ... } // The Activity binding is available without qualifiers. class AnalyticsAdapter @Inject constructor( activity: FragmentActivity ) { ... }
Java
public class AnalyticsAdapter { private final Context context; @Inject AnalyticsAdapter(@ActivityContext Context context) { this.context = context; } } // The Activity binding is available without qualifiers. public class AnalyticsAdapter { private final FragmentActivity activity; @Inject AnalyticsAdapter(FragmentActivity activity) { this.activity = activity; } }
แทรกทรัพยากร Dependency ในชั้นเรียนที่ Hilt ไม่รองรับ
Hilt มาพร้อมการรองรับชั้นเรียน Android ที่พบบ่อยที่สุด อย่างไรก็ตาม คุณอาจต้องทำการฉีดฟิลด์ในคลาสที่ Hilt ไม่รองรับ
ในกรณีดังกล่าว คุณสามารถสร้างจุดเข้าโดยใช้@EntryPoint
คำอธิบายประกอบ จุดแรกเข้าคือขอบเขตระหว่างโค้ดที่จัดการโดย Hilt กับโค้ดที่ไม่ได้เป็น ซึ่งเป็นจุดที่โค้ดเข้าสู่กราฟของออบเจ็กต์ที่ Hilt จัดการเป็นครั้งแรก จุดแรกเข้าช่วยให้ Hilt ใช้โค้ดที่ Hilt ไม่สามารถจัดหา Dependency ภายในกราฟ Dependency ได้
เช่น Hilt ไม่รองรับผู้ให้บริการเนื้อหาโดยตรง หากต้องการให้ผู้ให้บริการเนื้อหาใช้ Hilt เพื่อรับทรัพยากร Dependency บางอย่าง คุณต้องกำหนดอินเทอร์เฟซที่มีการใส่คำอธิบายประกอบด้วย @EntryPoint
สำหรับการเชื่อมโยงแต่ละประเภทที่ต้องการและรวมตัวระบุ จากนั้นเพิ่ม @InstallIn
เพื่อระบุคอมโพเนนต์ที่จะติดตั้งจุดแรกเข้า ดังนี้
Kotlin
class ExampleContentProvider : ContentProvider() { @EntryPoint @InstallIn(SingletonComponent::class) interface ExampleContentProviderEntryPoint { fun analyticsService(): AnalyticsService } ... }
Java
public class ExampleContentProvider extends ContentProvider { @EntryPoint @InstallIn(SingletonComponent.class) interface ExampleContentProviderEntryPoint { public AnalyticsService analyticsService(); } ... }
หากต้องการเข้าถึงจุดแรกเข้า ให้ใช้เมธอดแบบคงที่ที่เหมาะสมจาก EntryPointAccessors
พารามิเตอร์ควรเป็นอินสแตนซ์คอมโพเนนต์หรือออบเจ็กต์ @AndroidEntryPoint
ที่ทำหน้าที่เป็นผู้ถือคอมโพเนนต์ ตรวจสอบว่าคอมโพเนนต์ที่คุณส่งเป็นพารามิเตอร์และEntryPointAccessors
เมธอดแบบคงที่ทั้งคู่ตรงกับคลาส Android ในคำอธิบายประกอบ @InstallIn
ในอินเทอร์เฟซ @EntryPoint
Kotlin
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() ... } }
Java
public class ExampleContentProvider extends ContentProvider { @Override public Cursor query(...) { Context appContext = getContext().getApplicationContext(); ExampleContentProviderEntryPoint hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class); AnalyticsService analyticsService = hiltEntryPoint.analyticsService(); } }
ในตัวอย่างนี้ คุณต้องใช้ ApplicationContext
เพื่อดึงข้อมูลจุดแรกเข้าเนื่องจากมีการติดตั้งจุดแรกเข้าใน SingletonComponent
หากการเชื่อมโยงที่คุณต้องการดึงข้อมูลอยู่ใน ActivityComponent
คุณจะใช้ ActivityContext
แทน
ด้ามและกริช
Hilt สร้างขึ้นจากไลบรารีการฉีดข้อมูล Dagger ซึ่งให้วิธีมาตรฐานในการรวม Dagger เข้ากับแอปพลิเคชัน Android
สำหรับ Dagger เป้าหมายของ Hilt มีดังนี้:
- เพื่อลดความซับซ้อนของโครงสร้างพื้นฐานที่เกี่ยวข้องกับ Dagger สําหรับแอป Android
- เพื่อสร้างชุดคอมโพเนนต์และขอบเขตมาตรฐานเพื่อให้การตั้งค่า ความสะดวกในการอ่าน และการแชร์โค้ดระหว่างแอปง่ายขึ้น
- เพื่อมอบวิธีง่ายๆ ในการจัดสรรการเชื่อมโยงต่างๆ ให้กับประเภทบิลด์ต่างๆ เช่น การทดสอบ การแก้ไขข้อบกพร่อง หรือรุ่น
เนื่องจากระบบปฏิบัติการ Android จะสร้างอินสแตนซ์ของคลาสเฟรมเวิร์กของตัวเองจำนวนมาก คุณจึงต้องใช้โค้ดที่ซ้ำกันจำนวนมากเมื่อใช้ Dagger ในแอป Android Hilt จะลดโค้ดสำเร็จรูปที่เกี่ยวข้องกับการใช้ Dagger ในแอปพลิเคชัน Android Hilt จะสร้างและ ข้อมูลต่อไปนี้โดยอัตโนมัติ
- คอมโพเนนต์สำหรับการผสานรวมคลาสเฟรมเวิร์ก Android กับ Dagger ที่คุณอาจต้องสร้างด้วยตนเอง
- คำอธิบายประกอบขอบเขตเพื่อใช้กับคอมโพเนนต์ที่ Hilt สร้างขึ้นโดยอัตโนมัติ
- การเชื่อมโยงที่กําหนดไว้ล่วงหน้าเพื่อแสดงคลาส Android เช่น
Application
หรือActivity
- ตัวคําจํากัดที่กําหนดไว้ล่วงหน้าเพื่อแสดง
@ApplicationContext
และ@ActivityContext
โค้ด Dagger และ Hilt อยู่ร่วมกันได้ในฐานโค้ดเดียวกัน อย่างไรก็ตาม ในกรณีส่วนใหญ่ เราขอแนะนำให้ใช้ Hilt เพื่อจัดการการใช้งาน Dagger ทั้งหมดใน Android หากต้องการย้ายข้อมูลโปรเจ็กต์ที่ใช้ Dagger ไปยัง Hilt โปรดดูคำแนะนำในการย้ายข้อมูลและการย้ายข้อมูลแอป Dagger ไปยัง Hilt ใน Codelab
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Hilt ได้ที่แหล่งข้อมูลเพิ่มเติมต่อไปนี้
ตัวอย่าง
Codelabs
บล็อก
- Dependency Injection ใน Android ด้วย Hilt
- การจำกัดขอบเขตใน Android และ Hilt
- การเพิ่มคอมโพเนนต์ลงในลําดับชั้น Hilt
- การย้ายข้อมูลแอป Google I/O ไปยัง Hilt