Hilt هي مكتبة لتوفير التبعيات في Android، وهي تقلّل من الرمز النموذجي اللازم لتوفير التبعيات يدويًا في مشروعك. يتطلّب تنفيذ إدخال التبعيات يدويًا إنشاء كل فئة وتبعياتها يدويًا، واستخدام الحاويات لإعادة استخدام التبعيات وإدارتها.
توفّر Hilt طريقة عادية لاستخدام ميزة "توفير التبعية" في تطبيقك من خلال توفير حاويات لكل فئة Android في مشروعك وإدارة دورات حياتها تلقائيًا. تم إنشاء Hilt استنادًا إلى مكتبة DI الشائعة Dagger للاستفادة من الصحة في وقت الترجمة والأداء في وقت التشغيل وقابلية التوسّع وتوافقها مع Android Studio التي توفّرها Dagger. لمزيد من المعلومات، يُرجى الاطّلاع على Hilt وDagger.
يوضّح هذا الدليل المفاهيم الأساسية لـ Hilt والحاويات التي يتم إنشاؤها. ويتضمّن أيضًا عرضًا توضيحيًا لكيفية إعداد تطبيق حالي لاستخدام Hilt.
إضافة التبعيات
أولاً، أضِف المكوّن الإضافي hilt-android-gradle-plugin
إلى ملف build.gradle
في جذر مشروعك:
Groovy
plugins { ... id 'com.google.dagger.hilt.android' version '2.56.2' apply false }
Kotlin
plugins { ... id("com.google.dagger.hilt.android") version "2.56.2" apply false }
بعد ذلك، طبِّق إضافة Gradle وأضِف التبعيات التالية في ملف
app/build.gradle
:
Groovy
... plugins { id 'com.google.devtools.ksp' id 'com.google.dagger.hilt.android' } android { ... } dependencies { implementation "com.google.dagger:hilt-android:2.56.2" ksp "com.google.dagger:hilt-compiler:2.56.2" }
Kotlin
plugins { id("com.google.devtools.ksp") id("com.google.dagger.hilt.android") } android { ... } dependencies { implementation("com.google.dagger:hilt-android:2.56.2") ksp("com.google.dagger:hilt-android-compiler:2.56.2") }
يستخدم Hilt ميزات Java 8. لتفعيل Java 8 في مشروعك، أضِف ما يلي إلى ملف app/build.gradle
:
Groovy
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 فيها، راجِع مدد بقاء المكوّنات.
تحديد عمليات ربط 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، يجب إضافة التعليق التوضيحي @InstallIn
إلى وحدات Hilt لإخبار 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 ); }
يتم وضع التعليق التوضيحي AnalyticsModule
على وحدة Hilt لأنّك تريد أن يدرج Hilt هذه التبعية في ExampleActivity
.@InstallIn(ActivityComponent.class)
تعني هذه التعليقات التوضيحية أنّ جميع التبعيات في 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 destroyed |
ActivityRetainedComponent |
Activity#onCreate() |
Activity#onDestroy() |
ViewModelComponent |
تم إنشاء ViewModel . |
ViewModel destroyed |
ActivityComponent |
Activity#onCreate() |
Activity#onDestroy() |
FragmentComponent |
Fragment#onAttach() |
Fragment#onDestroy() |
ViewComponent |
View#super() |
View destroyed |
ViewWithFragmentComponent |
View#super() |
View destroyed |
ServiceComponent |
Service#onCreate() |
Service#onDestroy() |
نطاقات المكوّنات
تكون جميع عمليات الربط في Hilt غير محدودة النطاق تلقائيًا. وهذا يعني أنّه في كل مرة يطلب فيها تطبيقك الربط، ينشئ Hilt مثيلاً جديدًا من النوع المطلوب.
في المثال، في كل مرة يوفّر فيها Hilt AnalyticsAdapter
كعنصر تابع لنوع آخر أو من خلال عملية الربط بحقل (كما في ExampleActivity
)، يوفّر Hilt مثيلاً جديدًا من AnalyticsAdapter
.
ومع ذلك، يتيح Hilt أيضًا تحديد نطاق الربط بمكوّن معيّن. لا ينشئ Hilt عملية ربط محدودة النطاق إلا مرة واحدة لكل مثيل من المكوّن الذي يتم تحديد نطاق عملية الربط فيه، وتتشارك جميع طلبات عملية الربط المثيل نفسه.
يسرد الجدول أدناه تعليقات توضيحية للنطاق لكل مكوّن تم إنشاؤه:
فئة Android | المكوّن الذي تم إنشاؤه | النطاق |
---|---|---|
Application |
SingletonComponent |
@Singleton |
Activity |
ActivityRetainedComponent |
@ActivityRetainedScoped |
ViewModel |
ViewModelComponent |
@ViewModelScoped |
Activity |
ActivityComponent |
@ActivityScoped |
Fragment |
FragmentComponent |
@FragmentScoped |
View |
ViewComponent |
@ViewScoped |
View أضاف تعليقًا توضيحيًا باستخدام @WithFragmentBindings |
ViewWithFragmentComponent |
@ViewScoped |
Service |
ServiceComponent |
@ServiceScoped |
في المثال، إذا حدّدت نطاق AnalyticsAdapter
إلى ActivityComponent
باستخدام @ActivityScoped
، يوفّر Hilt مثيلاً واحدًا من AnalyticsAdapter
خلال مدة نشاط النشاط المقابل:
Kotlin
@ActivityScoped class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
Java
@ActivityScoped public class AnalyticsAdapter { private final AnalyticsService service; @Inject AnalyticsAdapter(AnalyticsService service) { this.service = service; } ... }
لنفترض أنّ AnalyticsService
يتضمّن حالة داخلية تتطلّب استخدام مثيل واحد في كل مرة، ليس فقط في ExampleActivity
، بل في أي مكان في التطبيق. في هذه الحالة، من المناسب تحديد نطاق AnalyticsService
إلى SingletonComponent
. والنتيجة هي أنّه كلما احتاج المكوّن إلى توفير مثيل من AnalyticsService
، سيوفّر المثيل نفسه في كل مرة.
يوضّح المثال التالي كيفية تحديد نطاق ربط بمكوّن في وحدة Hilt. يجب أن يتطابق نطاق الربط مع نطاق المكوّن الذي تم تثبيته فيه، لذا في هذا المثال، يجب تثبيت AnalyticsService
في SingletonComponent
بدلاً من ActivityComponent
:
Kotlin
// If AnalyticsService is an interface. @Module @InstallIn(SingletonComponent::class) abstract class AnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService } // If you don't own AnalyticsService. @Module @InstallIn(SingletonComponent::class) object AnalyticsModule { @Singleton @Provides fun provideAnalyticsService(): AnalyticsService { return Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService::class.java) } }
Java
// If AnalyticsService is an interface. @Module @InstallIn(SingletonComponent.class) public abstract class AnalyticsModule { @Singleton @Binds public abstract AnalyticsService bindAnalyticsService( AnalyticsServiceImpl analyticsServiceImpl ); } // If you don't own AnalyticsService. @Module @InstallIn(SingletonComponent.class) public class AnalyticsModule { @Singleton @Provides public static AnalyticsService provideAnalyticsService() { return new Retrofit.Builder() .baseUrl("https://example.com") .build() .create(AnalyticsService.class); } }
لمزيد من المعلومات عن نطاقات مكوّنات Hilt، يمكنك الاطّلاع على النطاق في Android وHilt.
التدرّج الهرمي للمكوّنات
يسمح تثبيت وحدة في أحد المكوّنات بالوصول إلى عمليات الربط الخاصة بها كعنصر تابع لعمليات ربط أخرى في هذا المكوّن أو في أي مكوّن ثانوي أسفله في التسلسل الهرمي للمكوّنات:
عمليات الربط التلقائية للمكوّنات
يأتي كل مكوّن من Hilt مزوّدًا بمجموعة من عمليات الربط التلقائية التي يمكن أن يدرجها Hilt كعناصر تابعة في عمليات الربط المخصّصة. يُرجى العِلم أنّ عمليات الربط هذه تتوافق مع أنواع الأنشطة والتقسيمات العامة، وليس مع أي فئة فرعية محدّدة. ويرجع ذلك إلى أنّ Hilt تستخدم تعريفًا واحدًا لمكوّن النشاط من أجل إدخال جميع الأنشطة. يحتوي كل نشاط على نسخة مختلفة من هذا المكوّن.
مكوِّن Android | عمليات الربط التلقائية |
---|---|
SingletonComponent |
Application |
ActivityRetainedComponent |
Application |
ViewModelComponent |
SavedStateHandle |
ActivityComponent |
"Application " و"Activity " |
FragmentComponent |
"Application " و"Activity " و"Fragment " |
ViewComponent |
"Application " و"Activity " و"View " |
ViewWithFragmentComponent |
Application ، Activity ، Fragment ، View |
ServiceComponent |
"Application " و"Service " |
يتوفّر ربط سياق التطبيق أيضًا باستخدام @ApplicationContext
.
مثلاً:
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، اطّلِع على المراجع الإضافية التالية.
نماذج
الدروس التطبيقية حول الترميز
المدوّنات
- توفير التبعية في Android باستخدام Hilt
- النطاق في Android وHilt
- إضافة مكوّنات إلى التسلسل الهرمي في Hilt
- نقل تطبيق Google I/O إلى Hilt