توفير التبعية باستخدام Hilt (طرق العرض)

المفاهيم وتنفيذ Jetpack Compose

‫Hilt هي مكتبة لحقن التبعية في Android تقلّل من الرموز المتكررة الناتجة عن حقن التبعية يدويًا في مشروعك. يتطلب إجراء حقن التبعية اليدوي إنشاء كل فئة وتبعياتها يدويًا، واستخدام الحاويات لإعادة استخدام التبعيات وإدارتها.

توفّر Hilt طريقة عادية لاستخدام حقن التبعية في تطبيقك من خلال توفير حاويات لكل فئة Android في مشروعك وإدارة مراحل نشاطها تلقائيًا. تم إنشاء Hilt استنادًا إلى مكتبة حقن التبعية الشهيرة Dagger للاستفادة من صحة وقت التجميع وأداء وقت التشغيل وقابلية التوسع ودعم استوديو Android الذي توفره Dagger. لمزيد من المعلومات، يُرجى الاطّلاع على Hilt وDagger.

يشرح هذا الدليل المفاهيم الأساسية لـ Hilt والحاويات التي ينشئها. ويتضمّن أيضًا عرضًا توضيحيًا لكيفية تشغيل تطبيق حالي لاستخدام Hilt.

فئة تطبيق 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 إذا كانت مجرّدة.

لمزيد من المعلومات حول الاستدعاء في إحدى مراحل النشاط الذي يتم حقن فئة 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

في بعض الأحيان، لا يمكن حقن نوع عبر المنشئ. يمكن أن يحدث هذا لعدة أسباب. على سبيل المثال، لا يمكنك حقن واجهة عبر المنشئ. لا يمكنك أيضًا الحقن عبر المنشئ لنوع لا تملكه، مثل فئة من مكتبة خارجية. في هذه الحالات، يمكنك تزويد Hilt بمعلومات الربط باستخدام وحدات Hilt.

وحدة Hilt هي فئة تمّت إضافة التعليق التوضيحي @Module إليها. مثل وحدة Dagger، تُعلِم هذه الوحدة Hilt بكيفية توفير مثيلات لأنواع معيّنة. على عكس وحدات Dagger، يجب إضافة تعليقات توضيحية إلى وحدات Hilt باستخدام @InstallIn لإعلام Hilt بفئة Android التي سيتم استخدام كل وحدة أو تثبيتها فيها.

تتوفّر التبعيات التي تقدّمها في وحدات Hilt في جميع المكوّنات التي تم إنشاؤها والمرتبطة بفئة Android التي تثبّت فيها وحدة Hilt.

حقن مثيلات الواجهة باستخدام ‎ @Binds

لنأخذ مثالاً على ذلك AnalyticsService. إذا كان AnalyticsService واجهة، لا يمكنك حقنه عبر المنشئ. بدلاً من ذلك، يمكنك تزويد 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
  );
}

تمت إضافة التعليق التوضيحي @InstallIn(ActivityComponent.class) إلى وحدة Hilt‏ AnalyticsModule لأنّك تريد أن يحقن Hilt هذه التبعية في ExampleActivity. يعني هذا التعليق التوضيحي أنّ جميع التبعيات في AnalyticsModule متاحة في جميع أنشطة التطبيق.

حقن النُسخ باستخدام ‎ @Provides

الواجهات ليست الحالة الوحيدة التي لا يمكنك فيها حقن نوع عبر المنشئ. لا يمكن أيضًا استخدام الحقن عبر المنشئ إذا لم تكن تملك الفئة لأنّها تأتي من مكتبة خارجية (فئات مثل Retrofit أو OkHttpClient أو قواعد بيانات Room)، أو إذا كان يجب إنشاء المثيلات باستخدام نمط المنشئ.

لنفترض المثال السابق. إذا لم تكن تملك فئة 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()

نطاقات المكوّنات

By default, all bindings in Hilt are 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، يمكنك الاطّلاع على تحديد النطاق في Android وHilt.

التدرّج الهرمي للمكوّنات

يتيح تثبيت وحدة في مكوّن الوصول إلى عمليات الربط الخاصة بها كعنصر تابع لعمليات ربط أخرى في هذا المكوّن أو في أي مكوّن فرعي أسفله في التسلسل الهرمي للمكوّنات:

يقع ViewWithFragmentComponent ضمن FragmentComponent. ‫FragmentComponent وViewComponent هما مكوّنان فرعيان لـ ActivityComponent. ‫ActivityComponent هو مكوّن فرعي من
    ActivityRetainedComponent. يقع ViewModelComponent ضمن
    ActivityRetainedComponent. ‫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 الأكثر شيوعًا. ومع ذلك، قد تحتاج إلى إجراء الحقن المباشر في المتغيرات في الفئات التي لا تتوافق مع 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 يتطابقان مع فئة 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 وDagger

تم إنشاء Hilt استنادًا إلى مكتبة حقن التبعية Dagger، ما يوفّر طريقة عادية لدمج Dagger في تطبيق Android.

في ما يتعلق بـ Dagger، تتمثّل أهداف Hilt في ما يلي:

  • لتبسيط البنية الأساسية المتعلّقة بـ Dagger لتطبيقات Android.
  • لإنشاء مجموعة معايير التعلُّم من المكوّنات والنطاقات لتسهيل عملية الإعداد والقراءة ومشاركة الرموز البرمجية بين التطبيقات.
  • لتوفير طريقة سهلة لتوفير عمليات ربط مختلفة لأنواع الإصدارات المختلفة، مثل الاختبار أو تصحيح الأخطاء أو الإصدار.

بما أنّ نظام التشغيل Android ينشئ العديد من فئات إطار العمل الخاصة به، فإنّ استخدام Dagger في تطبيق Android يتطلب منك كتابة قدر كبير من الرموز النموذجية. يقلّل Hilt من الرموز البرمجية النموذجية التي يتم استخدامها في Dagger في تطبيق Android. تنشئ Hilt تلقائيًا ما يلي وتوفّره:

  • مكوّنات لدمج فئات إطار عمل Android مع Dagger التي قد تحتاج إلى إنشائها يدويًا.
  • التعليقات التوضيحية للنطاق لاستخدامها مع المكوّنات التي تنشئها Hilt تلقائيًا
  • عمليات الربط المحدّدة مسبقًا لتمثيل فئات Android، مثل Application أو Activity
  • محدِّدات مُعرَّفة مسبقًا لتمثيل @ApplicationContext و@ActivityContext

يمكن أن يتوفّر رمز Dagger ورمز Hilt في قاعدة الرموز البرمجية نفسها. ومع ذلك، في معظم الحالات، من الأفضل استخدام Hilt لإدارة جميع استخداماتك لـ Dagger على Android. لنقل مشروع يستخدم Dagger إلى Hilt، يُرجى الاطّلاع على دليل نقل البيانات.