Hilt היא ספרייה להזרקת תלות (dependency injection) ל-Android שמפחיתה את כמות הקוד הסטנדרטי שנדרש כדי לבצע הזרקת תלות ידנית בפרויקט. כדי לבצע הזרקת תלות ידנית, צריך ליצור כל מחלקה ואת יחסי התלות שלה באופן ידני, ולהשתמש במאגרי מידע כדי לעשות שימוש חוזר ביחסי התלות ולנהל אותם.
Hilt מספק דרך סטנדרטית להשתמש ב-DI באפליקציה שלכם. הוא מספק קונטיינרים לכל מחלקה של Android בפרויקט ומנהל את מחזורי החיים שלהם באופן אוטומטי. Hilt מבוסס על ספריית ה-DI הפופולרית Dagger כדי ליהנות מהתכונות הבאות ש-Dagger מספקת: נכונות בזמן הקומפילציה, ביצועים בזמן הריצה, יכולת הרחבה ותמיכה ב-Android Studio. מידע נוסף זמין במאמר Hilt and Dagger.
במדריך הזה מוסברים המושגים הבסיסיים של Hilt והקונטיינרים שנוצרים ממנו. הוא כולל גם הדגמה של תהליך האתחול של אפליקציה קיימת כדי להשתמש ב-Hilt.
הוספת יחסי תלות
קודם כול, מוסיפים את הפלאגין hilt-android-gradle-plugin
לקובץ build.gradle
הבסיסי של הפרויקט:
מגניב
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 ומוסיפים את יחסי התלות האלה בקובץ app/build.gradle
:
מגניב
... 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
:
מגניב
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 היא הזרקת בנאי. משתמשים בהערה @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
לפעמים אי אפשר להזריק סוג באמצעות בנאי. יכולות להיות לכך כמה סיבות. לדוגמה, אי אפשר להשתמש ב-constructor-inject בממשק. בנוסף, אי אפשר להשתמש בהזרקה דרך בנאי לסוג שאינו בבעלותכם, כמו מחלקה מספרייה חיצונית. במקרים כאלה, אפשר לספק ל-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 ); }
המודול AnalyticsModule
של Hilt מסומן ב-@InstallIn(ActivityComponent.class)
כי רוצים ש-Hilt יזריק את התלות הזו ל-ExampleActivity
. ההערה הזו מציינת שכל התלויות ב-AnalyticsModule
זמינות בכל הפעילויות של האפליקציה.
הזרקת מופעים באמצעות @Provides
ממשקים הם לא המקרה היחיד שבו אי אפשר להזריק סוג באמצעות 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
עם interceptor. בשירותים אחרים, יכול להיות שתצטרכו ליירט שיחות בדרך אחרת. במקרה כזה, צריך להגדיר ל-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; } }
במאמר Component default bindings (התאמות ברירת מחדל של רכיבים) מפורטות התאמות מוגדרות מראש אחרות שזמינות ב-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 destroyed |
ActivityRetainedComponent |
Activity#onCreate() |
Activity#onDestroy() |
ViewModelComponent |
יצרת ViewModel |
ViewModel destroyed |
ActivityComponent |
Activity#onCreate() |
Activity#onDestroy() |
FragmentComponent |
Fragment#onAttach() |
Fragment#onDestroy() |
ViewComponent |
View#super() |
View destroyed |
ViewWithFragmentComponent |
View#super() |
View destroyed |
ServiceComponent |
Service#onCreate() |
Service#onDestroy() |
היקפי הרכיבים
כברירת מחדל, כל הקישורים ב-Hilt הם unscoped. כלומר, בכל פעם שהאפליקציה מבקשת את הקישור, 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 זמין במאמר Scoping in Android and Hilt.
היררכיית הרכיבים
התקנת מודול ברכיב מאפשרת גישה לקישורים שלו כתלות בקישורים אחרים באותו רכיב או בכל רכיב צאצא מתחתיו בהיררכיית הרכיבים:
קישורי ברירת מחדל של רכיבים
כל רכיב Hilt מגיע עם קבוצה של קישורי ברירת מחדל ש-Hilt יכול להחדיר כתלות לקישורים המותאמים אישית שלכם. שימו לב שהקישורים האלה מתאימים לפעילות הכללית ולסוגי הפעילויות, ולא לסוג משנה ספציפי. הסיבה לכך היא ש-Hilt משתמש בהגדרת רכיב פעילות יחידה כדי להחדיר את כל הפעילויות. לכל פעילות יש מופע שונה של הרכיב הזה.
רכיב Android | קישורי ברירת מחדל |
---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
Application |
ViewModelComponent |
SavedStateHandle |
ActivityComponent |
Application , Activity |
FragmentComponent |
Application , Activity Fragment |
ViewComponent |
Application , Activity View |
ViewWithFragmentComponent |
Application , Activity , Fragment , View |
ServiceComponent |
Application , Service |
אפשר גם להשתמש ב-@ApplicationContext
כדי לבצע קישור של הקשר האפליקציה.
לדוגמה:
Kotlin
class AnalyticsServiceImpl @Inject constructor( @ApplicationContext context: Context ) : AnalyticsService { ... } // The Application binding is available without qualifiers. class AnalyticsServiceImpl @Inject constructor( application: Application ) : AnalyticsService { ... }
Java
public class AnalyticsServiceImpl implements AnalyticsService { private final Context context; @Inject AnalyticsAdapter(@ApplicationContext Context context) { this.context = context; } } // The Application binding is available without qualifiers. public class AnalyticsServiceImpl implements AnalyticsService { private final Application application; @Inject AnalyticsAdapter(Application application) { this.application = application; } }
אפשר גם להשתמש ב-@ActivityContext
כדי לקשר את הקשר של הפעילות. לדוגמה:
Kotlin
class AnalyticsAdapter @Inject constructor( @ActivityContext context: Context ) { ... } // The Activity binding is available without qualifiers. class AnalyticsAdapter @Inject constructor( activity: FragmentActivity ) { ... }
Java
public class AnalyticsAdapter { private final Context context; @Inject AnalyticsAdapter(@ActivityContext Context context) { this.context = context; } } // The Activity binding is available without qualifiers. public class AnalyticsAdapter { private final FragmentActivity activity; @Inject AnalyticsAdapter(FragmentActivity activity) { this.activity = activity; } }
הזרקת תלויות בכיתות שלא נתמכות על ידי Hilt
Hilt כולל תמיכה במחלקות Android הנפוצות ביותר. עם זאת, יכול להיות שתצטרכו להשתמש בהזרקת שדות בכיתות ש-Hilt לא תומך בהן.
במקרים כאלה, אפשר ליצור נקודת כניסה באמצעות ההערה @EntryPoint
. נקודת כניסה היא הגבול בין קוד שמנוהל על ידי Hilt לבין קוד שלא מנוהל על ידו. זו הנקודה שבה הקוד נכנס לראשונה לגרף האובייקטים שמנוהל על ידי 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
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 and Dagger
Hilt מבוססת על ספריית הזרקת התלות Dagger, ומספקת דרך סטנדרטית לשלב את Dagger באפליקציית Android.
היעדים של Hilt בהשוואה ל-Dagger הם:
- כדי לפשט את התשתית שקשורה ל-Dagger באפליקציות ל-Android.
- כדי ליצור קבוצה סטנדרטית של רכיבים והיקפים כדי להקל על ההגדרה, על הקריאה ועל שיתוף הקוד בין האפליקציות.
- כדי לספק דרך קלה להקצאת קישורים שונים לסוגים שונים של גרסאות build, כמו גרסת בדיקה, גרסת ניפוי באגים או גרסת הפצה.
מערכת ההפעלה Android יוצרת מופעים של הרבה מחלקות של מסגרות משלה, ולכן כדי להשתמש ב-Dagger באפליקציית Android צריך לכתוב כמות גדולה של קוד boilerplate. 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