Hilt เป็นไลบรารีการขึ้นต่อกันสำหรับการแทรกใน Android ซึ่งช่วยลดโค้ดสำเร็จรูป ของการแทรกการขึ้นต่อกันด้วยตนเองในโปรเจ็กต์ การแทรก Dependency ด้วยตนเองกำหนดให้คุณต้องสร้าง ทุกคลาสและ Dependency ของคลาสนั้นด้วยตนเอง รวมถึงใช้คอนเทนเนอร์เพื่อนำกลับมาใช้ใหม่และ จัดการ Dependency
Hilt มีวิธีมาตรฐานในการใช้ DI ในแอปพลิเคชันโดยการจัดเตรียม คอนเทนเนอร์สำหรับทุกคลาส Android ในโปรเจ็กต์และจัดการวงจร โดยอัตโนมัติ Hilt สร้างขึ้นบนไลบรารี DI ยอดนิยมอย่าง Dagger เพื่อใช้ประโยชน์จาก ความถูกต้องขณะคอมไพล์ ประสิทธิภาพขณะรันไทม์ ความสามารถในการปรับขนาด และการรองรับ Android Studio ที่ Dagger มีให้ ดูข้อมูลเพิ่มเติมได้ที่ Hilt และ Dagger
คู่มือนี้อธิบายแนวคิดพื้นฐานของ Hilt และคอนเทนเนอร์ที่สร้างขึ้น นอกจากนี้ ยังมีตัวอย่างวิธีเริ่มต้นใช้งานแอปที่มีอยู่เพื่อใช้ Hilt
การเพิ่มทรัพยากร Dependency
ก่อนอื่น ให้เพิ่มปลั๊กอิน hilt-android-gradle-plugin
ลงในรูทของไฟล์ build.gradle
ของโปรเจ็กต์
Groovy
plugins { ... id 'com.google.dagger.hilt.android' version '2.56.2' apply false }
Kotlin
plugins { ... id("com.google.dagger.hilt.android") version "2.56.2" apply false }
จากนั้นใช้ปลั๊กอิน Gradle และเพิ่มทรัพยากร Dependency เหล่านี้ในไฟล์
app/build.gradle
Groovy
... plugins { id 'com.google.devtools.ksp' id 'com.google.dagger.hilt.android' } android { ... } dependencies { implementation "com.google.dagger:hilt-android:2.56.2" ksp "com.google.dagger:hilt-compiler:2.56.2" }
Kotlin
plugins { id("com.google.devtools.ksp") id("com.google.dagger.hilt.android") } android { ... } dependencies { implementation("com.google.dagger:hilt-android:2.56.2") ksp("com.google.dagger:hilt-android-compiler:2.56.2") }
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 ซึ่งรวมถึงคลาสพื้นฐานสำหรับ
แอปพลิเคชันของคุณที่ทำหน้าที่เป็นคอนเทนเนอร์การอ้างอิงระดับแอปพลิเคชัน
Kotlin
@HiltAndroidApp class ExampleApplication : Application() { ... }
Java
@HiltAndroidApp public class ExampleApplication extends Application { ... }
คอมโพเนนต์ Hilt ที่สร้างขึ้นนี้จะเชื่อมต่อกับวงจรของออบเจ็กต์ Application
และให้การอ้างอิงแก่คอมโพเนนต์ นอกจากนี้ ยังเป็นคอมโพเนนต์หลักของแอป ซึ่งหมายความว่าคอมโพเนนต์อื่นๆ สามารถเข้าถึงทรัพยากร 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 ที่ขึ้นอยู่กับคลาสดังกล่าวด้วย
เช่น หากใส่คำอธิบายประกอบใน
Fragment คุณต้องใส่คำอธิบายประกอบในกิจกรรมใดก็ตามที่ใช้
Fragment นั้นด้วย
@AndroidEntryPoint
จะสร้างคอมโพเนนต์ Hilt แต่ละรายการสำหรับคลาส Android
แต่ละรายการในโปรเจ็กต์ คอมโพเนนต์เหล่านี้รับการอ้างอิงจากคลาสระดับบนสุดที่เกี่ยวข้องได้ตามที่อธิบายไว้ในลำดับชั้นของคอมโพเนนต์
หากต้องการรับการอ้างอิงจากคอมโพเนนต์ ให้ใช้คำอธิบายประกอบ @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 ต้องทราบวิธีจัดเตรียมอินสแตนซ์ของ การอ้างอิงที่จำเป็นจากคอมโพเนนต์ที่เกี่ยวข้อง การเชื่อมโยงมีข้อมูลที่จำเป็นต่อการระบุอินสแตนซ์ของประเภทเป็นทรัพยากร Dependency
วิธีหนึ่งในการระบุข้อมูลการเชื่อมโยงให้กับ 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
คำอธิบายประกอบนี้หมายความว่า
การอ้างอิงทั้งหมดใน 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 หลายแบบ คุณต้องระบุ Binding หลายรายการให้ Hilt คุณสามารถ กำหนดการเชื่อมโยงหลายรายการสำหรับประเภทเดียวกันได้ด้วยตัวระบุ
ตัวระบุคือคำอธิบายประกอบที่คุณใช้เพื่อระบุการเชื่อมโยงที่เฉพาะเจาะจงสำหรับประเภทหนึ่งๆ เมื่อประเภทนั้นมีการเชื่อมโยงหลายรายการที่กำหนดไว้
ลองดูตัวอย่าง หากต้องการสกัดกั้นการโทรไปยัง AnalyticsService
คุณ
สามารถใช้ออบเจ็กต์ OkHttpClient
ที่มี
อินเทอร์เซ็ปเตอร์ สำหรับ
บริการอื่นๆ คุณอาจต้องสกัดกั้นการโทรด้วยวิธีอื่น ในกรณีนี้ คุณต้องบอก Hilt ว่าจะจัดเตรียมการติดตั้งใช้งาน 2 แบบที่แตกต่างกันของ
OkHttpClient
ได้อย่างไร
ก่อนอื่น ให้กำหนดตัวระบุที่คุณจะใช้เพื่อประกอบคำอธิบายประกอบของเมธอด @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
เป็นการอ้างอิงถึง
ประเภทอื่นหรือผ่านการแทรกฟิลด์ (ดังใน ExampleActivity
) Hilt จะระบุ
อินสแตนซ์ใหม่ของ AnalyticsAdapter
อย่างไรก็ตาม Hilt ยังอนุญาตให้กำหนดขอบเขตการเชื่อมโยงกับคอมโพเนนต์ที่เฉพาะเจาะจงได้ด้วย Hilt จะสร้างการเชื่อมโยงที่กำหนดขอบเขตเพียงครั้งเดียวต่ออินสแตนซ์ของคอมโพเนนต์ที่ การเชื่อมโยงกำหนดขอบเขตไว้ และคำขอทั้งหมดสำหรับการเชื่อมโยงนั้นจะใช้อินสแตนซ์เดียวกัน
ตารางด้านล่างแสดงคำอธิบายประกอบขอบเขตสำหรับคอมโพเนนต์ที่สร้างขึ้นแต่ละรายการ
คลาส 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
ขอบเขตของการเชื่อมโยงต้องตรงกับขอบเขตของคอมโพเนนต์ที่ติดตั้ง ดังนั้นในตัวอย่างนี้ คุณต้องติดตั้ง 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 สามารถแทรกเป็น การอ้างอิงในการเชื่อมโยงที่กำหนดเองของคุณเองได้ โปรดทราบว่าการเชื่อมโยงเหล่านี้สอดคล้อง กับกิจกรรมและประเภท Fragment ทั่วไป ไม่ใช่คลาสย่อยใดๆ เนื่องจาก 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; } }
แทรกการอ้างอิงในคลาสที่ Hilt ไม่รองรับ
Hilt มาพร้อมกับการรองรับคลาส Android ที่พบบ่อยที่สุด อย่างไรก็ตาม คุณอาจต้องทำการแทรกฟิลด์ในคลาสที่ Hilt ไม่รองรับ
ในกรณีดังกล่าว คุณสามารถสร้างจุดแรกเข้าโดยใช้@EntryPoint
คำอธิบายประกอบ จุดแรกเข้าคือขอบเขตระหว่างโค้ดที่ Hilt จัดการ
กับโค้ดที่ไม่ได้จัดการ ซึ่งเป็นจุดที่โค้ดเข้าสู่กราฟของ
ออบเจ็กต์ที่ Hilt จัดการเป็นครั้งแรก Entry Point ช่วยให้ Hilt ใช้โค้ดที่ Hilt ไม่ได้จัดการเพื่อระบุทรัพยากร Dependency ภายในกราฟ Dependency ได้
เช่น Hilt ไม่รองรับผู้ให้บริการเนื้อหาโดยตรง หากต้องการให้ผู้ให้บริการเนื้อหาใช้ Hilt เพื่อรับการอ้างอิงบางอย่าง คุณต้องกำหนดอินเทอร์เฟซที่มีคำอธิบายประกอบด้วย @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
static
ตรงกับคลาส 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
เป้าหมายของ Hilt เกี่ยวกับ Dagger มีดังนี้
- เพื่อลดความซับซ้อนของโครงสร้างพื้นฐานที่เกี่ยวข้องกับ Dagger สำหรับแอป Android
- เพื่อสร้างชุดคอมโพเนนต์และขอบเขตมาตรฐานเพื่ออำนวยความสะดวกในการตั้งค่า ความสามารถในการอ่าน และการแชร์โค้ดระหว่างแอป
- เพื่อให้การจัดสรรการเชื่อมโยงต่างๆ ให้กับบิลด์ประเภทต่างๆ เป็นเรื่องง่าย เช่น การทดสอบ การแก้ไขข้อบกพร่อง หรือการเผยแพร่
เนื่องจากระบบปฏิบัติการ Android สร้างอินสแตนซ์ของคลาสเฟรมเวิร์กของตัวเองจำนวนมาก การใช้ Dagger ในแอป Android จึงกำหนดให้คุณต้องเขียนโค้ดบอยเลอร์เพลตจำนวนมาก Hilt ช่วยลดโค้ดเทมเพลตที่เกี่ยวข้องกับการใช้ Dagger ในแอปพลิเคชัน Android Hilt จะสร้างและ จัดเตรียมสิ่งต่อไปนี้โดยอัตโนมัติ
- คอมโพเนนต์สำหรับการผสานรวมคลาสเฟรมเวิร์ก Android กับ Dagger ซึ่งคุณ จะต้องสร้างด้วยตนเอง
- ขอบเขตของคำอธิบายประกอบเพื่อใช้กับคอมโพเนนต์ที่ Hilt สร้างขึ้น โดยอัตโนมัติ
- การเชื่อมโยงที่กำหนดไว้ล่วงหน้าเพื่อแสดงคลาส Android เช่น
Application
หรือActivity
- ตัวระบุที่กำหนดไว้ล่วงหน้าเพื่อแสดง
@ApplicationContext
และ@ActivityContext
โค้ด Dagger และ Hilt สามารถอยู่ร่วมกันในฐานของโค้ดฐานเดียวกันได้ อย่างไรก็ตาม ในกรณีส่วนใหญ่ การใช้ Hilt เพื่อจัดการการใช้งาน Dagger ทั้งหมดใน Android จะดีที่สุด หากต้องการย้ายข้อมูล โปรเจ็กต์ที่ใช้ Dagger ไปยัง Hilt โปรดดูคำแนะนำ ในการย้ายข้อมูลและCodelab การย้ายข้อมูล แอป Dagger ไปยัง Hilt
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Hilt ได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้
ตัวอย่าง
Codelabs
บล็อก
- การแทรก Dependency ใน Android ด้วย Hilt
- การกำหนดขอบเขตใน Android และ Hilt
- การเพิ่มคอมโพเนนต์ลงในลำดับชั้นของ Hilt
- การย้ายข้อมูลแอป Google I/O ไปยัง Hilt