این نوشتار مربوط به بخش هفتم از کُدلب آموزشی استفاده از Dagger در پروژه اندرویدی به زبان کاتلین هست و میتونید بخش قبلی رو از اینجا بخونید.
در بخشهای قبلی کُدلَب یک subcomponent ساختیم به نام RegistrationComponent
چرا؟
چون یک instance از ویومدل داشته باشیم که بین اکتیویتی و فرگمنت هاش مشترک باشه نه در کل برنامه.
قبلا دیدیدم که اگر ما کامپوننت و کلاس های داخلی اش رو با scope annotation یکسانی مشخص کنیم. باعث میشه که تایپ مون مثلا اینجا ویو مدل مون ، instance یکسانی در اون کامپوننت داشته باشه.
اما
نمی تونیم دوباره از اسکوپ انوتیشن Singleton استفاده کنیم. چون یکبار توسط کامپوننت اصلی برنامه استفاده شده است.
پس
باید یک Scope Annotation جدید بسازیم!
آیا اسم @RegistrationScope
خوبه؟ نه ! اسم Scope Annotation نباید مستقیما هدف رو مشخص کنه. اسمش باید وابسته به لایف تایم اش باشه تا بتونه توسط کامپوننتهای هم ردهی خودش استفاده مجدد بشه (مثل LoginComponent و غیره)
وقتی یک تایپ مثل یک ویومدل با Scope Annotation مشخص میشه فقط توسط کامپوننتهایی که با همون scope مشخص شده اند قابل استفاده است.
وقتی یک کامپوننت رو با یک Scope Annotation مشخص میکنیم، ایشون میتونه فقط تایپهایی که با اون انوتیشن مشخص شدهاند یا اونهایی که هیچ انوتیشنی ندارند رو provide کنه.
یک ساب کامپوننت نمیتونه از Scope Annotation که توسط پدرش بکار رفته، استفاده کنه
یک فایل ActivityScope.kt
به نام در پکیج di بسازید و ActivityScope
رو اینطوری تعریف کنین:
برای اسکوپ کردن RegistrationViewModel
به RegistrationComponent
باید این کلاس و اینترفیس هر دو با @ActivityScope
حاشیه نویسی بشن.
در نتیجه این تغییرات دیگه همیشهRegistrationComponent
یک نمونه از RegistrationViewModel
تامین خواهد کرد.
میخواهیم ببینیم لایف سایکل این subcomponent که ساختیم باید چی باشه. AppComponent که به لایف سایکل اپلیکیشن متصل شده بود چون می خواستیم تا وقتی برنامه در حافظه است فقط یک instance از گراف برنامه داشته باشیم. از طرفی گفتیم میخواهیم subcomponent بین اکتیویتی و فرگمنت هاش به اشتراک گذاشته بشه و هر وقت اکتیویتی عوض شد یک نمونه جدید ازش ساخته شه ، بنابراین باید به لایف سایکل اکتیویتی متصل بشه. برای رسیدن به این هدف یک رفرنس به آن در اکتیویتی می سازیم همان طور که instance کامپوننت AppComponent را در کلاس اپلیکیشن ساختیم.
سپس یک instance از RegistrationComponent
در متدonCreate
قبل از super.onCreate
می سازیم و اکتیویتی را بهregistrationComponent
بجای appComponent
تزریق میکنیم.
RegistrationComponent
فکتوری رو برمیگردوند؟ اون متد رو فراخوانی میکنیم و بعد هم متد create رو تا از RegistrationComponent
نمونه بسازیم@Inject
مشخص شده اند توسطRegistrationComponent
تامین وابستگی خواهند شد. اگر گیج شدید تصویر زیر رو نگاه کنید.اگر دقت کرده باشید متغیرregistrationComponent
با@Inject
انوتیت نشده است. چون انتظار نداریم این متغیر توسط Dagger پرواید شود.
حالا نوبت فرگمنت هاست:
یک نمونه از RegistrationComponent
در اکتیویتی داریم و از همون برای Inject فرگمنت ها به RegistrationComponent
استفاده میکنیم. یادتونه که ! در متد on Attach.
همین مراحل رو برای فرگمنت TermsAndConditions
هم برید.
پروژه را اجرا کنید.
مثل قبل از register خارج و دوباره register کنید. خواهید دید که روند ثبت نام به درستی انجام میشود ولی Setting باعث میشه که برنامه دچار کرش شود چون هنوز از دگر استفاده نمی کند که در ادامه انجام خواهد شد.
گراف اپلیکیشن تا این لحظه به صورت زیر درآمده است:
تفاوت این گراف با گراف بخش قبل این است که RegistrationViewModel
بهRegistrationComponent
اسکوپ شده است که این موضوع با یک دایره قرمز کوچک روی RegistrationViewModel
مشخص شده است.
جدا از قضیه scope کردن آبجکتها به لایف سایکلهای متفاوت، ساختن subcomponent یک عادت خوب برنامه نویسی است که بخشهای مختلف برنامه رو از هم encapsulate می کند.
اینکه طوری برنامه رو طراحی کنید که یک sub graph برای هر فیچر یا جریان برنامه بسازید باعث میشه از نظر حافظه و زمان باز شدن، برنامه پرفرمنس بیشتری پیدا کنه و مقیاس پذیر بشه.
از اینکه یک گراف کلی داشته باشید که تمام آبجکت ها رو بسازه به شدت اجتناب کنید چون باعث میشه کامپوننت Dagger ناخوانا بشه و قابلیت ماژولار کردن نداشته باشه.
با این توضیحات بیایید جریان Login رو ریفکتور کنیم تا این بخش هم به دگر اضافه بشه. برای اینکار میخواهیم یک subcomponent جدید برای جریان Login بسازیم.
یک فایل به نام LoginComponent.kt
در پکیج login
بسازید. LoginComponent
را همان طور که کامپوننت RegistrationComponent
را ساختیم، تعریف کنید و کلاس های مربوط به لاگین را به آن اضافه کنید.
یادتونه برای Annotation Scope ای که ساختیم یک نام جامع گذاشتیم تا کامپوننت های مشابه اش بتونن ازش استفاده کنن؟ حالا LoginComponent
رو باActivityScope
انوتیت میکنیم.
بعد LoginViewModel
را با روش constructor Inject به Dagger معرفی مینماییم.
?از این بعد حواسمون هست که کدوم کلاسها باید با انوتیشتن @ActivityScope
مشخص بشن. اما LoginViewModel
قرار نیست توسط کلاس دیگه ای reuse بشه پس لازم نیست با این Annotation مشخص بشه.
همچنین باید این SubComponent جدید را به لیست subcomponentهای کامپوننت AppComponent
در کلاس AppSubcomponents
اضافه نماییم.
مثل اکتیویتی قبلی، برای اینکه LoginActivity
بتونه به فکتوری این ساب کامپوننت جدید دسترسی داشته باشه، باید یک متد در AppComponent
بذاریم که فکتوری اونو برگردونه.
حالا دیگه همه چی فراهم شده تا یک instance از LoginComponent
بسازیم و در LoginActivity
آن را inject کنیم.
مثل ابرای اینکه del
را @Inject
کنید و ساخت آن را حذف کنید تا این وظیفه روی دوش Dقرار بگیرد.
2. فکتوری LoginComponent
را ازappComponent
با فراخوانی متد loginComponent()
بدست بیاورید و یک نمونه از LoginComponent
با متد create بسازید و inject
را با ارسال اکتیویتی انجام دهید.
3. ساخت دستی loginViewModel
را حذف کنید.
حالا پروژه را اجرا کنید.
جریان Login باید به درستی کار کند.? با LoginComponent
جدید گراف برنامه به صورت زیر خواهد بود:
سولوشن کُدلب تا این لحظه در 2_subcomponents برنچ قرار دارد.
در بخش بعدی کُدلب قراره Setting به دگر معرفی شود و داشتن چندین اکیتیوتی با Scope یکسان را خواهید آموخت.
بعدی
کُدلَب استفاده از دَگِر در برنامه اندرویدی(8-داشتن چند اکتیویتی در یک Scope) بزودی...
کُدلَب استفاده از دَگر در برنامه اندرویدی (6-SubComponent)
کُدلَب استفاده از دَگِر در برنامه اندرویدی (5-استفاده از Scope)
کُدلَب استفاده از دَگر در برنامه اندرویدی (4-تزریق گراف به اکتیوتی)
کُدلَب استفاده از دَگِر در برنامه اندرویدی (3-انوتیشنها)
کُدلَب استفاده از دَگِر در برنامه اندرویدی (2-شروع)
کُدلَب استفاده از دَگِر در برنامه اندرویدی(1-معرفی)