Dagger — این یک فریمورک تزریق وابستگی برای Java و Kotlin است که کد DI را در مرحله کامپایل از طریق پردازش حاشیهنویسی تولید میکند. Hilt — لایهای روی Dagger برای اندروید است که تنظیم کامپوننتها و چرخه حیات را ساده میکند. بر اساس Google، 2025، Hilt در بیش از 70 درصد از برنامههای اندرویدی Google Play Top-100 استفاده میشود و Activity، Fragment، ViewModel و Service را از طریق کامپوننتهای از پیش تعریفشده پشتیبانی میکند. هر دو فریمورک بررسی گراف وابستگی در زمان کامپایل را تضمین میکنند و خطاهای runtime در تزریق را حذف میکنند.
نکات اصلی
Dagger — این یک فریمورک تزریق وابستگی با تولید کد در مرحله کامپایل (compile-time DI) است. Dagger که در Square توسعه داده شد و بعداً به Google منتقل شد، از پردازنده حاشیهنویسی Java APT برای تحلیل گراف وابستگی و تولید کلاسهای فابریک استفاده میکند. برخلاف runtime-DI (Guice، Koin)، Dagger از بازتاب (reflection) استفاده نمیکند — تمام کد در زمان کامپایل ساخته میشود که حداکثر عملکرد در زمان اجرا و شناسایی خطاها در مرحله build را تضمین میکند.
Hilt — این یک کتابخانه از Google است که روی Dagger ساخته شده و برای اندروید بهینه شده است. Hilt کامپوننتهای از پیش تعریفشدهای متناسب با چرخه حیات کامپوننتهای اندرویدی ارائه میدهد: @SingletonComponent برای Application، @ActivityComponent برای Activity، @FragmentComponent برای Fragment، @ViewModelComponent برای ViewModel. این کار تنظیمات تکراری Component و Module را که در Dagger خالص لازم است حذف میکند. Hilt همچنین گراف وابستگی را برای هر کامپوننت اندرویدی به صورت خودکار از طریق @AndroidEntryPoint تولید میکند.
طبق Google I/O 2024، Hilt راهحل پیشنهادی برای DI در برنامههای اندرویدی نوشتهشده با Kotlin است. کتابخانههای Jetpack (Navigation، Room، WorkManager) یکپارچهسازی داخلی با Hilt از طریق @HiltViewModel و @HiltWorker دارند. در پروژههایی که از اندروید استفاده نمیکنند (کتابخانههای خالص Java/Kotlin، برنامههای سرور)، Dagger خالص بدون لایه Hilt استفاده میشود.
بدون فریمورک DI، توسعهدهنده اشیاء را به صورت دستی از طریق سازندهها یا فابریکها ایجاد میکند و وابستگیها را به صورت زنجیرهای منتقل میکند. هر نیاز جدید — تغییر امضای همه سازندهها در زنجیره است. Dagger این فرآیند را خودکار میکند: کافی است اعلام کنید چه نوعی مورد نیاز است (@Inject constructor) و Dagger گراف وابستگی را ایجاد میکند و همه انواع تو در تو را حل میکند. هنگام تغییر وابستگیها، Dagger کد تولیدشده را به صورت خودکار بهروزرسانی میکند — اشتباه در زنجیره غیرممکن است.
تزریق وابستگی (Dependency Injection) الگویی است که در آن شیء وابستگیهای خود را از بیرون دریافت میکند، نه اینکه خودش آنها را بسازد. DI اصل وارونگی کنترل (IoC) را پیادهسازی میکند: کلاس مسئول ایجاد وابستگیهای خود نیست، بلکه آنها را از طریق سازنده، متد یا فیلد اعلام میکند. تزریق از طریق سازنده (constructor injection) ارجحترین روش در نظر گرفته میشود، زیرا تضمین میکند شیء در وضعیت معتبر ایجاد میشود.
| نوع تزریق | سینتکس Dagger | چه زمانی استفاده شود |
|---|---|---|
| Constructor injection | @Inject constructor | روش اصلی — برای همه کلاسهای شخصی |
| Field injection | @Inject lateinit var | فقط برای کامپوننتهای اندرویدی (Activity، Fragment) |
| Method injection | @Inject fun bind() | برای مقداردهی post-construct |
مزایای اصلی DI شامل قابلیت تست (وابستگیها از طریق اشیاء mock جایگزین میشوند)، اتصال ضعیف (کلاسها به رابطها وابستهاند نه پیادهسازیها) و مدیریت صریح چرخه حیات اشیاء از طریق scopes است. Dagger به صورت خودکار تضمین میکند که شیء فقط یک بار در محدوده scope خود ایجاد میشود و هنگام خروج از scope از بین میرود.
Component — عنصر مرکزی گراف وابستگی Dagger است. این یک رابط است که با @Component حاشیهنویسی شده و پل ارتباطی بین Module و اهداف تزریق را توصیف میکند. Dagger پیادهسازی Component (مثلاً DaggerAppComponent) را در مرحله کامپایل تولید میکند. Component مشخص میکند چه نوعهایی برای تزریق در دسترس هستند، از طریق متدهای انتزاعی که انواع مورد نیاز را برمیگردانند یا از طریق متدهای inject که شیء را برای field injection دریافت میکنند.
// ماژول: وابستگیهایی را فراهم میکند که 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)
}
}
// کامپوننت: Module و اهداف Injection را به هم متصل میکند
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module — این کلاسی است که شامل متدهای @Provides است و نمونههایی از وابستگیها را برمیگرداند. Module برای نوعهایی استفاده میشود که Dagger نمیتواند به صورت خودکار ایجاد کند: کتابخانههای شخص ثالث (OkHttp، Retrofit)، اشیاء دارای پارامتر سازنده، رابطهایی با انتخاب پیادهسازی. @Binds — جایگزین @Provides برای مواردی است که متد یک رابط را برمیگرداند و فقط یک پیادهسازی را میپذیرد: Dagger یک cast مستقیم بدون فراخوانی متد تولید میکند.
@Scope طول عمر شیء را در گراف وابستگی تعیین میکند. @Singleton — شیء یک بار برای کل برنامه ایجاد میشود. @ActivityScoped — شیء تا زمانی که Activity زنده است زندگی میکند. @FragmentScoped — تا زمانی که Fragment زنده است. بدون scope، Dagger در هر تزریق یک نمونه جدید ایجاد میکند. @Reusable — scope برای اشیایی که لزومی به singleton بودن ندارند اما ایجاد آنها گران است — Dagger میتواند نمونه را کش کند اما این را تضمین نمیکند.
Hilt تنظیم Dagger برای اندروید را از طریق کامپوننتهای از پیش تعریفشده و تولید خودکار گراف پایه ساده میکند. حاشیهنویسی @HiltAndroidApp روی کلاس Application تولید کامپوننت Hilt را آغاز میکند. بدون این حاشیهنویسی Hilt کار نمیکند — برای هر برنامه اندرویدی که از 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 — qualifier داخلی Hilt برای دریافت Context برنامه است.
هنگامی که باید دو پیادهسازی متفاوت از یک رابط تزریق شود، از qualifierها استفاده میشود. Hilt از @Named برای شناسههای رشتهای و حاشیهنویسیهای سفارشی با @Qualifier پشتیبانی میکند. مثلاً @Named("baseUrl") و @Named("imageBaseUrl") برای پیکربندیهای رشتهای متفاوت. qualifierهای سفارشی به دلیل بررسی در مرحله کامپایل بر @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 — scope در Hilt برای وابستگیهایی است که تا زمانی که ViewModel زنده است زندگی میکنند. اگر دو ViewModel از یک نوع یک وابستگی @ViewModelScoped را تزریق کنند، هر کدام نمونه خود را دریافت میکند. این تفاوت @ViewModelScoped با @ActivityScoped است، جایی که یک Activity یک نمونه واحد برای همه fragmentها دریافت میکند. برای وابستگیهای مخصوص ViewModel (مثلاً SavedStateHandle) از @HiltViewModel با @Inject constructor(savedStateHandle: SavedStateHandle) استفاده میشود.
Hilt از assisted injection از طریق کتابخانه Hilt Extensions پشتیبانی میکند. Assisted injection امکان انتقال پارامترها به سازنده را در زمان تزریق فراهم میکند، زمانی که بخشی از وابستگیها فقط در زمان اجرا شناخته شدهاند (مثلاً ID کاربر از intent). برای assisted injection از @AssistedInject در ترکیب با پارامترهای @Assisted استفاده میشود. Hilt AssistedFactory را تولید میکند که میتوان آن را به روش استاندارد تزریق کرد.
Dagger خالص به ایجاد دستی Component، تعریف scopeها و پیکربندی تزریق در هر کامپوننت اندرویدی نیاز دارد. توسعهدهنده AppComponent، ActivityComponent، FragmentComponent را ایجاد میکند و ارتباطات آنها را از طریق @Subcomponent مدیریت میکند. این رویکرد حداکثر کنترل را میدهد اما به کد boilerplate قابل توجهی نیاز دارد. Dagger در پروژههای بزرگ که معماری DI غیراستاندارد لازم است، یا در پروژههای غیر-اندرویدی Java/Kotlin استفاده میشود.
Hilt boilerplate را خودکار میکند: یک @HiltAndroidApp، یک @AndroidEntryPoint برای هر کامپوننت، scopeهای از پیش تعریفشده. Hilt توسط Google برای همه پروژههای جدید اندرویدی توصیه میشود. مهاجرت از Dagger به Hilt شامل جایگزینی Component با @InstallIn، جایگزینی @Subcomponent با کامپوننتهای از پیش تعریفشده Hilt و جایگزینی ViewModelProvider.Factory دستی با @HiltViewModel است. اکثر کلاسهای @Module با افزودن @InstallIn بدون تغییر متدهای @Provides منتقل میشوند.
| ویژگی | Dagger | Hilt |
|---|---|---|
| پیکربندی | دستی: Component، Subcomponent، Builder | خودکار: @HiltAndroidApp، @AndroidEntryPoint |
| کامپوننتهای اندروید | از پیش تعریفشده ندارد | بیش از 12 کامپوننت داخلی |
| ViewModel | فابریک دستی | @HiltViewModel + @Inject constructor |
| چندماژولی | از طریق @Component(dependencies) | از طریق @InstallIn + تجمیع |
| پیچیدگی | بالا — به تجربه نیاز دارد | کم — به طور شهودی قابل درک |
| انعطافپذیری | حداکثر | استاندارد (95% سناریوها را پوشش میدهد) |
محدودیتهای Hilt: کتابخانه فقط از اندروید پشتیبانی میکند (برای پروژههای صرفاً سمت سرور جاوا مناسب نیست)، ساختار خاصی از کامپوننتها را تحمیل میکند (بازتعریف آنها دشوار است) و وابستگی android.hilt:hilt-navigation-compose را برای پروژههای Jetpack Compose اضافه میکند. برای برنامههای Compose، Hilt @HiltViewModel را فراهم میکند که در Composable از طریق hiltViewModel() در دسترس است — بدون ارائه دستی ViewModel از Activity.
سوالات متداول
Dagger — فریمورک پایه compile-time DI با پیکربندی دستی Component و Module است. Hilt — لایهای برای اندروید است که ایجاد کامپوننتها و یکپارچهسازی با چرخه حیات Activity، Fragment، ViewModel، Service و BroadcastReceiver را خودکار میکند.
@HiltAndroidApp تولید کامپوننت Hilt را برای Application فعال میکند. بدون این حاشیهنویسی، Hilt نمیتواند کامپوننت پایه SingletonComponent را ایجاد کند که همه ActivityComponent، FragmentComponent و ViewModelComponent از آن ارث میبرند. این حاشیهنویسی برای هر پروژه Hilt ضروری است.
Hilt Navigation @HiltViewModel را برای ViewModel در NavBackStackEntry و hiltNavGraphViewModels() را برای scoping ViewModel در محدوده گراف ناوبری فراهم میکند. کتابخانه android.hilt:hilt-navigation-fragment به صورت خودکار ViewModel را برای هر NavBackStackEntry ایجاد میکند.
از @ApplicationContext برای context برنامه یا @ActivityContext برای context Activity استفاده کنید. Hilt این qualifierها را به صورت داخلی در کتابخانه android.hilt:hilt-android فراهم میکند. @ActivityContext فقط در ماژولهایی در دسترس است که در ActivityComponent نصب شدهاند.
@Binds — جایگزین مؤثر @Provides است، زمانی که متد دقیقاً یک پارامتر میپذیرد و نوع آن را به عنوان رابط برمیگرداند. @Binds یک cast مستقیم بدون فراخوانی متد تولید میکند که مقدار کد تولیدشده را کاهش میدهد و عملکرد را در تزریق افزایش میدهد.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید