در مقاله قبلی HILT رو مورد بررسی قرار دادیم و مفاهیم اون رو یاد گرفتیم ، در این قسمت یک پروژه با HILT میزنیم و در این بین چیزهای باقی مونده رو هم میپوشونیم ، در این پروژه شما با ترکیب HILT با MVVM و Retrofit آشنا میشید (در واقع اصلی ترین چیزهایی که برای یک پروژه نیاز دارید)
ارتباط با سرور در اندروید - Coroutines + Retrofit
اولی رو حتما باید بخونید ، دومی رو بهتره بخونید ، اگه زمانی که به قسمتی که به دومی ربط داره رسیدیم و دیدید مانع فهمیدنتون شده اونم خوندنش حتمی میشه .
پیشنهاد میکنم اول پروژه رو clone کنید بعد هم زمان مقاله رو بخونید .
پروژه ای خواهیم نوشت که یک ریکوئست به سرور بزند و از سرور یک مدل حاوی Url بگیرد و این Url که یک عکس هست را نمایش دهد
بذارید قبل از توضیح عکس فولدر های پروژه رو با هم ببینیم :
کلاس App که مشخصه ، در پوشه View دو کلاس MainActivity و MainFragment داریم که مشخصا MainFragment داخل MainActivity نشون داده میشه ، در MainFragment ما با استفاده از MyViewModel (در پوشه ViewModel) یک ریکوئست به سرور میزنیم ، در MyViewModel این کار رو با ApiRepo (در پوشه Api) انجام میدیم ، ApiRepo در اینجا محل اتصال ما به تابعی به نام apiCall در فایلِ safeApi هست که ریکوئست های ما اونجا اجرا میشن .
خب قدم به قدم :
کلاسِ App ما کاری قرار نیست انجام بده و صرفا یه بدنه خواهد بود که با HiltAndroidApp مشخص شده ، مثل کاری که در مقاله قبلی کردیم :
برای اضافه کردن Activity و Fragment به HILT باید از AndroidEntryPoint استفاده میکردیم ، برای مثال MainActivity به این صورت در میاد :
اینجا سوال پیش میاد که اون دو تا تابع تهِ کلاس ما چی هستند ؟ من قبلا مقاله ای در مورد وصل کردن Coroutines به Retrofit در ویرگول گذاشته بودم که در اون توضیح دادم چطوری دیگه به روش قدیمی برای ریکوئست زدن به سرور عمل نکنیم و بیاییم یک روش بهینه و قشنگ تر رو با استفاده از Coroutines و LiveData به وجود بیاریم ، چون مفصلا در اون مقاله این قضیه توضیح داده شده در مورد خودِ روش توضیحی نمیدم اما در این روش یک interface به نام RemoteErrorEmitter داشتیم ، این interface مشخص کننده این بود که وقتی به error میخوریم چه واکنشی نشون بدیم ، ما اینجا MainActivity رو از اون Implement کردیم که وقتی به طورکلی به error بخوریم (این error ها چیزایی مثل درست نبودن url ، قطعی نت و ... هستند) چه کار کنیم ، من اینجا چیزی در بنده این دو تابع ننوشتم ولی میتونید یک دیالوگ نشون بدید یا با یه Toast خشک و خالی قضیه رو جمع کنید . نکته ای که جلوتر خواهم گفت اینه که ما باید این RemoteErrorEmitter رو inject کنیم که سر تایمش بررسیش میکنیم .
حالا باید dependency های مورد نیازمون رو که اینجا یک instance از Retrofit میشه رو تهیه (Provide) کنیم ، این instance باید به صورتی باشه که در هر Component یا SubComponentای که باشیم بتونیم ازش استفاده کنیم پس مربوط به ApplicationComponent میشه ، این کار رو در AppModule انجام میدیم :
کاری که در این کد انجام داده میشه ساده است ، Module مشخص کننده اینه که این کلاس یک ماژوله که قراره dependency های ما رو ایجاد کنه ، InstallIn مشخص کننده اینه که مربوط به کدوم Component میشه و Singleton هم مشخص کننده سراسری بودن تابع درون ماژوله (که میتونید نذاریدش) ، بدنه تابع هم که واضحه ، داره یک شی از Retrofit رو برمیگردونه ، از این به بعد ما به راحتی Retrofit رو میتونیم هر جای App خواستیم Inject کنیم .
اما اگه به فایل ها دقت کنید یک ActivityModule ماژول هم داریم ، این مربوط به همون RemoteErrorEmitter میشه که بالا گفتم :
ما در safeApi نیاز داریم یک RemoteErrorEmitter داشته باشیم و این رو باید inject کنیم ، برای تهیه اون هم چون این MainActivity از این کلاس implement شده باید خودِ MainActivity رو بهش برگردونید ، حالا چرا تو ورودی های تابع ما activity رو آوردیم ؟ این ورودی کجا پر میشه ؟ وقتی ما MainActivity رو به گرافِ HILT اضافه کردیم میتونیم ازش درون ماژول ها استفاده کنیم ، نکته اینه که کلا هر dependency ای رو که provide کردید رو میتونید برای ایجاد dependency های دیگه در ورودی ها بنویسید چون قبلا تهیه شده و HILT اون رو خودش داخل ورودی تابع میفرسته که البته این به HILT ربطی نداره و مربوط به Dagger میشه .
حالا میریم سراغ ApiRepo ، این قسمت در مقاله مربوط به اتصال Coroutine و Retrofit نبوده ، ApiRepo کلاسیه که ما قراره در ViewModel تزریق کنیم :
صرفا یک یادآوری بکنم که apiCall دو ورودی داشت ، یکی RemoteErrorEmitter و اون یکی یک تابع که ما در این تابع ریکوئستمون رو صدا میزنیم ، حالا خروجی نهایی به صورت liveData خواهد بود که اون رو میتونیم observe کنیم (اگه تا اینجا رو خوندید و هنوز نمیدونید قضیه apiCall و safeApiCall چیه همین الان stop کنید و برید این مقاله رو سریع بخونید) . ما به صورت constructor injection اومدیم apiService و emitter رو inject کردیم ، طبق توضیح قبلی که دادم emitter در واقع همون MainActivity میشه که provide شده . حالا این کلاس ApiRepo رو کجا صدا بزنیم ؟ داخلِ MyViewModel :
تابع getPhoto که مشخصه ، repository هم که باز به روش constructor injection تزریق شده ولی اون ViewModelInject چیه ؟ این رو در مقاله قبلی نیاورده بودم . اگر شما با Dagger کار کرده باشید میدونید که مشکلی که ViewModel داشت این بود که نمیتونستیم چیزی داخلش inject کنیم و مجبور بودیم هزار تا مصیبت بکشیم تا بتونیم این قضیه رو اوکی کنیم ، اما اینجا یه ViewModelInject قضیه تموم میشه !
حالا دیگه نوبت MainFragment میشه :
مثل Activity و با توجه به توضیحات قسمت اول سری HILT با استفاده از AndroidEntryPoint این Fragment رو داخل گرافِ HILT اضافه میکنیم ، با استفاده از ()by viewModels هم MyViewModel رو میسازیم که این به همون ViewModelInject ربط داره .
نکته :
این by چیه ؟ در کاتلین delegateرو با by انجام میدن ، delegate چیه ؟ خیلی مختصر و مفید میشه وقتی شما میخواید یه چیزی رو مقداردهی کنید ولی میخواید این کار رو کلا به یک کلاس دیگه انجام بدید ، در کاتلین با by میتونیم delegate رو انجام بدیم ، برای مطالعه بیشتر delegate in programming رو در گوگل سرچ کنید (و به نظرم از رو ویکیپدیا انگلیسی بخونید)
و تمام ! دیگه هیچ نیازی به اون Component نویسی های مزخرفی که تو Dagger انجام میدادید نیست ! به چند تا انوتیشن خیلی ساده و شیک همه چیو به هم وصل کردید و هر چی خواستید رو تو هر کجا خواستید Inject کردید .
لینکِ پروژه :
من رو در لینکدین و اینستاگرام دنبال کنید ???
اگه دوست داشتید میتونید به صفحه Spotify بنده هم برید و موسیقی های منو گوش بدید ???