Hilt היא ספרייה להזרקת תלויות ל-Android שמפחיתה את הקוד הסטנדרטי הנדרש להזרקת תלויות באופן ידני בפרויקט. כדי לבצע הזרקה ידנית של יחסי תלות, צריך ליצור כל כיתה ואת יחסי התלות שלה באופן ידני, ולהשתמש בקונטיינרים כדי לעשות שימוש חוזר ביחסי התלות ולנהל אותם.
Hilt מספק דרך רגילה להשתמש ב-DI באפליקציה, על ידי מתן קונטיינרים לכל כיתה של Android בפרויקט וניהול מחזור החיים שלהם באופן אוטומטי. Hilt מבוסס על ספריית ה-DI הפופולרית Dagger, כדי ליהנות מהנכסים הבאים של Dagger: תקינות בזמן הידור, ביצועים בסביבת זמן ריצה, יכולת התאמה לעומס ותמיכה ב-Android Studio. מידע נוסף זמין במאמר Hilt and Dagger.
במדריך הזה מוסבר על המושגים הבסיסיים של Hilt ועל הקונטיינרים שנוצרים על ידו. בנוסף, תוכלו לראות הדגמה של איך להפעיל אפליקציה קיימת כדי להשתמש ב-Hilt.
הוספת יחסי תלות
קודם כול, מוסיפים את הפלאגין hilt-android-gradle-plugin
לקובץ build.gradle
ברמה הבסיסית של הפרויקט:
Groovy
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 ומוסיפים את יחסי התלות האלה בקובץ app/build.gradle
:
Groovy
... 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, כולל מחלקת בסיס לאפליקציה שמשמש כקונטיינר של יחסי התלות ברמת האפליקציה.
Kotlin
@HiltAndroidApp class ExampleApplication : Application() { ... }
Java
@HiltAndroidApp public class ExampleApplication extends Application { ... }
רכיב Hilt שנוצר מצורף למחזור החיים של האובייקט Application
ומספק לו יחסי תלות. בנוסף, הוא הרכיב ההורה של האפליקציה, כלומר רכיבים אחרים יכולים לגשת ליחסי התלות שהוא מספק.
הזרקת יחסי תלות לכיתות ב-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 בפרויקט. הרכיבים האלה יכולים לקבל יחסי תלות ממערכי ההורים שלהם, כפי שמתואר בקטע היררכיית רכיבים.
כדי לקבל יחסי תלות מרכיב, משתמשים בהערה @Inject
כדי לבצע הזרקה של שדה:
Kotlin
@AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var analytics: AnalyticsAdapter ... }
Java
@AndroidEntryPoint public class ExampleActivity extends AppCompatActivity { @Inject AnalyticsAdapter analytics; ... }
לכיתות ש-Hilt מזריצה יכולות להיות כיתות בסיס אחרות שמשתמשות גם בהזרקה.
אם הכיתות האלה הן ערכיות, אין צורך בהערה @AndroidEntryPoint
.
למידע נוסף על קריאה חוזרת (callback) של מחזור חיים שדרכה מוזרק שיעור של Android, תוכלו לקרוא את המאמר משכי החיים של רכיבים.
הגדרת קישורים של Hilt
כדי לבצע הזרקה של שדות, Hilt צריך לדעת איך לספק מופעים של יחסי התלות הנדרשים מהרכיב המתאים. קישור מכיל את המידע הנדרש כדי לספק מכונות של סוג מסוים כיחס תלות.
אחת הדרכים לספק מידע על קישור ל-Hilt היא הזרקה לברירת המחדל (constructor injection). כדי להורות ל-Hilt איך לספק מכונות של הכיתה הזו, משתמשים בהערה @Inject
ב-constructor של הכיתה:
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
לפעמים אי אפשר להחדיר סוג באמצעות ה-constructor. יכולות להיות לכך כמה סיבות. לדוגמה, אי אפשר להחדיר ממשק באמצעות ה-constructor. בנוסף, אי אפשר להחדיר סוג שאינו בבעלותכם ליצירת ה-constructor, למשל כיתה מספרייה חיצונית. במקרים כאלה, אפשר לספק ל-Hilt את פרטי הקישור באמצעות מודולים של Hilt.
מודול Hilt הוא מחלקה עם הערה @Module
. בדומה למודול של Dagger, הוא מעדכן את Hilt איך לספק מופעים של סוגים מסוימים. בניגוד למודולים של Dagger, צריך להוסיף הערה למודולים של Hilt באמצעות @InstallIn
כדי להודיע ל-Hilt באיזו כיתה של Android ישתמשו בכל מודול או באיזו כיתה יתקינו אותו.
יחסי התלות שאתם מספקים במודולים של Hilt זמינים בכל הרכיבים שנוצרים ומשויכים לכיתה של Android שבה מתקינים את מודול Hilt.
הזרקה של מופע ממשק באמצעות @Binds
נבחן את הדוגמה AnalyticsService
. אם AnalyticsService
הוא ממשק, אי אפשר להחדיר אותו באמצעות ה-constructor. במקום זאת, צריך לספק ל-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
ממשקים הם לא המקרה היחיד שבו אי אפשר להחדיר סוג באמצעות ה-constructor.
אי אפשר גם להזריק ל-constructor אם אין לכם בעלות על הכיתה כי היא מגיעה מספרייה חיצונית (כיתות כמו Retrofit, OkHttpClient
או מסדי נתונים של Room), או אם צריך ליצור את המופעים באמצעות תבנית ה-builder.
נבחן את הדוגמה הקודמת. אם אתם לא הבעלים הישירים של הכיתה 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 יספק הטמעות שונות של אותו סוג של יחסי תלות, עליכם לספק ל-Hilt כמה קישורים. אפשר להגדיר מספר קישורים לאותו סוג באמצעות מסננים.
מגדיר הוא הערה שמשמשת לזיהוי קישור ספציפי לסוג, כשיש לסוג הזה כמה קישורים מוגדרים.
דוגמה: אם אתם צריכים ליירט קריאות ל-AnalyticsService
, תוכלו להשתמש באובייקט OkHttpClient
עם מנטרה. בשירותים אחרים, יכול להיות שתצטרכו ליירט שיחות בדרך אחרת. במקרה כזה, צריך להורות ל-Hilt איך לספק שתי הטמעות שונות של 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
.
לשתי השיטות יש את אותו סוג החזרה, אבל המאפיינים המזהים מסמנים אותן כשתי קישורויות שונות:
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 | הזרקה ל- |
---|---|
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 יכול להזריק כיחסי תלות לקישור בהתאמה אישית שלכם. חשוב לזכור שהקישורים האלה תואמים לסוגי הפעילות והקטע הכלליים, ולא לתת-סוג ספציפי כלשהו. הסיבה לכך היא ש-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
כדי לקשר את ה-context של האפליקציה.
לדוגמה:
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 יש תמיכה בסוגי ה-class הנפוצים ביותר ב-Android. עם זאת, יכול להיות שתצטרכו לבצע הזרקה של שדות בכיתות שלא נתמכות על ידי Hilt.
במקרים כאלה, אפשר ליצור נקודת כניסה באמצעות ההערה @EntryPoint
. נקודת הכניסה היא הגבול בין קוד שמנוהל על ידי Hilt לבין קוד שלא מנוהל על ידו. זוהי הנקודה שבה הקוד נכנס לראשונה לתרשים האובייקטים שמנוהל על ידי Hilt. נקודות הכניסה מאפשרות ל-Hilt להשתמש בקוד שלא ניתן לספק לו יחסי תלות בתרשים יחסי התלות.
לדוגמה, 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
תואמים לכיתה של 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 and Dagger
Hilt מבוסס על ספריית ההזרקה של יחסי התלות Dagger, ומספק דרך רגילה לשילוב Dagger באפליקציית Android.
בהשוואה ל-Dagger, המטרות של Hilt הן:
- כדי לפשט את התשתית שקשורה ל-Dagger לאפליקציות ל-Android.
- כדי ליצור קבוצה רגילה של רכיבים והיקפים, וכך להקל על ההגדרה, הקריאה ושיתוף הקוד בין אפליקציות.
- כדי לספק דרך קלה להקצות קישורים שונים לסוגים שונים של גרסאות build, כמו בדיקה, ניפוי באגים או גרסה זמינה.
מאחר שמערכת ההפעלה של 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
בלוגים
- הזרקת יחסי תלות ב-Android באמצעות Hilt
- היקף הבדיקה ב-Android וב-Hilt
- הוספת רכיבים להיררכיה של Hilt
- העברת אפליקציית Google I/O ל-Hilt