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
:
מגניב
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. כדי להורות ל-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. עם זאת, יכול להיות שתצטרכו לבצע החדרת שדה למחלקות שלא נתמכות בהילט.
במקרים כאלה, אפשר ליצור נקודת כניסה באמצעות ההערה @EntryPoint
. נקודת הכניסה היא הגבול בין קוד שמנוהל על ידי Hilt לבין קוד שלא מנוהל על ידו. זו הנקודה שבה הקוד נכנס לראשונה לתרשים האובייקטים שמנוהל על ידי Hilt. נקודות הכניסה מאפשרות ל-Hhilt להשתמש בקוד שה-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