הזרקת תלות באמצעות Hilt

Hilt היא ספריית הזרקת תלות ל-Android שמפחיתה את שיעור החימום של החדרת תלות ידנית בפרויקט. ביצוע תלות ידנית החדרת נתונים דורשת מכם ליצור בכל רמה ויחסי התלות שלה באופן ידני, ולהשתמש בקונטיינרים כדי לעשות שימוש חוזר לנהל יחסי תלות.

Hilt מספק דרך סטנדרטית לשימוש ב-DI באפליקציה שלכם, קונטיינרים לכל מחלקה של Android בפרויקט וניהול מחזורי החיים שלהם באופן אוטומטי. Hilt מבוסס על ספריית DI הפופולרית Dagger כדי להפיק תועלת נכונות זמן הידור (compile), ביצועים בזמן ריצה, מדרגיות (scalability) ו-Android Studio תמיכה ש-Dagger. מידע נוסף זמין במאמר Hilt and Dagger.

במדריך הזה מוסבר על המושגים הבסיסיים של Hilt ועל הקונטיינרים שנוצרים על ידו. בנוסף, תוכלו לראות הדגמה של איך להפעיל אפליקציה קיימת כדי להשתמש ב-Hilt.

הוספת יחסי תלות

קודם כול, מוסיפים את הפלאגין hilt-android-gradle-plugin לרמה הבסיסית (root) של הפרויקט קובץ 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:

GroovyKotlin
...
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:

GroovyKotlin
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, כולל מחלקת בסיס לאפליקציה שמשמש כקונטיינר של יחסי התלות ברמת האפליקציה.

KotlinJava
@HiltAndroidApp
class ExampleApplication : Application() { ... }
@HiltAndroidApp
public class ExampleApplication extends Application { ... }

רכיב Hilt שנוצר מצורף לאובייקט Application של מחזור החיים שלו ומספקת יחסי תלות בו. בנוסף, הוא הרכיב ההורה של האפליקציה, כלומר רכיבים אחרים יכולים לגשת ליחסי התלות שהוא מספק.

הזרקת יחסי תלות לכיתות Android

אחרי שמגדירים את Hilt בכיתה Application וברמת האפליקציה שהרכיב זמין, Hilt יכול לספק יחסי תלות למחלקות אחרות של Android כוללים את ההערה @AndroidEntryPoint:

KotlinJava
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }
@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity { ... }

נכון לעכשיו, Hilt תומך בכיתות Android הבאות:

  • Application (באמצעות @HiltAndroidApp)
  • ViewModel (באמצעות @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

אם מוסיפים הערות לכיתה ב-Android באמצעות @AndroidEntryPoint, צריך גם להוסיף הערות למחלקות של Android שתלויות באפשרות הזו. לדוגמה, אם מוסיפים הערות עליך גם להוסיף הערות לפעילויות שבהן משתמשים מקטע.

@AndroidEntryPoint יוצר רכיב Hilt נפרד לכל Android בכיתה בפרויקט שלכם. הרכיבים האלה יכולים לקבל יחסי תלות את סיווגי ההורה המתאימים, כפי שמתואר ברכיב ההיררכיה.

כדי לקבל יחסי תלות מרכיב, משתמשים בהערה @Inject כדי לבצע הזרקה של שדה:

KotlinJava
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}
@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

לכיתות ש-Hilt מזריצה יכולות להיות כיתות בסיס אחרות שמשתמשות גם בהזרקה. אם הכיתות האלה הן ערכיות, אין צורך בהערה @AndroidEntryPoint.

למידע נוסף על קריאה חוזרת (callback) של מחזור חיים שדרכה מוזרק מחלקה של Android, תוכלו לקרוא את המאמר משכי החיים של רכיבים.

הגדרת קישורים של Hilt

כדי לבצע החדרת שדה, Hilt צריך לדעת איך לספק מופעים של של יחסי התלות ההכרחיים מהרכיב המתאים. הקישור מכיל המידע הדרוש כדי לספק מופעים מסוג מסוים כתלות.

אחת הדרכים לספק מידע על קישור ל-Hilt היא הזרקה ל-constructor. כדי להורות ל-Hilt איך לספק מכונות של הכיתה הזו, משתמשים בהערה @Inject ב-constructor של הכיתה:

KotlinJava
class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }
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 לדעת איזו הטמעה לספק.
KotlinJava
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. ההערה הזו מציינת שכל יחסי התלות ב-AnalyticsModule זמינים בכל הפעילויות של האפליקציה.

החדרת מכונות עם @Provides

ממשקים הם לא המקרה היחיד שבו אי אפשר להחדיר סוג באמצעות ה-constructor. בנוסף, לא ניתן לבצע החדרת בנייה אם אתם לא הבעלים של הכיתה, כי היא מגיע מספרייה חיצונית (כיתות כמו Retrofit, OkHttpClient, או מסדי נתונים של חדר), או אם המופעים חייבים ניתן ליצור באמצעות הבונה דוגמת עיצוב.

נבחן את הדוגמה הקודמת. אם אתם לא הבעלים הישירים של הכיתה AnalyticsService, תוכלו להורות ל-Hilt איך לספק מכונות מהסוג הזה על ידי יצירת פונקציה בתוך מודול Hilt והוספת הערה לפונקציה הזו עם @Provides.

הפונקציה עם ההערה מספקת ל-Hilt את המידע הבא:

  • סוג ההחזרה של הפונקציה מאפשר ל-Hilt לדעת איזה סוג יש למופעים שהפונקציה מספקת.
  • הפרמטרים של הפונקציה מאפשרים ל-Hilt לדעת מהם יחסי התלות של הסוג המתאים.
  • גוף הפונקציה מורה ל-Hilt איך לספק מופע של הסוג המתאים. Hilt מאמת את גוף הפונקציה בכל פעם שהוא צריך לספק במופע מהסוג הזה.
KotlinJava
@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.

ראשית, עליך להגדיר את המאפיינים המאשרים שישמשו אותך כדי להוסיף הערות ל@Binds או @Provides אמצעי תשלום:

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

KotlinJava
@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();
  }
}

כדי להחדיר את הסוג הספציפי הנדרש, אפשר להוסיף הערה לשדה או לפרמטר באמצעות המאפיין המתאים:

KotlinJava
// 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;
  ...
}

מומלץ להוסיף מאפיין מגדיר לסוג מסוים, דרכים אפשריות לספק את התלות הזאת. אם משאירים את ההטמעה הבסיסית או המשותפת ללא מגדיר, יש סיכוי גבוה לשגיאות, ויכול להיות ש-Hilt יזין את התלות הלא נכונה.

ערכי אישור מוגדרים מראש ב-Hilt

ב-Hilt יש כמה מגדירים מוגדרים מראש. לדוגמה, מכיוון שאולי צריך הכיתה Context מהאפליקציה או מהפעילות, Hilt מספק שלב המוקדמות של @ApplicationContext ו-@ActivityContext.

נניח שלכיתה AnalyticsAdapter מהדוגמה נדרש ההקשר של הפעילות. הקוד הבא מדגים איך מספקים את הפעילות הקשר לAnalyticsAdapter:

KotlinJava
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 סוג אחר או החדרת שדה (כמו ExampleActivity), Hilt מספק מופע חדש של AnalyticsAdapter.

עם זאת, 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 לכל אורך החיים של הפעילות המתאימה:

KotlinJava
@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. היקף הקישור חייב להתאים להיקף הרכיב שבו הוא ולכן בדוגמה הזו צריך להתקין את AnalyticsService ב- SingletonComponent במקום ActivityComponent:

KotlinJava
// 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.

היררכיית הרכיבים

התקנת מודול ברכיב מאפשרת לגשת לקישורים שלו יחסי תלות בקישורים אחרים ברכיב הזה או בכל רכיב צאצא כלשהו בהיררכיית הרכיבים:

הרכיב ViewWithFragmentComponent נמצא בקטע FragmentComponent. הרכיבים FragmentComponent ו-ViewComponent נמצאים בתוך ActivityComponent. רכיב הפעילות מתחת
    ActivityRetainedElement. הערך של ViewModelComponent נמצא ב-
    ActivityRetainedElement. activityRetainedComponent ו-ServiceComponent
    ברכיב SingletonComponent.
איור 1. היררכיית הרכיבים ש-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. לדוגמה:

KotlinJava
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 כדי לקשר את הפעילות להקשר. עבור דוגמה:

KotlinJava
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;
  }
}

הזרקת יחסי תלות בכיתות שלא נתמכות על ידי Hilt

Hilt כולל תמיכה בסוגים הנפוצים ביותר של Android. אבל יכול להיות תצטרכו לבצע החדרת שדה במחלקות שלא נתמכות בהיטים.

במקרים כאלה, אפשר ליצור נקודת כניסה באמצעות @EntryPoint הערה. נקודת הכניסה היא הגבול בין קוד שמנוהל על ידי Hilt לבין קוד שלא מנוהל על ידו. זו הנקודה שבה הקוד נכנס לראשונה לתרשים האובייקטים שמנוהל על ידי Hilt. נקודות הכניסה מאפשרות ל-Hhilt להשתמש בקוד ש-Hhilt עושה לא מאפשרות לספק יחסי תלות בתוך תרשים התלות.

לדוגמה, Hilt לא תומך ישירות בספקי תוכן. אם אתם רוצים לראות תוכן להשתמש ב-Hhilt כדי לקבל יחסי תלות, צריך להגדיר ממשק שנוסף לו הערות עם @EntryPoint לכל סוג קישור שרוצים כוללים מגדירים. לאחר מכן מוסיפים את @InstallIn כדי לציין את הרכיב שבו רוצים להתקין את נקודת הכניסה, באופן הבא:

KotlinJava
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 תואמים לכיתה של Android בהערה @InstallIn בממשק @EntryPoint:

KotlinJava
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 and Dagger

‏Hilt מבוסס על ספריית ההזרקה של יחסי התלות Dagger, ומספק דרך רגילה לשילוב Dagger באפליקציית Android.

בהשוואה ל-Dagger, המטרות של Hilt הן:

  • כדי לפשט את התשתית שקשורה ל-Dagger לאפליקציות ל-Android.
  • כדי ליצור קבוצה סטנדרטית של רכיבים והיקפים, שבעזרתם קל יותר להגדיר את הרכיבים, לקרוא אותם ולשתף את הקוד בין אפליקציות.
  • כדי לספק דרך קלה להקצות קישורים שונים לסוגים שונים של גרסאות build, כמו בדיקה, ניפוי באגים או גרסה זמינה.

כי מערכת ההפעלה Android יוצרת הרבה מה-framework שלה לשימוש ב-Dagger באפליקציה ל-Android, נדרש לכתוב כמות גדולה של סטנדרטי. Hilt מקטין את הקוד הסטנדרטי שמעורב באמצעות Dagger באפליקציה ל-Android. Hilt יוצר באופן אוטומטי את הפריטים הבאים ומספק אותם:

  • רכיבים לשילוב שיעורי framework של Android עם Dagger שאחרת היה צריך ליצור ידנית.
  • הערות היקף לשימוש עם הרכיבים ש-Hilt יוצר באופן אוטומטי.
  • קישורים מוגדרים מראש לייצוג סיווגים של Android, כמו Application או Activity
  • מסננים מוגדרים מראש שמייצגים את @ApplicationContext ואת @ActivityContext.

קוד Dagger ו-Hilt יכולים להתקיים בו-זמנית באותו קוד בסיס. עם זאת, ברוב המקרים עדיף להשתמש ב-Hilt כדי לנהל את כל השימוש ב-Dagger ב-Android. להעברה לפרויקט שמשתמש ב-Dagger ל-Hilt, ראו העברה הדרך ומעבר את אפליקציית Dagger to Hilt Codelab.

מקורות מידע נוספים

מידע נוסף על Hilt זמין במקורות המידע הנוספים הבאים.

דוגמיות

כך מטמיעים הגדרות מנוהלות, שאפליקציות אחרות יכולות לשנות באותו מכשיר.

התכונות הארגוניות של Android מספקות לארגונים פלטפורמת ניידות אחידה של Android — שילוב מכשירים, אפליקציות, וניהול. אפליקציות ל-Android תואמות לתכונות של Android לארגונים כברירת מחדל. אבל יש תכונות נוספות שאפשר להשתמש בהן כדי כדי שהאפליקציה תפעל בצורה

יישום שיטות מומלצות יעזור לכם לוודא שהאפליקציות שלכם פועלות בצורה חלקה בסביבה הארגונית.

Codelabs

בלוגים