แนวคิดและการใช้งาน Jetpack Compose
Hilt เป็นไลบรารีทรัพยากร Dependency สำหรับการแทรกใน Android ซึ่งช่วยลดโค้ดสำเร็จรูปของการแทรกทรัพยากร Dependency ด้วยตนเองในโปรเจ็กต์ การแทรกทรัพยากร Dependency ด้วยตนเองกำหนดให้คุณต้องสร้าง ทุกคลาสและการอ้างอิงด้วยตนเอง รวมถึงใช้คอนเทนเนอร์เพื่อนำกลับมาใช้ซ้ำและ จัดการทรัพยากร Dependency
Hilt มีวิธีมาตรฐานในการใช้ DI ในแอปพลิเคชันโดยการจัดเตรียม คอนเทนเนอร์สำหรับทุกคลาส Android ในโปรเจ็กต์และจัดการวงจร โดยอัตโนมัติ Hilt สร้างขึ้นบนไลบรารี DI ยอดนิยมอย่าง Dagger เพื่อใช้ประโยชน์จาก ความถูกต้องขณะคอมไพล์ ประสิทธิภาพขณะรันไทม์ ความสามารถในการปรับขนาด และการรองรับ Android Studio ที่ Dagger มีให้ ดูข้อมูลเพิ่มเติมได้ที่ Hilt และ Dagger
คู่มือนี้อธิบายแนวคิดพื้นฐานของ Hilt และคอนเทนเนอร์ที่สร้างขึ้น นอกจากนี้ ยังมีตัวอย่างวิธีบูตสแตรปแอปที่มีอยู่ให้ใช้ Hilt
คลาสแอปพลิเคชัน Hilt
แอปทั้งหมดที่ใช้ Hilt ต้องมีคลาส
Application ที่มีคำอธิบายประกอบด้วย
@HiltAndroidApp
@HiltAndroidApp จะทริกเกอร์การสร้างโค้ดของ Hilt ซึ่งรวมถึงคลาสพื้นฐานสำหรับ แอปพลิเคชันของคุณที่ทำหน้าที่เป็นคอนเทนเนอร์ทรัพยากร Dependency ระดับแอปพลิเคชัน
Kotlin
@HiltAndroidApp class ExampleApplication : Application() { ... }
Java
@HiltAndroidApp public class ExampleApplication extends Application { ... }
คอมโพเนนต์ Hilt ที่สร้างขึ้นนี้จะเชื่อมต่อกับวงจรของออบเจ็กต์ Application
และจัดเตรียมการอ้างอิงให้กับออบเจ็กต์ นอกจากนี้ ยังเป็นคอมโพเนนต์หลักของแอป ซึ่งหมายความว่าคอมโพเนนต์อื่นๆ สามารถเข้าถึงการอ้างอิงที่คอมโพเนนต์นี้มีให้ได้
แทรกการอ้างอิงในคลาส Android
เมื่อตั้งค่า Hilt ในคลาส Application และมีคอมโพเนนต์ระดับแอปพลิเคชัน แล้ว Hilt จะระบุทรัพยากร Dependency ไปยังคลาส Android อื่นๆ ที่มีคำอธิบายประกอบ @AndroidEntryPoint ได้
Kotlin
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { ... }
Java
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { ... }
ปัจจุบัน Hilt รองรับคลาส Android ต่อไปนี้
Application(โดยใช้@HiltAndroidApp)ViewModel(โดยใช้@HiltViewModel)ActivityFragmentViewServiceBroadcastReceiver
หากใส่คำอธิบายประกอบในคลาส Android ด้วย @AndroidEntryPoint คุณจะต้องใส่คำอธิบายประกอบในคลาส Android ที่ขึ้นอยู่กับคลาสดังกล่าวด้วย เช่น หากใส่คำอธิบายประกอบใน
ส่วน คุณต้องใส่คำอธิบายประกอบในกิจกรรมใดก็ตามที่คุณใช้
ส่วนนั้นด้วย
@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 หากเป็นคลาส
นามธรรม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Lifecycle Callback ของวงจรใดที่ระบบจะแทรกลงในคลาส 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; } ... }
พารามิเตอร์ของเครื่องมือสร้างที่มีคำอธิบายประกอบของคลาสคือทรัพยากร Dependency ของคลาสนั้น ในตัวอย่างนี้ 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 แทรกทรัพยากร Dependency ที่ไม่ถูกต้อง
ตัวระบุที่กำหนดไว้ล่วงหน้าใน 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 ถึง
ประเภทอื่นหรือผ่านการแทรกฟิลด์ (ดังใน 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
ลำดับชั้นของคอมโพเนนต์
การติดตั้งโมดูลลงในคอมโพเนนต์จะช่วยให้เข้าถึงการเชื่อมโยงของโมดูลได้ในฐานะทรัพยากร Dependency ของการเชื่อมโยงอื่นๆ ในคอมโพเนนต์นั้นหรือในคอมโพเนนต์ย่อยใดๆ ที่อยู่ใต้คอมโพเนนต์นั้นในลำดับชั้นของคอมโพเนนต์
การเชื่อมโยงเริ่มต้นของคอมโพเนนต์
คอมโพเนนต์ 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
Hilt สร้างขึ้นบนไลบรารีการแทรกทรัพยากร Dependency ของDagger โดยให้วิธีมาตรฐานในการรวม Dagger เข้ากับแอปพลิเคชัน Android
เป้าหมายของ Hilt เกี่ยวกับ Dagger มีดังนี้
- เพื่อลดความซับซ้อนของโครงสร้างพื้นฐานที่เกี่ยวข้องกับ Dagger สำหรับแอป Android
- เพื่อสร้างชุดคอมโพเนนต์และขอบเขตมาตรฐานเพื่ออำนวยความสะดวกในการตั้งค่า ความสามารถในการอ่าน และการแชร์โค้ดระหว่างแอป
- เพื่อให้จัดสรรการเชื่อมโยงต่างๆ ให้กับบิลด์ประเภทต่างๆ ได้ง่าย เช่น การทดสอบ การแก้ไขข้อบกพร่อง หรือการเผยแพร่
เนื่องจากระบบปฏิบัติการ Android สร้างอินสแตนซ์ของคลาสเฟรมเวิร์กของตัวเองจำนวนมาก การใช้ Dagger ในแอป Android จึงกำหนดให้คุณต้องเขียนโค้ดบอยเลอร์เพลตจำนวนมาก Hilt ช่วยลดโค้ด Boilerplate ที่เกี่ยวข้องกับ การใช้ Dagger ในแอปพลิเคชัน Android Hilt จะสร้างและ จัดเตรียมสิ่งต่อไปนี้โดยอัตโนมัติ
- คอมโพเนนต์สำหรับการผสานรวมคลาสเฟรมเวิร์ก Android กับ Dagger ซึ่งคุณ จะต้องสร้างด้วยตนเอง
- กำหนดขอบเขตของคำอธิบายประกอบเพื่อใช้กับคอมโพเนนต์ที่ Hilt สร้างขึ้น โดยอัตโนมัติ
- การเชื่อมโยงที่กำหนดไว้ล่วงหน้าเพื่อแสดงคลาส Android เช่น
ApplicationหรือActivity - ตัวระบุที่กำหนดไว้ล่วงหน้าเพื่อแสดง
@ApplicationContextและ@ActivityContext
โค้ด Dagger และ Hilt สามารถอยู่ร่วมกันในฐานของโค้ดเดียวกันได้ อย่างไรก็ตาม ในกรณีส่วนใหญ่ การใช้ Hilt เพื่อจัดการการใช้งาน Dagger ทั้งหมดใน Android จะดีที่สุด หากต้องการย้ายข้อมูล โปรเจ็กต์ที่ใช้ Dagger ไปยัง Hilt โปรดดูคู่มือ การย้ายข้อมูล