فرشته ناجی
فرشته ناجی
خواندن ۵ دقیقه·۳ سال پیش

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

این نوشتار مربوط به بخش هفتم از کُدلب آموزشی استفاده از Dagger در پروژه اندرویدی به زبان کاتلین هست و می‌تونید بخش قبلی رو از اینجا بخونید.

در بخش‌های قبلی کُدلَب یک subcomponent ساختیم به نام RegistrationComponentچرا؟

چون یک instance از ویومدل داشته باشیم که بین اکتیویتی و فرگمنت هاش مشترک باشه نه در کل برنامه.

قبلا دیدیدم که اگر ما کامپوننت و کلاس های داخلی اش رو با scope annotation یکسانی مشخص کنیم. باعث میشه که تایپ مون مثلا اینجا ویو مدل مون ، instance یکسانی در اون کامپوننت داشته باشه.

اما

نمی تونیم دوباره از اسکوپ انوتیشن Singleton استفاده کنیم. چون یکبار توسط کامپوننت اصلی برنامه استفاده شده است.

پس

باید یک Scope Annotation جدید بسازیم!

اسمش چی باشه؟

آیا اسم @RegistrationScope خوبه؟ نه ! اسم Scope Annotation نباید مستقیما هدف رو مشخص کنه. اسمش باید وابسته به لایف تایم اش باشه تا بتونه توسط کامپوننت‌های هم رده‌ی خودش استفاده مجدد بشه (مثل LoginComponent و غیره)

قوانین Scoping

وقتی یک تایپ مثل یک ویومدل با Scope Annotation مشخص میشه فقط توسط کامپوننت‌هایی که با همون scope مشخص شده اند قابل استفاده است.
وقتی یک کامپوننت رو با یک Scope Annotation مشخص می‌کنیم، ایشون می‌تونه فقط تایپ‌هایی که با اون انوتیشن مشخص شده‌اند یا اون‌هایی که هیچ انوتیشنی ندارند رو provide کنه.
یک ساب کامپوننت نمی‌تونه از Scope Annotation که توسط پدرش بکار رفته، استفاده کنه

یک فایل ActivityScope.ktبه نام در پکیج di بسازید و ActivityScopeرو اینطوری تعریف کنین:

ActivityScope.kt
ActivityScope.kt

برای اسکوپ کردن RegistrationViewModelبه RegistrationComponentباید این کلاس و اینترفیس هر دو با @ActivityScopeحاشیه نویسی بشن.

RegistrationViewModel.kt
RegistrationViewModel.kt
RegistrationComponent.kt
RegistrationComponent.kt

در نتیجه این تغییرات دیگه همیشهRegistrationComponent یک نمونه از RegistrationViewModelتامین خواهد کرد.

لایف سایکل SubComponent ها

میخواهیم ببینیم لایف سایکل این subcomponent که ساختیم باید چی باشه. AppComponent که به لایف سایکل اپلیکیشن متصل شده بود چون می خواستیم تا وقتی برنامه در حافظه است فقط یک instance از گراف برنامه داشته باشیم. از طرفی گفتیم میخواهیم subcomponent بین اکتیویتی و فرگمنت هاش به اشتراک گذاشته بشه و هر وقت اکتیویتی عوض شد یک نمونه جدید ازش ساخته شه ، بنابراین باید به لایف سایکل اکتیویتی متصل بشه. برای رسیدن به این هدف یک رفرنس به آن در اکتیویتی می سازیم همان طور که instance کامپوننت AppComponent را در کلاس اپلیکیشن ساختیم.

RegistrationActivity.kt
RegistrationActivity.kt

سپس یک instance از RegistrationComponentدر متدonCreate قبل از super.onCreateمی سازیم و اکتیویتی را بهregistrationComponentبجای appComponentتزریق می‌کنیم.

  1. یادتونه یک متد در AppComponent گذاشتیم که RegistrationComponentفکتوری رو برمیگردوند؟ اون متد رو فراخوانی میکنیم و بعد هم متد create رو تا از RegistrationComponentنمونه بسازیم
  2. با استفاده از این instance اکتیویتی رو به این ساب کامپوننت inject میکنیم.
  3. حالا دیگه فیلدهایی که تو این اکتیویتی هستن و با @Injectمشخص شده اند توسطRegistrationComponent تامین وابستگی خواهند شد. اگر گیج شدید تصویر زیر رو نگاه کنید.
RegistrationActivity.kt
RegistrationActivity.kt
اگر دقت کرده باشید متغیر registrationComponentبا@Inject انوتیت نشده است. چون انتظار نداریم این متغیر توسط Dagger پرواید شود.

حالا نوبت فرگمنت هاست:

یک نمونه از RegistrationComponent در اکتیویتی داریم و از همون برای Inject فرگمنت ها به RegistrationComponent استفاده میکنیم. یادتونه که ! در متد on Attach.

EnterDetailsFragment.kt
EnterDetailsFragment.kt

همین مراحل رو برای فرگمنت TermsAndConditionsهم برید.

TermsAndConditionsFragment.kt
TermsAndConditionsFragment.kt

پروژه را اجرا کنید.

مثل قبل از register خارج و دوباره register کنید. خواهید دید که روند ثبت نام به درستی انجام می‌شود ولی Setting باعث میشه که برنامه دچار کرش شود چون هنوز از دگر استفاده نمی کند که در ادامه انجام خواهد شد.

گراف اپلیکیشن تا این لحظه به صورت زیر درآمده است:


تفاوت این گراف با گراف بخش قبل این است که RegistrationViewModelبهRegistrationComponent اسکوپ شده است که این موضوع با یک دایره قرمز کوچک روی RegistrationViewModelمشخص شده است.



افزودن فیچر Login به Dagger

جدا از قضیه scope کردن آبجکت‌ها به لایف سایکل‌های متفاوت، ساختن subcomponent یک عادت خوب برنامه نویسی است که بخش‌های مختلف برنامه رو از هم encapsulate می کند.

اینکه طوری برنامه رو طراحی کنید که یک sub graph برای هر فیچر یا جریان برنامه بسازید باعث میشه از نظر حافظه و زمان باز شدن، برنامه پرفرمنس بیشتری پیدا کنه و مقیاس پذیر بشه.

از اینکه یک گراف کلی داشته باشید که تمام آبجکت ها رو بسازه به شدت اجتناب کنید چون باعث میشه کامپوننت Dagger ناخوانا بشه و قابلیت ماژولار کردن نداشته باشه.

با این توضیحات بیایید جریان Login رو ریفکتور کنیم تا این بخش هم به دگر اضافه بشه. برای اینکار میخواهیم یک subcomponent جدید برای جریان Login بسازیم.

یک فایل به نام LoginComponent.ktدر پکیج loginبسازید. LoginComponentرا همان طور که کامپوننت RegistrationComponentرا ساختیم، تعریف کنید و کلاس های مربوط به لاگین را به آن اضافه کنید.

login/LoginComponent.kt
login/LoginComponent.kt

یادتونه برای Annotation Scope ای که ساختیم یک نام جامع گذاشتیم تا کامپوننت های مشابه اش بتونن ازش استفاده کنن؟ حالا LoginComponentرو باActivityScope انوتیت میکنیم.

بعد LoginViewModelرا با روش constructor Inject به Dagger معرفی می‌نماییم.

LoginViewModel.kt
LoginViewModel.kt


?از این بعد حواسمون هست که کدوم کلاسها باید با انوتیشتن @ActivityScope مشخص بشن. اما LoginViewModelقرار نیست توسط کلاس دیگه ای reuse بشه پس لازم نیست با این Annotation مشخص بشه.

همچنین باید این SubComponent جدید را به لیست subcomponentهای کامپوننت AppComponentدر کلاس AppSubcomponentsاضافه نماییم.

AppSubcomponents.kt
AppSubcomponents.kt


مثل اکتیویتی قبلی، برای اینکه LoginActivityبتونه به فکتوری این ساب کامپوننت جدید دسترسی داشته باشه، باید یک متد در AppComponentبذاریم که فکتوری اونو برگردونه.

AppComponent.kt
AppComponent.kt


حالا دیگه همه چی فراهم شده تا یک instance از LoginComponentبسازیم و در LoginActivityآن را inject کنیم.

مثل ابرای اینکه del را @Injectکنید و ساخت آن را حذف کنید تا این وظیفه روی دوش Dقرار بگیرد.

2. فکتوری LoginComponentرا ازappComponent با فراخوانی متد loginComponent()بدست بیاورید و یک نمونه از LoginComponentبا متد create بسازید و injectرا با ارسال اکتیویتی انجام دهید.

3. ساخت دستی loginViewModelرا حذف کنید.

LoginActivity.kt
LoginActivity.kt

حالا پروژه را اجرا کنید.

جریان Login باید به درستی کار کند.? با LoginComponentجدید گراف برنامه به صورت زیر خواهد بود:

سولوشن کُدلب تا این لحظه در 2_subcomponents برنچ قرار دارد.

در بخش بعدی کُدلب قراره Setting به دگر معرفی شود و داشتن چندین اکیتیوتی با Scope یکسان را خواهید آموخت.

بعدی

کُدلَب استفاده از دَگِر در برنامه اندرویدی(8-داشتن چند اکتیویتی در یک Scope) بزودی...

قبلی

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

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

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

کُدلَب استفاده از دَگِر در برنامه اندرویدی (3-انوتیشن‌ها)

کُدلَب استفاده از دَگِر در برنامه اندرویدی (2-شروع)

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




daggerkotlinتزریق وابستگیکاتلینdi
برنامه نویس اندروید @NeshanMap
شاید از این پست‌ها خوشتان بیاید