إدخال المهام التابعة باستخدام Hilt

Hilt هي مكتبة حقن التبعية لنظام التشغيل Android تقلل من النصوص النموذجية من تنفيذ عمليات إضافة التبعية اليدوية في مشروعك. يتطلب منك إدخال التبعية اليدوي إنشاء كل فئة وتبعياتها يدويًا واستخدام الحاويات لإعادة استخدام التبعيات وإدارتها.

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

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

إضافة التبعيات

أولاً، أضِف المكوّن الإضافي hilt-android-gradle-plugin إلى ملف الجذر 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 {
  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

يجب أن تحتوي جميع التطبيقات التي تستخدم 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 التي يتم إدخال فئة في 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 هي فئة تتضمّن تعليقات توضيحية باستخدام السمة @Module. وعلى غرار وحدة الخنجر، فهي تخبر Hilt بكيفية توفير مثيلات لأنواع معينة. على عكس وحدات Dagger، يجب إضافة تعليقات توضيحية إلى وحدات Hilt باستخدام @InstallIn لإعلام Hilt بفئة Android التي سيتم استخدام كل وحدة أو تثبيتها فيها.

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

إدخال مثيلات الواجهة باستخدام @bounds

اطّلِع على مثال 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
  );
}

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

إدخال مثيلات باستخدام @توفيرs

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

ضع في الاعتبار المثال السابق. إذا لم تكن تملك الفئة AnalyticsService بشكل مباشر، يمكنك إعلام Hilt بكيفية توفير مثيلات من هذا النوع من خلال إنشاء دالة داخل وحدة Hilt وإضافة تعليقات توضيحية إلى تلك الدالة باستخدام @Provides.

توفر الدالة التي تتضمن تعليقات توضيحية المعلومات التالية إلى Hilt:

  • يخبر نوع إرجاع الدالة Hilt بالنوع الذي توفر الدالة له مثيلات.
  • تخبر معلمات الدالة Hilt تبعيات النوع المتجاوب.
  • يخبر نص الدالة Hilt بكيفية توفير مثيل من النوع المقابل. ينفذ Hilt الدالة body في كل مرة تحتاج فيها إلى توفير مثال من هذا النوع.

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 المكونات التالية:

مكوِّن النتائج حاقن لـ
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، يتم توفير المثيل نفسه في كل مرة.

يوضح المثال التالي كيفية نطاق ربط لمكوِّن في وحدة النتائج. يجب أن يتطابق نطاق الربط مع نطاق المكوِّن الذي تم تثبيته فيه، لذا يجب في هذا المثال تثبيت 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. يوجد عنصر النشاط ضمن
    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 and 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، يمكنك الاطّلاع على دليل نقل البيانات ونقل تطبيق Dagger إلى الدرس التطبيقي حول الترميز من Hilt.

مراجع إضافية

للتعرُّف على المزيد من المعلومات عن Hilt، يُرجى الاطّلاع على المراجع الإضافية التالية.

العيّنات

الدروس التطبيقية حول الترميز

المدوّنات