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

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

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

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

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

קודם כול, מוסיפים את הפלאגין hilt-android-gradle-plugin לרמה הבסיסית (root) של הפרויקט קובץ build.gradle:

מגניב

plugins {
  ...
  id 'com.google.dagger.hilt.android' version '2.44' apply false
}

Kotlin

plugins {
  ...
  id("com.google.dagger.hilt.android") version "2.44" apply false
}

אחר כך משתמשים בפלאגין של Gradle ומוסיפים את יחסי התלות האלה קובץ app/build.gradle:

מגניב

...
plugins {
  id 'kotlin-kapt'
  id 'com.google.dagger.hilt.android'
}

android {
  ...
}

dependencies {
  implementation "com.google.dagger:hilt-android:2.44"
  kapt "com.google.dagger:hilt-compiler:2.44"
}

// 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.44")
  kapt("com.google.dagger:hilt-android-compiler:2.44")
}

// 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 חייבות להכיל כיתה 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 אם מופשט.

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

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

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

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

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. לסמן לייק לפגיעים מודול, מיידעים את Hilt איך לספק מופעים מסוגים מסוימים. בניגוד למודולים של Dagger, צריך להוסיף הערות למודולים של Hilt באמצעות @InstallIn כדי לומר ל-Hilt לאיזה Android הכיתה שבה ייעשה שימוש או יותקן בכל מודול.

יחסי התלות שמספקים במודולים של Hilt זמינים בכל שמשויכים למחלקה של Android שבה מתקינים את מודול Hilt.

החדרת מופעי ממשק עם @Binds

עיינו בדוגמה AnalyticsService. אם AnalyticsService הוא ממשק, אי אפשר להחדיר אותו באמצעות constructor. במקום זאת, צריך לספק את Hilt עם הקישור מידע על ידי יצירת פונקציה מופשטת שמסומנת בהערה @Binds בתוך מודול Hilt.

ההערה @Binds מנחה את 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) כי ברצונך ש-Hhilt יכניס את הקובץ הזה של תלות ב-ExampleActivity. המשמעות של ההערה הזאת היא שכל יחסי תלות ב-AnalyticsModule זמינים בכל הפעילויות של האפליקציה.

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

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

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

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

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

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

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 עילית.

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

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

ViewWithFragmentComponent נמצא בקטע FragmentComponent. רכיב Fragment
    ו-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. לדוגמה:

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. אבל יכול להיות צריכים לבצע החדרת שדה במחלקות שלא נתמכות בהיטים.

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

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

הילט ופגיונות

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

דוגמיות

שיעורי Lab

בלוגים