Dagger2 In Android - Part 1 - Basic - Vol 1

Dagger2مطمئنا Dagger یکی از مهم ترین مطالبی است که شما به عنوان یک برنامه نویس اندروید باید بلد باشید . در سری مقاله هایی که می‌نویسم اون رو با هم بررسی می‌کنیم .تزریق وابستگی ها - Dependency Injectionوابستگی چیه ؟ اولا که وقتی میگیم Dependency منظورمون اون چیزایی که شما تو فایل gradle می‌نویسید نیست ! هر کلاس می‌تواند به کلاس های دیگر وابسته باشد ، مثلا کلاس "ماشین" وابستگی به کلاس "چرخ" داره و این وابستگی باید به اون تزریق بشه ، یکی از راه ها اینه که ما تو "سازنده" یا "constructor" کلاسِ "ماشین" ، اِلمانِ کلاس "چرخ" رو بیاریم و ما با این کار به نوعی عملِ "تزریق وابستگی رو انجام دادیم" ، ولی اگه بعدا سازنده رو عوض کنیم چی ؟ هزار جای کد باید این تغییر رو اعمال کنیم ! پس بهترین کار اینه که به نحوی بیاییم این عملیات رو در لایه های بالاتری از انتزاع (Abstraction) انجام بدیم .کتاب‌خانه Dagger2قبل از Dagger2 کتابخونه Dagger وجود داشت که گوگل از اون یه fork زد و Dagger2 رو درست کرد ، Dagger2 کتابخونه‌ایه که برای ما این تزریق وابستگی رو انجام میده و میاد برامون بر حسب کدهایی که می‌زنیم یک سری کلاس هایی رو تولید می‌کنه که اونا در پشت صحنه عملیات DI رو برامون انجام میدن ، البته اینو بگم که Dagger2 یک حالت کلی داره که برای جاوا استفاده میشه و یک حالت مخصوص برای اندروید ، چیزی که من تو این مقاله به شما میگم توضیح بر اساس اندرویدشه ولی شما میتونید حالتی که برای جاوا هست رو هم استفاده کنید .معماریِ کلی Dagger2workflowطبق شکلی که بالا می‌بینید Dagger از سه قسمت تشکیل میشه ، یکی کلاس های عادی مثل Activity و ... که Dependency هایی دارن که باید Inject بشن ، یک Interface به نام Component که قراره این Dependency ها رو برامون تزریق کنه و یک Module که متعلق به اون Component هست و وظیفه اش تهیه یا Provide کردن و درست کردن این Dependency هاست . خیلی ساده و شیک !خب ما قدم به قدم شروع می‌کنیم و یه پروژه ساده رو براتون بالا میاریم ، به نظرم با کد بهتر تفهیم میشه .ساخت Componentنکته : اگه این قسمت رو نفهمیدید مهم نیست ، همیشه این قسمت رو قراره Copy Paste کنید ولی حتما یه بار بخونید و بعد برید Vol2 این نوشته رو بخونیدطبق عکس و توضیح بالا ما یک interface نیاز داریم ، اسمشو هر چی دوست دارید بذارید ولی چون ترجیح ما نظم بخشیدن به کاراست پس اسمای با معنی استفاده می‌کنیم ، اولین Component ما یک Component سراری هست ، یعنی همه جای برنامه می‌تونه استفاده بشه و وظیفه اش تهیه چیزای کلیه ، بنابراین اسمشو میذاریم AppComponent و با @Component به سیستم می‌فهمونیم که یک Component هست@Singleton @Component interface AppComponent : AndroidInjector<App> { // we are going to inject App // fun inject(folan : Folan) }چیزی که اونو ازش implement کردیم برای اینه که به سیستم بفهمونیم ما قراره این وابستگی رو به کلاس App تزریق کنیم پس باید کلاس App رو همین الان درست کنیم منتها به جای اینکه از Application ارث ببریم می‌آییم از DaggerApplication ارث بری می‌کنیم ، کلاس App توی مباحث DI یکی از کلاس هایی هست که باید اونو برای باقی قسمت ها تهیه کنیم ، برای این کار به روش کلاسیک Dagger عمل نمی‌کنیم ، با روش های جدید خودش خیلی از کارا رو انجام میده ، این کدی که الان نوشتم کامل نیست ، تابعی که override شده باید این کار رو بکنه ولی ما الان هنوز چیزی نداریم !class App : DaggerApplication() { override fun applicationInjector(): AndroidInjector<out DaggerApplication> { return null } }در حین استفاده از Dagger وقتی شما Component ها و ... رو می‌سازید میاد و براتون یک سری کلاس تولید می‌کنه که با استفاده از اون کلاس ها باید DI رو انجام بدید . من الان نیاز دارم به جای null وردارم و AppComponent رو build بگیرم و داخل کلاس App رو وارد کنم که بعدا به جاهای مختلف inject بشه ولی کلاس واسط هنوز ساخته نشده . برای ساخته شدنش کد AppComponent رو کامل می‌کنم :@Singleton @Component(modules = [AndroidSupportInjectionModule::class]) interface AppComponent : AndroidInjector<App> { // we are going to inject App @Component.Builder // override Builder interface Builder{ @BindsInstance // bind an object to the component at time of its construction fun application(app : Application) : Builder fun build() : AppComponent } // fun inject(folan : Folan) }اون تابعی که comment کردم روش قدیمی قضیه است که شما یک داده از جنس Folan یا مثلا Application رو می‌نوشتی و بعدا Dagger اونو به جایی که می‌خواستی inject می‌کرد . خب ما الان خیلی تغییرات دادیم که باید قدم به قدم توضیح بدم . اولا که هر Component یک سری Module داره که اینجا اولیش رو نوشتم یعنی AndroidSupportInjectionModule ، این یک Module آماده و پیشفرض خودِ Dagger هست که وقتی شما به Component مورد نظر وصلش کنید Dagger براتون کلاسی به اسم DaggerAppComponent رو می‌سازه (کلمه Dagger رو به اضافه اسم Component می‌کنه) ، این کلاس رو در App استفاده خواهیم کرد ، AppComponent از AndroidInjector ارث برده (در واقع چون interface هست نمیشه گفت ارث و باید گفت implement ولی چون در کاتلین بر عکس جاوا علامت این دو یکسانند منم در کل این مقاله همیشه میگم ارث) و این کلاس یک Builder داره (به نظرم این مقاله رو مورد دیزاین پترن Builder بعدا بخونید) و ما نیاز داریم کلاس App خودمون رو وارد Builder بکنیم ، @Component.Builder میاد و برای ما این Builder رو override می‌کنه و ما می‌تونیم یک سری توابع جدید بهش اضافه کنیم (که دو تا کردیم) ، @BindsInstance میاد یک شی رو که در اینجا از جنس Application هست رو در زمانی که سازنده یا constructor ساخته میشه وصل می‌کنه به Component ، تابع build هم که بهمون یه خروجی از جنس AppComponent میده، حالا وقتی شما پروژه تون رو Re-Build کنید کلاس DaggerAppComponent برای شما ساخته میشه ! شما می‌تونید کلاس App رو به این شکل تکمیل کنید :class App : DaggerApplication() { override fun applicationInjector(): AndroidInjector<out DaggerApplication> { return DaggerAppComponent.builder().application(this).build() } }از این به بعد هرجایی که ما نیاز داشته باشیم از کلاس App استفاده کنیم خیلی راحت می‌تونیم اونو Inject کنیم . ممکنه توضیحاتی که در بالا داده باشم رو نفهمیده باشید که اهمیتی نداره ، این قسمتی که تا الان نوشتم یک چیز کاملا آماده است که همیشه قراره همینو Copy و Paste کنید پس نگران نباشید و برید سراغ قسمت دوم مقاله که کمی مباحث رو بیشتر بسط دادم .لینک کدها رو در قسمت دوم میذارم .