Hilt เป็นไลบรารี Dependency Injection สําหรับ Android ที่ช่วยลดการเขียนโค้ดซ้ำๆ ในการทำ Dependency Injection ด้วยตนเองในโปรเจ็กต์ การใช้ทรัพยากร Dependency ด้วยตนเอง injection ต้องการให้คุณสร้าง ทุกคลาสและทรัพยากร Dependency ทั้งหมดด้วยตนเอง และเพื่อนําคอนเทนเนอร์ไปใช้ซ้ำและ จัดการการอ้างอิง
Hilt ให้วิธีมาตรฐานในการใช้ DI ในแอปพลิเคชันของคุณโดยให้ คอนเทนเนอร์ของคลาส Android ทุกคลาสในโปรเจ็กต์ รวมถึงการจัดการวงจร โดยอัตโนมัติ Hilt สร้างขึ้นจากไลบรารี DI ที่ได้รับความนิยมอย่าง Dagger เพื่อให้ได้รับประโยชน์จากความถูกต้องของเวลาคอมไพล์ ประสิทธิภาพรันไทม์ ความสามารถในการปรับขนาด และการรองรับ Android Studio ที่ Dagger มีให้ สำหรับข้อมูลเพิ่มเติม โปรดดู Hilt และ กริช
คู่มือนี้จะอธิบายแนวคิดพื้นฐานของ Hilt และคอนเทนเนอร์ที่สร้างขึ้น รวมถึงมีการแสดงวิธีเริ่มต้นแอปที่มีอยู่เพื่อใช้ Hilt ด้วย
การเพิ่มทรัพยากร Dependency
ก่อนอื่น ให้เพิ่มปลั๊กอิน hilt-android-gradle-plugin
ลงในไฟล์ build.gradle
รูทของโปรเจ็กต์ โดยทำดังนี้
plugins { ... id 'com.google.dagger.hilt.android' version '2.51.1' apply false }
plugins { ... id("com.google.dagger.hilt.android") version "2.51.1" apply false }
จากนั้นใช้ปลั๊กอิน Gradle และเพิ่มการอ้างอิงเหล่านี้ใน
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 }
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
android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
android { ... compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } }
คลาสแอปพลิเคชัน Hilt
แอปทั้งหมดที่ใช้ Hilt ต้องมีคลาส Application
ที่มีคำอธิบายประกอบด้วย @HiltAndroidApp
@HiltAndroidApp
จะทริกเกอร์การสร้างโค้ดของ Hilt รวมถึงคลาสพื้นฐานสําหรับแอปพลิเคชันของคุณซึ่งทําหน้าที่เป็นคอนเทนเนอร์การพึ่งพาระดับแอปพลิเคชัน
@HiltAndroidApp class ExampleApplication : Application() { ... }
@HiltAndroidApp public class ExampleApplication extends Application { ... }
คอมโพเนนต์ Hilt ที่สร้างขึ้นนี้แนบอยู่กับออบเจ็กต์ Application
และช่วยมอบทรัพยากร Dependency ให้ นอกจากนี้ โมเดลยังอยู่ภายใต้
ซึ่งหมายความว่าคอมโพเนนต์อื่นๆ สามารถเข้าถึง
Dependency ที่มีให้
แทรก Dependency ลงในคลาส Android
เมื่อตั้งค่า Hilt ในชั้นเรียน Application
และระดับแอปพลิเคชันแล้ว
คอมโพเนนต์พร้อมใช้งาน Hilt สามารถให้ทรัพยากร Dependency ไปยังคลาส Android อื่นๆ ได้
ที่มีคำอธิบายประกอบ @AndroidEntryPoint
:
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { ... }
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { ... }
ปัจจุบัน Hilt รองรับคลาส Android ต่อไปนี้
Application
(โดยใช้@HiltAndroidApp
)ViewModel
(โดยใช้@HiltViewModel
)Activity
Fragment
View
Service
BroadcastReceiver
หากคุณกำกับเนื้อหาของคลาส Android ด้วย @AndroidEntryPoint
คุณต้องกำกับเนื้อหาของคลาส Android ที่ใช้คลาสดังกล่าวด้วย เช่น หากใส่คำอธิบายประกอบ
Fragment คุณต้องใส่คำอธิบายประกอบของกิจกรรมที่คุณใช้
ส่วนย่อย
@AndroidEntryPoint
จะสร้างคอมโพเนนต์ Hilt แต่ละรายการสําหรับคลาส Android แต่ละคลาสในโปรเจ็กต์ คอมโพเนนต์เหล่านี้จะรับทรัพยากร Dependency จาก
คลาสระดับบนที่เกี่ยวข้องตามที่อธิบายไว้ในคอมโพเนนต์
ลำดับชั้น
หากต้องการรับทรัพยากร Dependency จากคอมโพเนนต์ ให้ใช้คำอธิบายประกอบ @Inject
ในการดำเนินการ
การแทรกฟิลด์:
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var analytics: AnalyticsAdapter ... }
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { @Inject AnalyticsAdapter analytics; ... }
คลาสที่ Hilt ฉีดอาจมีคลาสพื้นฐานอื่นๆ ที่ใช้การฉีดด้วย
ชั้นเรียนเหล่านั้นไม่ต้องใช้คำอธิบายประกอบ @AndroidEntryPoint
หาก
ที่เป็นนามธรรม
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Callback ของวงจรที่คลาส Android ได้รับการแทรกในอายุการใช้งานของคอมโพเนนต์
กำหนดการเชื่อมโยง Hilt
หากต้องการทำการแทรกฟิลด์ Hilt จำเป็นต้องทราบวิธีระบุอินสแตนซ์ของข้อกำหนดที่จำเป็นจากคอมโพเนนต์ที่เกี่ยวข้อง การเชื่อมโยงประกอบด้วย ข้อมูลที่จำเป็นต่อการระบุอินสแตนซ์ของประเภทเป็นทรัพยากร Dependency
วิธีหนึ่งในการให้ข้อมูลการเชื่อมโยงกับ Hilt คือการแทรกตัวสร้าง ใช้แอตทริบิวต์ @Inject
ในคอนสตรัคเตอร์ของคลาสเพื่อบอก Hilt ว่าจะจัดหาอินสแตนซ์ของคลาสนั้นอย่างไร
class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
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 ว่าจะจัดหาอินสแตนซ์ของบางประเภทอย่างไร ซึ่งแตกต่างจากโมดูล Dagger
คุณต้องใส่คำอธิบายประกอบให้กับโมดูล 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 }
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
อินเทอร์เฟซไม่ได้เป็นเพียงกรณีเดียวที่คุณไม่สามารถสร้างการแทรกประเภท
การแทรกตัวสร้างเป็นไปไม่ได้เช่นกัน หากคุณไม่ใช่เจ้าของชั้นเรียนเนื่องจาก
มาจากไลบรารีภายนอก (ชั้นเรียนต่างๆ เช่น
การปรับปรุง
OkHttpClient
,
หรือฐานข้อมูลห้อง) หรือหากอินสแตนซ์ต้อง
สร้างขึ้นด้วยเครื่องมือสร้าง
รูปแบบ
ลองดูตัวอย่างก่อนหน้านี้ หากคุณไม่ได้เป็นเจ้าของ AnalyticsService
โดยตรง
คุณสามารถบอก Hilt ถึงวิธีจัดเตรียมอินสแตนซ์ประเภทนี้โดยสร้าง
ภายในโมดูล Hilt และใส่คำอธิบายประกอบฟังก์ชันนั้นด้วย @Provides
ฟังก์ชันที่มีคำอธิบายประกอบจะให้ข้อมูลต่อไปนี้แก่ Hilt
- ประเภทผลลัพธ์ของฟังก์ชันจะบอก Hilt ว่าฟังก์ชันสร้างอินสแตนซ์ประเภทใด
- พารามิเตอร์ฟังก์ชันจะบอก Hilt ถึงทรัพยากร Dependency ของประเภทที่เกี่ยวข้อง
- เนื้อความของฟังก์ชันจะบอก 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) } }
@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 เพื่อแสดงการติดตั้งใช้งานที่แตกต่างกัน เป็นประเภทการอ้างอิง คุณต้องระบุ Hilt ที่มีการเชื่อมโยงหลายรายการ คุณกำหนดการเชื่อมโยงหลายรายการสำหรับประเภทเดียวกันได้โดยใช้ตัวระบุ
ตัวระบุคือคำอธิบายประกอบที่คุณใช้เพื่อระบุการเชื่อมโยงที่เฉพาะเจาะจงสำหรับประเภทหนึ่งๆ เมื่อประเภทนั้นมีการเชื่อมโยงหลายรายการที่กําหนดไว้
พิจารณาตัวอย่างต่อไปนี้ หากต้องการขัดขวางการเรียก AnalyticsService
คุณสามารถใช้ออบเจ็กต์ OkHttpClient
ที่มีอินเตอร์เซปเตอร์ สำหรับบริการอื่นๆ คุณอาจต้องขัดจังหวะการโทรด้วยวิธีอื่น ในกรณีนี้ คุณต้องบอก Hilt ว่าจะให้การติดตั้งใช้งาน OkHttpClient
2 แบบได้อย่างไร
ก่อนอื่น ให้กำหนดคำขยายที่คุณจะใช้ในการใส่คำอธิบายประกอบ @Binds
หรือ
@Provides
วิธี:
@Qualifier @Retention(AnnotationRetention.BINARY) annotation class AuthInterceptorOkHttpClient @Qualifier @Retention(AnnotationRetention.BINARY) annotation class OtherInterceptorOkHttpClient
@Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface AuthInterceptorOkHttpClient {} @Qualifier @Retention(RetentionPolicy.RUNTIME) private @interface 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() } }
@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(); } }
คุณสามารถแทรกประเภทที่ต้องการได้โดยการกำกับเนื้อหาในช่องหรือพารามิเตอร์ด้วยตัวระบุที่เกี่ยวข้อง ดังนี้
// 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 }
// 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; ... }
สำหรับแนวทางปฏิบัติที่ดีที่สุด หากคุณเพิ่มตัวระบุลงในประเภท ให้เพิ่มตัวระบุลงใน วิธีต่างๆ ที่เป็นไปได้ในการให้ทรัพยากร Dependency นั้น การไม่ใส่ตัวระบุในการใช้งานพื้นฐานหรือทั่วไปมีแนวโน้มที่จะเกิดข้อผิดพลาดและอาจส่งผลให้ Hilt แทรกพึ่งพาที่ไม่ถูกต้อง
ตัวระบุที่กำหนดไว้ล่วงหน้าใน Hilt
Hilt มีตัวระบุที่กำหนดไว้ล่วงหน้า ตัวอย่างเช่น เนื่องจากคุณอาจต้องใช้คลาส Context
จากแอปพลิเคชันหรือกิจกรรม Hilt จึงมีตัวคําจํากัด @ApplicationContext
และ @ActivityContext
สมมติว่าคลาส AnalyticsAdapter
จากตัวอย่างต้องการบริบทของ
กิจกรรมนั้น โค้ดต่อไปนี้แสดงวิธีจัดเตรียมกิจกรรม
บริบทสำหรับ AnalyticsAdapter
:
class AnalyticsAdapter @Inject constructor( @ActivityContext private val context: Context, private val service: AnalyticsService ) { ... }
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 | หัวฉีดสำหรับ |
---|---|
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
อินสแตนซ์เดียวกัน
ตลอดระยะเวลาของกิจกรรมที่เกี่ยวข้อง:
@ActivityScoped class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
@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
// 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) } }
// 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 สามารถแทรกได้ Dependency ลงในการเชื่อมโยงที่คุณกำหนดเอง โปรดทราบว่าการเชื่อมโยงเหล่านี้สอดคล้องกับประเภทกิจกรรมและประเภทข้อมูลโค้ดทั่วไป ไม่ใช่กับคลาสย่อยที่เฉพาะเจาะจง นั่นเป็นเพราะ 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
ได้ด้วย
เช่น
class AnalyticsServiceImpl @Inject constructor( @ApplicationContext context: Context ) : AnalyticsService { ... } // The Application binding is available without qualifiers. class AnalyticsServiceImpl @Inject constructor( application: Application ) : AnalyticsService { ... }
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
ด้วย เช่น
class AnalyticsAdapter @Inject constructor( @ActivityContext context: Context ) { ... } // The Activity binding is available without qualifiers. class AnalyticsAdapter @Inject constructor( activity: FragmentActivity ) { ... }
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 เพื่อรับข้อมูลบางส่วน คุณต้องกำหนดอินเทอร์เฟซที่มีคำอธิบายประกอบด้วย @EntryPoint
สําหรับการเชื่อมโยงแต่ละประเภทที่ต้องการ และใส่ตัวระบุ จากนั้นเพิ่ม @InstallIn
เพื่อระบุคอมโพเนนต์ที่จะ
ติดตั้งจุดแรกเข้าดังนี้
class ExampleContentProvider : ContentProvider() { @EntryPoint @InstallIn(SingletonComponent::class) interface ExampleContentProviderEntryPoint { fun analyticsService(): AnalyticsService } ... }
public class ExampleContentProvider extends ContentProvider { @EntryPoint @InstallIn(SingletonComponent.class) interface ExampleContentProviderEntryPoint { public AnalyticsService analyticsService(); } ... }
หากต้องการเข้าถึงจุดแรกเข้า ให้ใช้เมธอดแบบคงที่ที่เหมาะสมจาก EntryPointAccessors
พารามิเตอร์ควรเป็นอินสแตนซ์ของคอมโพเนนต์หรือออบเจ็กต์ @AndroidEntryPoint
ที่ทำหน้าที่เป็นผู้ถือคอมโพเนนต์ ตรวจสอบว่า
ที่คอมโพเนนต์ที่คุณส่งเป็นพารามิเตอร์และ EntryPointAccessors
แบบคงที่
ทั้ง 2 วิธีนี้ตรงกับคลาส 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() ... } }
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 ไลบรารี Dependency Injection ที่มีวิธีมาตรฐานในการรวม 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 to Hilt โปรดดูการย้ายข้อมูล คู่มือและการย้ายข้อมูล แอป Dagger เพื่อ Hilt Codelab
แหล่งข้อมูลเพิ่มเติม
หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับ Hilt โปรดดูแหล่งข้อมูลเพิ่มเติมต่อไปนี้
ตัวอย่าง
Codelabs
บล็อก
- Dependency Injection ใน Android ด้วย Hilt
- การจำกัดขอบเขตใน Android และ ฮิลต์
- การเพิ่มคอมโพเนนต์ลงในลําดับชั้น Hilt
- การย้ายข้อมูลแอป Google I/O ไปยัง Hilt