برای دانلود سورس اینجا کلیک کنید
در این مقاله ، نحوه اجرای معماری MVVM با Hilt ، RxJava ، Retrofit ، Room ، Live Data و View Binding را خواهیم دید.
این مقاله برای شخصی است که میخواهد
- تزریق وابستگی جدید با استفاده از Hilt
- مهاجرت به Hilt از داگر
را یاد بگیرد.
در این پروژه ، جزئیات که یک API REST تشکیل می دهد را به دست می آوریم ،و سپس از Room برای ذخیره آن به صورت آفلاین استفاده می کنیم. از آنجا که تعدادی مفاهیم در اینجا مورد استفاده قرار می گیرند، به تک تک آنها خواهیم پرداخت.
ساختار پروژه:
ابتدا تمام وابستگی های لازم را به پروژه خود اضافه کنید.
تنظیم Api Service
برای واکشی جزئیات در اینجا از PokeApi استفاده می کنیم.
همانطور که می بینید نوع بازگشت Observable است ، در مورد RxJava اطلاعات بعدی را بعداً در مقاله مشاهده خواهید کرد.
کلاس مدل Pokemon و PokemonResponse ما به این شکل است:
تنظیم Hilt
کلاس BaseApplication این کلاس برای Hilt لازم است و باید انوتیشین @HiltAndroidApp را برای آن بنویسیم فراموش نکنید که این کلاس را به manifest اضافه کنید
اکنون ماژول Network را ایجاد خواهیم کرد.
حالا ماژول چیست؟
ماژول کلاسی است که اطلاعاتی را در مورد چگونگی تهیه instance از کلاس که ما از آن برخوردار نیستیم ، در اختیارمان قرار می دهد. برای اینکه بتوانیم در Hilt از اشیاء استفاده کنیم از انوتیشن Inject@ بالاتر از سازنده کلاس استفاده می کنیم اما هنگامی که ما یک کلاس نداریم یا وقتی با یک interface کار می کنیم که سازنده ندارد ، کجا می توانیم انوتیشن @inject را قرار دهیم. در این موارد ما از انوتیشن Provide@ در داخل کلاس ماژول استفاده می کنیم تا به hilt بگوییم که این object ها را برای ما بسازد.
برای راه اندازی ماژول یک کلاس NetworkModule و حاشیه نویسی آن با انوتیشن Module@ اکنون باید یک انوتیشن دیگر اضافه کنیم که انوتیشن InsatallIn@ است و ما ApplicationComponent را به آن پاس میدهیم زیرا ما می خواهیم NetworkModule برای application scope در دسترس ما باشد.در ماژول متدی را برای دریافت شیء PokeApiService ارائه خواهیم داد. یک متد PokeApiService از نوع بازگشت PokeApiService ایجاد کنید و آن را با انوتیشن Provide@ بنویسید.
برنامه ما دو fragment دارد که یکی از آنها لیست pokemon گرفته شده از API را نشان میدهد و fragment دوم pokemon مورد علاقه خود را که با استفاده از Room ذخیره کرده ایم نشان می دهد و ما از دکمه ای برای جابجایی بین این fragment ها استفاده خواهیم کرد.
تنظیماتRepository
ما از Inject@ در بالای سازنده Repository استفاده کرده ایم تا هر زمان که به یک شی Repository احتیاج داشته باشیم ، یک Repository را برای ما فراهم می کند. کلاس Repository دارای دو وابستگی PokeApiService و PokeDao است.
PokeDao مربوط به Room است که آن را نادیده می گیرد و اکنون در مقاله در مورد آن صحبت خواهیم کرد.
نکته مهم این است که اگر ما می خواهیم hilt به ما Repository را ارائه دهد ، ما همچنین باید بگوییم که چگونه نمونه ای از کلاس ها یا interface هایی را که کلاس Repository ما به آنها وابستگی دارد ارائه دهیم. ما قبلاً NetworkModule را ایجاد کرده ایم که متدی را برای گرفتن یک شیء PokeApiService ارائه می دهد. بنابر این هنگامی که ما Repository را در اختیار داریم Hilt به صورت خودکار تمام وابستگی های کلاس Repository را تامین میکند. ما به سادگی یک متد getPokemons ایجاد کرده ایم تا Observable از نوع PokemonResponse را بازگردانیم.
تنظیمات ViewModel
در اینجا ما انوتیشن جدید ViewModelInject را مشاهده می کنیم و به من اعتماد کنید بهترین کار در مورد استفاده از Hilt است. اگر در پروژه های خود با MVVM از Dagger استفاده کرده اید ، باید با مشکل تزریق ViewModel ها روبرو شده باشید. معروف ترین راه حل ایجاد کلاس ViewModelFactory بود اما این کار طولانی و خسته کننده بود. اما با Hilt ، انوتیشن تمام کارها را انجام می دهد و به راحتی می توانید ViewModels را تزریق کنید. در اینجا ما یک متد getPokemons ایجاد کرده ایم. همانطور که می دانیم repository.getPokemons () یک Observable را برمی گرداند ما آن را observe می کنیم و سپس pokemonList (Mutable Live Data) را تنظیم می کنیم تا fragment تغییرات را به روز کند.
RxJava دارای سه مفهوم اساسی Observable ، Observers و Operators است.
observable به عنوانentity برای برخی از داده های منتشر شده قابل مشاهده است اما اگر کسی در observable آن نباشد ، چه فایده ای دارد از این رو observe کنید ، بنابراین ما Observer هایی داریم که داده های منتشر شده توسط Observable ها را مشاهده می کنند. اپراتورها چیزی است که داده ها را دستکاری می کند و یا داده ها را دگرگون می کند و آن را به subscribers ها می دهد.
SubscribOn (Schedulers.io ()) به این معنی است که می خواهیم در thread بکگراند subscribe شویم و observeOn(AndroidSchedulers.mainThread()) به این معنی است که می خواهیم داده ها را در thread اصلی مشاهده کنیم.
در اینجا از اپراتور map استفاده می شود ، زیرا ما می خواهیم Pokemon List را دریافت کنیم ، اما ما یک Observable از PokemonReosponse را دریافت می کنیم تا بتوانیم لیست pokemon را از pokemonResponse.getResults() بگیریم.
اما ما همچنین می خواهیم یک چیز دیگر را تغییر دهیم. URL ای که از Pokemon Object دریافت می کنیم حاوی جزئیاتی درباره pokemon است اما ما فقط تصویر pokemon را می خواهیم بنابراین نمایه pokemon را از این URL دریافت می کنیم و سپس آن را با URL های مختلف به هم می زنیم که مستقیماً آدرس تصویر pokemon را در اختیار ما قرار می دهد.
تنظیمات Room
برای تنظیم Room ، ما به سه چیز Entity ، Dao و Database نیاز داریم.
برای Entity ، کلاس Pokemon Pojo را مانند زیر به روز خواهیم کرد
برای Dao ، ما یک Interface به اسم PokeDao به شرح زیر ایجاد خواهیم کرد:
ما متد هایی را برای قرار دادن Pokemon در لیست علاقه مندی های ما ایجاد کرده ایم ، pokemon را از لیست حذف کرده و همه پوکمون ها را از لیست دریافت می کنیم
اکنون ما برای ذخیره pokemon مورد علاقه هایمان کلاس پایگاه داده PokemonDB که از نوع abstract هست را ایجاد می کنیم.
ما یک متد abstract از نوع بازگشت PokeDao اضافه کرده ایم.
اکنون DatabaseModule را ایجاد خواهیم کرد که در صورت داشتن این پایگاه داده به نمونه سازی کمک می کند.
ما انوتیشن های @Module و@InstallIn را در بالای این کلاس اضافه کرده ایم تا به Hilt بگوییم این یک ماژول است و برای application scope لازم است.
@Singleton برای داشتن یک نمونه واحد(instance single) از این بانک اطلاعاتی در کل برنامه استفاده می شود. اکنون کلاس Repository و کلاس PokemonViewModel را اصلاح خواهیم کرد.
تنظیمات Room ، Hilt و ViewModel را به پایان رسانده ایم. اکنون Fragment و Activity را تنظیم خواهیم کرد.
تنطیمات Fragment و Activity
فرگمنتHome
ما این fragment را با انوتیشن AndroidEntryPoint نوشته ایم ، بدین معنی که Hilt باید تمام وابستگی های این fragment را که می خواهد ارائه دهد.
نکته مهمی که باید به آن توجه داشت:
اگر کلاس Android را با انوتیشن AndroidEntryPoint مینویسید، باید کلاسهای Android که به آن بستگی دارد را با همین انوتیشن بنویسید. به عنوان مثال ، اگر یک fragment را با این انوتیشن مینویسید ، پس از آن باید Activity هایی را که در آن قسمت استفاده می کنید با این انوتیشن بنویسید.
ما از کلاس ItemTouchHelper برای عملکرد swiping function استفاده کرده ایم و می خواهیم وقتی یک آیتم از لیست را تاچ کردیم و به سمت راست کشیدیم pokemon را به موارد دلخواه اضافه کند و با کشیدن انگشت به سمت چپ pokemon مورد علاقه را حذف کرده و آنها را از لیست موارد دلخواه حذف کند.
فرگمنت Favorites
ما همان کارهایی را انجام داده ایم که در فرگمنت Home انجام داده ایم و فقط تغییر جهت را در آیتم TouchHelper تغییر می دهیم.
Main Activity
در Activity ، از دکمه برای تغییر fragment ها استفاده کرده ایم و از viewbinding هم استفاده کرده ایم.
برای افزودن دیدگاه خود، نیاز است ابتدا وارد حساب کاربریتان شوید