کُدلَب استفاده از دَگر در برنامه اندرویدی (6-SubComponent)

این نوشتار مربوط به بخش ششم از کُدلب آموزشی استفاده از Dagger در پروژه اندرویدی به زبان کاتلین هست و می‌تونید بخش قبلی رو از اینجا بخونید.جریان Registrationبیایید در ادامه جریان Registration رو کامل کنیم، هنوز فرگمنت‌های این فیچر از DI دستی استفاده می‌کنند. دو تا فرگمنت EnterDetailsFragmentو TermsAndConditionsFragmentرو هم مانند اکتیویتی‌ها به متدهای کلاس AppComponent اضافه می‌کنیم.AppComponent.ktفرگمنت EnterDetailsFragmentرو باز کنید تا ببینیم چه فیلدهایی رو می‌خواهیم به دگر معرفی کنیم؟ دو تا ویومدل داره. از طریق فیلد اینجکشن این دو ویو مدل را به دگر معرفی می کنیم و مودیفایر private را هم حذف می کنیم.EnterDetailsFragment.ktو باید مقدار دهی دستی ویومدل ها را هم از کد حذف کنیم.EnterDetailsFragment.ktحالا وقتشه که از نمونه appComponent که در کلاس اپلیکیشن قراره داره برای Inject فرگمنت ها استفاده کنیم. در فرگمنت متد on Attach را اضافه کنید و کد مربوط به Inject را در این متد بعد از super.onAttach قرار دهید.EnterDetailsFragment.ktعادت خوب کدنویسیدر اکتیویتی، درخواست Inject به Dagger در متد on Create و قبل از super نوشته می‌شود.در فرگمنت، درخواست Inject به Dagger در متد on Attach و بعد از super نوشته می‌شود.فقط ویومدل مربوط به این فرگمنت یعنی RegistrationViewModelمونده که از قبل به دَگِر معرفی شده.RegistrationViewModel پس کارمون با فرگمنت EnterDetailsFragmentتمومه! همین کارها رو با فرگمنتTermsAndConditionsFragment هم انجام می‌دهیم:1- فیلدهایی که قراره توسط دگر تامین وابستگی بشوند رو با @Inject انوتیت می کنیم (مثلا registrationViewModel) و مودیفایر private رو حذف می‌کنیم.2. ساخت نمونه از registrationViewModelرو حذف می‌کنیم.چون این کار وظیفه Dagger هست.3. درخواست Inject به دَگِر را در متد onAttach ارسال می‌کنیم.TermsAndConditionsFragment.ktپروژه را اجرا کنید.چه اتفاقی افتاد؟ بعد از ثبت نام برنامه کرش کرد! مشکل اینه که instanceهای متفاوتی از RegistrationViewModelبه RegistrationActivityو EnterDetailsFragmentو TermsAndConditionsFragmentتزریق شده است. اما این آرمان ما نبود! ما می‌خواستیم یک instance یکسان از این ویومدل بین اکتیویتی و فرگمنت‌هاش به اشتراک گذاشته بشه!چقدر شبیه مشکلی که با UserManager داشتیمه نه؟ پس بیاییم ویومدل رو Singleton کنیم چطوره؟خب در این لحظه مشکلمون حل میشه اما در آینده مشکلاتی بوجود میاد.ما نمی خواهیم که instance ویومدل تا وقتی برنامه در حال اجراست در حافظه بمونه حتی وقتی روند ثبت نام یک یوزر به پایان رسید و دیگه ویو مدل رو لازم نداریم!برای هر جریان ثبت نام یک instance متفاوت از ویومدل میخوایم. اگر کاربر ثبت نام و بعد لغو ثبت نام کنه نمی خوایم اطلاعات ثبت نام قبلی در ثبت نام جدید در دسترس باشه.پس چی میخوایم؟میخواهیم فرگمنت‌های مربوط به Registration از ویومدل یکسانی که از اکتیویتی میاد استفاده کنند و همونو reuse کنند اما اگر اکتیویتی تغییر کرد، instance ویومدل هم عوض بشه.پس باید instance ویومدل به لایف سایکل اکتیویتی scope بشه نه کل برنامه!می‌تونیم یک کامپوننت جدید برای Register بسازیم و ویومدل رو به اون scope کنیم. برای اینکار از Dagger subcomponents استفاده می‌کنیم.معرفی Dagger subcomponents ساب کامپوننت ها کامپوننت هایی هستند که گراف Dagger رو از کامپوننت والد ارث بری می کنند.بنابراین همه ی آبجکت‌هایی که در کامپوننت والد provide شدند در این ساب کامپوننت ها provide خواهند شد. با این روش آبجکتی که در ساب کامپوننت هست می تونه به آبجکتی که در کامپوننت والد provide شده، وابستگی داشته باشد.برای ایجاد subComponent مراحل زیر را می رویم:یک فایل جدید به نام RegistrationComponent.ktدر پکیج registration بسازیدیک اینترفیس به نام RegistrationComponent در آن ایجاد و با @Subcomponent حاشیه نویسی‌اش می‌کنیم.registration/RegistrationComponent.ktهمه مواردی که به Registration ربط داره از AppComponent به این subComponent منتقل می‌کنیم.یک اینترفیس فکتوری در این subComponent بسازید تا بتوانیم از آن instance بسازیم.RegistrationComponent.ktبجای آنکه RegistrationActivity یک instance از RegistrationComponentبسازه، یه متد به AppComponent اضافه می‌کنیم تا فکتوری، ساب کامپوننت RegistrationComponentرو برگردونه.AppComponent.kt?به این فکر کنید اگر اینکارو نمی کردیم چی می‌شد و راه حل چی بود؟ آیا این روش کد تمیز تری بهمون میده؟یادآوریدو تا راه متفاوت برای ارتباط با گراف Dagger وجود داره:1- متدی داشته باشیم که خروجی اش unit باشه(در جاوا void) و پارامتر ورودی اش کلاسی باشد که می‌خواهیم فیلد اینجکشن را در آن فعال کنیم. (مثل fun inject(activity: MainActivity))2- متدی داشته باشیم که خروجی اش تایپی باشه که میخواهیم از گراف بدست بیاریم (مثل fun registrationComponent(): RegistrationComponent.Factoryکه تایپ RegistrationComponentرو بهمون میده )از راه دوم استفاده کردیم تاRegistrationActivityبتونه از فکتوری RegistrationComponentاستفاده کنه و ازش instance بسازه. در واقع این متد تایپ RegistrationComponent رو از گراف در اختیارمون قرار داد.چالش ?چطور به AppComponent بگیم که RegistrationComponent یکی از subcomponentهای توست و براش کدهای لازم رو تولید کن؟از طریق DaggerModule!تو پکیج di یک ماژول به نام AppSubcomponentsمی‌سازیم و با @Module حاشیه نویسی‌اش می کنیم. تو همین @Module در بخش پارامتر subcomponentsنام ساب کامپوننتی که ساختیم یعنی RegistrationComponentرو اضافه میکنیم. AppSubcomponents.ktهمچنین اسم این ماژول جدید رو تو لیست ماژول‌های AppComponentاضافه می‌کنیم.AppComponent.ktحالا دیگه AppComponent از ساب کامپوننت‌اش خبر داره!تصویر گراف برنامه تا این لحظههمان طور که در پایین سمت چپ گراف می بینید کلاس های ویو (دو فرگمنت و یک اکتیویتی) توسط RegistrationComponentاینجکت شده اند. از آنجا که RegistrationViewModelو EnterDetailsViewModelفقط توسط کلاس‌هایی که از RegistrationComponentاستفاده می‌کنند درخواست شده‌اند، بخشی از این subcomponent هست بجای AppComponent.در بخش بعدی کُدلب Scope کردن SubComponent ها را خواهید آموخت.بعدیکُدلَب استفاده از دَگر در برنامه اندرویدی (7-Scoping SubComponent) قبلیکُدلَب استفاده از دَگِر در برنامه اندرویدی (5-استفاده از Scope)کُدلَب استفاده از دَگر در برنامه اندرویدی (4-تزریق گراف به اکتیوتی)کُدلَب استفاده از دَگِر در برنامه اندرویدی (3-انوتیشن‌ها)کُدلَب استفاده از دَگِر در برنامه اندرویدی (2-شروع)کُدلَب استفاده از دَگِر در برنامه اندرویدی(1-معرفی)