Dagger هو إطار عمل لحقن التبعيات للغتي Java وKotlin، يقوم بتوليد كود DI في مرحلة الترجمة من خلال معالجة التعليقات التوضيحية. Hilt هو طبقة فوق Dagger لـ Android تعمل على تبسيط إعداد المكونات ودورة الحياة. وفقًا لـ Google، 2025، يتم استخدام Hilt في أكثر من 70% من تطبيقات Android من قائمة Google Play Top-100، ويدعم Activity وFragment وViewModel وService من خلال مكونات محددة مسبقًا. يوفر كلا الإطارين التحقق من رسم بياني للتبعيات في وقت الترجمة، مما يستبعد أخطاء الحقن في وقت التشغيل.
الخلاصة
Dagger هو إطار عمل لحقن التبعيات مع توليد الكود في وقت الترجمة. تم تطويره في Square ثم نُقل لاحقًا إلى Google، يستخدم Dagger معالج التعليقات التوضيحية Java APT لتحليل الرسم البياني للتبعيات وتوليد فئات المصنع. على عكس DI في وقت التشغيل (Guice، Koin)، لا يستخدم Dagger الانعكاس — يتم إنشاء كل الكود في وقت الترجمة، مما يضمن أقصى أداء في وقت التشغيل واكتشاف الأخطاء في وقت البناء.
Hilt هي مكتبة من Google مبنية فوق Dagger ومحسّنة لـ Android. توفر Hilt مكونات محددة مسبقًا تتوافق مع دورة حياة مكونات Android: @SingletonComponent لـ Application، و@ActivityComponent لـ Activity، و@FragmentComponent لـ Fragment، و@ViewModelComponent لـ ViewModel. هذا يلغي الإعداد الروتيني لـ Component وModule المطلوب في Dagger النقي. تقوم Hilt أيضًا بتوليد الرسم البياني للتبعيات لكل مكون Android تلقائيًا عبر @AndroidEntryPoint.
وفقًا لـ Google I/O 2024، Hilt هو الحل الموصى به لـ DI في تطبيقات Android المكتوبة بلغة Kotlin. مكتبات Jetpack (Navigation، Room، WorkManager) لديها تكامل مدمج مع Hilt عبر @HiltViewModel و@HiltWorker. في المشاريع التي لا تستخدم Android (مكتبات Java/Kotlin النقية، تطبيقات الخادم)، يتم استخدام Dagger النقي بدون طبقة Hilt.
بدون إطار عمل DI، يقوم المطور بإنشاء الكائنات يدويًا عبر المنشئات أو المصانع، ويمرر التبعيات على طول السلسلة. كل متطلب جديد يعني تغيير توقيعات جميع المنشئات في السلسلة. Dagger يؤتمت هذه العملية: ما عليك سوى إعلان النوع المطلوب (@Inject constructor)، ويقوم Dagger بإنشاء الرسم البياني للتبعيات، وحل جميع الأنواع المتداخلة. عندما تتغير التبعيات، يقوم Dagger بتحديث الكود المولد تلقائيًا — من المستحيل ارتكاب خطأ في السلسلة.
حقن التبعيات هو نمط يتلقى فيه الكائن تبعياته من الخارج بدلاً من إنشائها بنفسه. يطبق DI مبدأ عكس التحكم (IoC): الفئة ليست مسؤولة عن إنشاء تبعياتها الخاصة، بل تعلن عنها عبر منشئ أو طريقة أو حقل. يعتبر الحقن عبر المنشئ الأكثر تفضيلاً لأنه يضمن إنشاء الكائن في حالة صالحة.
| نوع الحقن | صياغة Dagger | متى تستخدم |
|---|---|---|
| Constructor injection | @Inject constructor | الطريقة الأساسية — لجميع الفئات المخصصة |
| Field injection | @Inject lateinit var | فقط لمكونات Android (Activity، Fragment) |
| Method injection | @Inject fun bind() | للتهيئة بعد الإنشاء |
تشمل المزايا الرئيسية لـ DI قابلية الاختبار (يمكن استبدال التبعيات بكائنات mock)، والاقتران الضعيف (تعتمد الفئات على الواجهات، وليس التنفيذات)، والإدارة الصريحة لدورة حياة الكائنات من خلال النطاقات. يضمن Dagger تلقائيًا أن يتم إنشاء الكائن مرة واحدة ضمن نطاقه ويتم تدميره عند الخروج من النطاق.
Component هو العنصر المركزي في الرسم البياني للتبعيات في Dagger. إنها واجهة موضحة بـ @Component تصف الجسر بين Module وأهداف الحقن. يقوم Dagger بتوليد تنفيذ Component (مثل DaggerAppComponent) في وقت الترجمة. يحدد Component الأنواع المتاحة للحقن من خلال طرق مجردة تعيد الأنواع المطلوبة أو من خلال طرق inject التي تقبل كائنًا للحقل injection.
// Module: يوفر التبعيات التي لا يستطيع Dagger إنشاؤها بنفسه
@Module
class NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.build()
}
@Provides
@Singleton
fun provideApiService(client: OkHttpClient): ApiService {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
// Component: يربط Module وأهداف Injection
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module هو فئة تحتوي على طرق مع @Provides تعيد نسخًا من التبعيات. يُستخدم Module للأنواع التي لا يستطيع Dagger إنشاؤها تلقائيًا: المكتبات الخارجية (OkHttp، Retrofit)، الكائنات ذات معلمات المنشئ، الواجهات مع اختيار التنفيذ. @Binds هو بديل لـ @Provides للحالات التي تعيد فيها الطريقة واجهة وتقبل تنفيذًا واحدًا: يقوم Dagger بتوليد تحويل مباشر دون استدعاء الطريقة.
@Scope يحدد وقت حياة الكائن في الرسم البياني للتبعيات. @Singleton — يتم إنشاء الكائن مرة واحدة للتطبيق بأكمله. @ActivityScoped — يعيش الكائن طالما تعيش Activity. @FragmentScoped — طالما يعيش Fragment. بدون نطاق، يقوم Dagger بإنشاء نسخة جديدة في كل حقن. @Reusable — نطاق للكائنات التي لا يجب أن تكون مفردة ولكن إنشائها مكلف — قد يخزن Dagger النسخة مؤقتًا لكنه لا يضمن ذلك.
Hilt تبسط إعداد Dagger لـ Android من خلال مكونات محددة مسبقًا وتوليد تلقائي للرسم البياني الأساسي. التعليق التوضيحي @HiltAndroidApp على فئة Application يشغل توليد مكون Hilt. بدون هذا التعليق التوضيحي، لا يعمل Hilt — وهو إلزامي لأي تطبيق Android يستخدم Hilt. ينشئ @HiltAndroidApp المكون الأب SingletonComponent، الذي ترث منه جميع مكونات التطبيق الأخرى.
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// apiService تم حقنه بالفعل قبل استدعاء onCreate
}
}
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext ctx: Context): AppDatabase {
return Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db").build()
}
}
@AndroidEntryPoint هو تعليق توضيحي لـ Activity وFragment وService وBroadcastReceiver وView. يولد مكون Hilt لكل نوع: @AndroidEntryPoint على Activity ينشئ ActivityComponent الذي يرث من SingletonComponent. يتلقى المكون الفرعي تلقائيًا جميع تبعيات الأب. Field injection مع @Inject lateinit var متاح فقط في الفئات الموضحة بـ @AndroidEntryPoint — في الفئات العادية يُستخدم constructor injection.
@InstallIn يحدد في أي مكون Hilt يتم تثبيت الوحدة. NetworkModule مع @InstallIn(SingletonComponent::class) متاح في جميع أنحاء التطبيق. Module مع @InstallIn(ActivityComponent::class) متاح فقط في Activity. هذا يعزل الرسوم البيانية للتبعيات: الوحدات الخاصة بـ Activity غير مرئية في Fragment وViewModel، مما يمنع الاستخدام العرضي للتبعيات غير الصالحة. @ApplicationContext هو مؤهل مدمج في Hilt للحصول على Context التطبيق.
عندما يلزم حقن تنفيذين مختلفين لنفس الواجهة، تُستخدم المؤهلات. يدعم Hilt @Named للمعرفات النصية والتعليقات التوضيحية المخصصة مع @Qualifier. على سبيل المثال، @Named("baseUrl") و@Named("imageBaseUrl") لتكوينات نصية مختلفة. المؤهلات المخصصة أفضل من @Named بسبب التحقق في وقت الترجمة — اسم النص غير الصحيح لن يتم اكتشافه حتى وقت التشغيل.
@HiltViewModel هو تعليق توضيحي يحل محل المصنع اليدوي ViewModelProvider.Factory. الفئة الموضحة بـ @HiltViewModel مع @Inject constructor تتلقى تلقائيًا جميع التبعيات عبر Dagger. تولد Hilt ViewModelFactory المستخدمة من قبل Jetpack ViewModelProvider. بدون Hilt، يجب على المطور كتابة المصنع يدويًا، وتمرير كل معلمة من Activity أو Fragment.
@HiltViewModel
class MainViewModel
@Inject constructor(
private val apiService: ApiService,
private val database: AppDatabase
) : ViewModel() {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
_users.value = apiService.getUsers()
}
}
}
// في Activity — Hilt ينشئ ViewModel تلقائيًا
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
ViewModelScoped هو نطاق Hilt للتبعيات التي تعيش طالما يعيش ViewModel. إذا قام اثنان من ViewModels من نفس النوع بحقن نفس التبعية @ViewModelScoped، كل منهما يحصل على نسخته الخاصة. هذا يفرق @ViewModelScoped عن @ActivityScoped، حيث تحصل Activity واحدة على نسخة واحدة لجميع الأجزاء. للتبعيات الخاصة بـ ViewModel (مثل SavedStateHandle)، يُستخدم @HiltViewModel مع @Inject constructor(savedStateHandle: SavedStateHandle).
يدعم Hilt assisted injection من خلال مكتبة Hilt Extensions. يسمح الحقن المساعد بتمرير المعلمات إلى المنشئ في وقت الحقن عندما تكون بعض التبعيات معروفة فقط في وقت التشغيل (مثل معرف المستخدم من intent). للحقن المساعد، يُستخدم @AssistedInject مع معلمات @Assisted. تولد Hilt AssistedFactory التي يمكن حقنها بالطريقة القياسية.
Dagger النقي يتطلب إنشاء يدوي لـ Component، وتحديد النطاقات وتكوين الحقن في كل مكون Android. ينشئ المطور AppComponent وActivityComponent وFragmentComponent ويدير علاقاتهم عبر @Subcomponent. هذا النهج يعطي أقصى تحكم لكنه يتطلب كودًا样板يًا كبيرًا. يُستخدم Dagger في المشاريع الكبيرة التي تتطلب هندسة DI غير قياسية، أو في مشاريع Java/Kotlin غير Android.
Hilt يؤتمت الكود样板ي: @HiltAndroidApp واحد، و@AndroidEntryPoint واحد لكل مكون، ونطاقات محددة مسبقًا. توصي Google بـ Hilt لجميع مشاريع Android الجديدة. تتضمن الهجرة من Dagger إلى Hilt استبدال Component بـ @InstallIn، واستبدال @Subcomponent بمكونات Hilt محددة مسبقًا، واستبدال المصنع اليدوي ViewModelProvider.Factory بـ @HiltViewModel. معظم فئات @Module يتم ترحيلها مع إضافة @InstallIn دون تغيير طرق @Provides.
| الخاصية | Dagger | Hilt |
|---|---|---|
| الإعداد | يدوي: Component وSubcomponent وBuilder | تلقائي: @HiltAndroidApp و@AndroidEntryPoint |
| مكونات Android | غير محددة مسبقًا | 12+ مكونًا مدمجًا |
| ViewModel | مصنع يدوي | @HiltViewModel + @Inject constructor |
| متعدد الوحدات | عبر @Component(dependencies) | عبر @InstallIn + التجميع |
| التعقيد | عالي — يحتاج خبرة | منخفض — مفهوم حدسيًا |
| المرونة | قصوى | قياسية (تغطي 95% من السيناريوهات) |
قيود Hilt: المكتبة تدعم Android فقط (غير مناسبة لمشاريع Java الخادمة البحتة)، تفرض هيكل مكونات معين (يصعب تجاوزه)، وتضيف تبعية على android.hilt:hilt-navigation-compose لمشاريع Jetpack Compose. لتطبيقات Compose، توفر Hilt @HiltViewModel يمكن الوصول إليه في Composable عبر hiltViewModel() — دون الحاجة إلى توفير ViewModel يدويًا من Activity.
الأسئلة الشائعة
Dagger هو إطار عمل DI أساسي في وقت الترجمة مع إعداد يدوي لـ Component وModule. Hilt هي طبقة لـ Android تؤتمت إنشاء المكونات والتكامل مع دورة حياة Activity وFragment وViewModel وService وBroadcastReceiver.
@HiltAndroidApp يفعل توليد مكون Hilt لـ Application. بدون هذا التعليق التوضيحي، لا يمكن لـ Hilt إنشاء SingletonComponent الأساسي الذي ترث منه جميع ActivityComponent وFragmentComponent وViewModelComponent. التعليق التوضيحي إلزامي لأي مشروع Hilt.
Hilt Navigation يوفر @HiltViewModel لـ ViewModel في NavBackStackEntry وhiltNavGraphViewModels() لنطاق ViewModel ضمن رسم بياني التنقل. مكتبة android.hilt:hilt-navigation-fragment تنشئ تلقائيًا ViewModel لكل NavBackStackEntry.
استخدم @ApplicationContext لسياق التطبيق أو @ActivityContext لسياق Activity. توفر Hilt هذه المؤهلات مدمجة في مكتبة android.hilt:hilt-android. @ActivityContext متاح فقط في الوحدات المثبتة في ActivityComponent.
@Binds هو بديل فعال لـ @Provides عندما تقبل الطريقة معلمة واحدة تمامًا وتعيد نوعها كواجهة. يولد @Binds تحويلًا مباشرًا دون استدعاء الطريقة، مما يقلل من كمية الكود المولد ويحسن أداء الحقن.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.