در این مقاله سه کار انجام میدیم ، اول یک سری توضیحات در مورد Dagger2 که در مقاله قبلی گفته نشد (چون بیسیک بود) ، بعد اون میام فرگمنت ها رو توضیح میدم و در نهایت با مفهومی به اسم Scope کار رو تموم میکنم ، در مقاله Vol2 که منتشر خواهم کرد در مورد Api در Dagger2 صحبت خواهم کرد
خب آیا این یه مفهومه که تو مقاله قبلی نداشتیم ؟ چرا داشتیم ولی من برای این قسمت توضیحش رو گذاشتم و نیازی هم نبود توضیحش بدم چون در واقع شما نمیدیدیش !
طبق تعریف داکیومنتِ خودِ Dagger :
یک کامپوننت که ویژگی های کامپوننت اصلی شما رو به ارث ببره
حالا ما توی کد قسمت قبلی اینو کجا داشتیم ؟
@Module
abstract class ActivityBuilderModule {
@ContributesAndroidInjector(modules = [AuthViewModelModule::class,AuthModule::class])
abstract fun contributeAuthActivity() : AuthActivity
// AuthActivity is a client , we can inject sth to it
}
توی این قسمت در واقع کدهایی در پس زمینه ساخته میشه که برای ما AuthComponent رو میسازن ، یک کامپوننت که ویژگی های AppComponent رو داره و وقتی کارمون در این صفحه (AuthActivity) تموم میشه از بین میره (در صوتری که AppComponent باقی میمونه ، در واقع AuthComponent زیرمجموعه AppComponent حساب میشه .
اگه شما در این قسمت بیایید میبینید کدی ساخته شده :
و در داخل این کلاس میتونیم این SubComponent رو ببینیم :
خب من توضیح SubComponent رو دادم ، بیایید یک شمای کلی از چیزی که قراره داشته باشیم بهتون نشون بدم :
در شمای کلی AuthComponent که بالاتر توضیح دادم جزئی از AppComponent حساب میشه ، وقتی کار ما با صفحه AuthActivity تموم میشه این Component از بین رفته و میریم سراغ DetailComponent ، زیر هر Component اسم Module هایی که درش هست رو نوشتم ، شما با viewModelFactoryModule و AuthViewModelModule فعلا کاری نداشته باشید .
ما قبلا یاد گرفتیم که وقتی Activity جدیدی رو اضافه میکنیم چطوری به Dagger وصلش کنیم و تو این مقاله هم توضیح دادم که در واقع در بک گراند میاد یک SubComponent با این کار میسازه ، حالا مثل همون کارو باید برای DetailActivity انجام بدید پس میرید در ActvityBuilderModule (طبق مقاله قبلی) و به این شکل در میاریدش :
@Module
abstract class ActivityBuilderModule {
@ContributesAndroidInjector(modules = [AuthViewModelModule::class,AuthModule::class])
abstract fun contributeAuthActivity() : AuthActivity
// AuthActivity is a client , we can inject sth to it
@ContributesAndroidInjector(modules = [DetailFragmentBuilderModule::class])
abstract fun contributeDetailActivity() : DetailActivity
// DetailActivity is a client , we can inject sth to it
}
اگه دقت کنید ما دو جا تغییر دادیم ، یکی اینکه به AuthActivity یک سری Module اضافه کردیم که با AuthViewModelModule کاری نداریم فعلا ولی AuthModule رو توی همین مقاله توضیح میدم و عینا برای DetailActivity هم یک SubCompnent ساختیم ، ماژولی که برای Detail در نظر گرفتیم اسمش هست DetailFragmentBuilderModule که در واقع میاد کار مشابه ActivityBuilderModule رو اینبار برای Fragment های اون Activity انجام میده ، پس به نوعی دوباره یه SubComponent میسازه که Component مادرش خودش یه SubComponent هستش !
@Module
abstract class DetailFragmentBuilderModule {
@ContributesAndroidInjector()
abstract fun contributeDetailFragment() : DetailFragment
// and other fragment DetailActivity has
}
هر Fragment جدیدی که قراره جزئی از DetailActivity باشه رو اینجا تعریف میکنیم (که در این مثال کلا یه دونه است) و حالا باید خودِ Fragment رو بسازیم ، اگه یادتون باشه ما Activity رو از DaggerAppCompatActivity ارث بردیم که Dagger بتونه بشناستش و کدی که بالاتر زدیم کار کنه ، همین کارو این بار برای Fragment با ارث بری از DaggerFragment انجام میدیم
class DetailFragment : DaggerFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_detail,container,false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// some code
}
}
فکر نمیکنم توضیح خاصِ دیگه ای داشته باشه ، این طور نیست ؟ خودِ Dagger پشت صحنه داره SubComponent ها رو میسازه و چیزایی که لازمه رو انجام میده (مثل مقاله های سری Basic از مجموعه Dagger)
معنی لغوی Scope میشه محدوده ، ما از Scope برای مشخص کردن مرز هر Component استفاده میکنم و همین طور برای اینکه instance هایی برای قسمت های مختلف provide میکنیم رو حفظ کنیم (تا تغییر نکنند)
اول از همه میام و به AppComponent یک Scope جدید میدم
@Singleton
@Component(modules =
[AndroidSupportInjectionModule::class, // special class
ActivityBuilderModule::class,
AppModule::class])
interface AppComponent : AndroidInjector<App>{...}
همون طور که میبینید با @Singleton اونو مشخص کردم ، خب این Scope که اسمش Singleton هست چه میکنه ؟ با این کار ما AppComponent رو به عنوان یک Component که حالت Global داره مشخص کردیم یعنی کلیه dependency هایی که شما توسط AppComponent براتون تهیه میشه تا زمانی که ما از AppComponent استفاده میکنیم به صورت Singleton باقی میمونن (فقط یکبار ازشون instance گرفته میشه و برای همیشه همون باقی میمونه) ، حالا باید بریم سراغ Component های دیگه هر کدوم رو Scope بندی کنیم .
طبق چیزی که تو همین مقاله در مورد SubComponent یاد گرفتید میخوایم این کارو برای اونها انجام بدیم ، پس کدی که در ActivityBuilderModule رو به این صورت تغییر میدیم :
@Module
abstract class ActivityBuilderModule {
@AuthScope
@ContributesAndroidInjector(modules = [AuthViewModelModule::class,AuthModule::class])
abstract fun contributeAuthActivity() : AuthActivity
// AuthActivity is a client , we can inject sth to it
@DetailScope
@ContributesAndroidInjector(modules = [DetailFragmentBuilderModule::class])
abstract fun contributeDetailActivity() : DetailActivity
// DetailActivity is a client , we can inject sth to it
}
الان دو SubComponent که اونها رو در این قسمت تعریف کردیم به وسیفه AuthScope و DetailScope مشخص شدند ، ولی ما یک کار رو انجام ندادیم ، اونم اینه که خودِ AuthScope و هر Scope دیگه ای رو چطوری تعریف کنیم ؟ خیلی ساده و به این صورت :
@Scope
@MustBeDocumented
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class AuthScope
اگر هنوز کاربرد Scope رو متوجه نشدید یک بار دیگه بهتر میگم :
فرض کنید شما یک خرگوش دارید و این خرگوش چهار تا فرزند داره ، قرارداد میکنیم که به محض اینکه هر فرزند از بین رفت فرزندای اون هم از بین بره ، پس با از بین رفتن خرگوش بالا باید چهار فرزندش هم از بین برن ، ما این مساله رو در SubComponent ها داریم ولی برای اینکه بهتر به سیستم بفهمونیم باید از Scope استفاده کنیم ، همین طور اگه از Scope استفاده نکنیم هر باری که بیاییم یکی instance از چیزی که قراره provide کنیم بگیریم ، Component میاد کلیه مراحل ساخت اون object رو از اول انجام میده ولی اگه بیاییم Scope بندی کنیم فقط و فقط یکبار این کارو میکنه و باقی دفعات از اون instance که قبلا ساخته استفاده میکنه ، همه این کارا برای بهتر شدن معماری و نظم بخشیدن و مشخص کردن و جداسازی اجزا از همه .
در vol2 از سری intermediate با ویژگی های دیگه از Dagger2 آشنا میشیم و کم کم اونو به سمت کامل شدن و ترکیبش با mvvm میبریم . . .
کد این قسمت رو از branch به اسم inter1 بردارید (یک مقدار کد های اضافی داره که در نظر نگیرید)