چالش ساخت اپ اندروید برای گرفتن قیمت بلیت!

عید امسال رو به خوردن و فیلم دیدن میگذروندم تا اینکه یه وظیفه‌ای رو بهم محول کردن?. اونم این بود که قیمت بلیت‌ هواپیمارو توی یه سایتی چک کنم تا وقتی که خیلی ارزون شد، ازش بخریم. چند دفعه‌‌ای توی سایت رفتم و قیمت‌هارو نگاه کردم ولی بعدش گفتم چکاریه که خودم چک کنم، بجاش یه اپ اندروید درست میکنم تا قیمت بلیت رو هر ۱۵ دقیقه بگیره و اگر از یه حدی ارزون‌تر بود، خبرم کنه.

چون پروژه برای خودم بود و وقت هم داشتم، هرچیزی که این یه مدت میخواستم امتحان کنم ولی نشده بود رو توی این پروژه استفاده کردم!? دو روزی ازم وقت گرفت ولی تجربه‌ی خوبی بود، در ادامه چندتا نکته از این تجربه رو میگم. در ضمن سورس پروژه رو هم توی گیت‌هاب منتشر کردم تا شاید به عنوان نمونه سورس به کار کسی بیاد یا حتی بعدا خودم چیزهای دیگه‌ای رو روش تست کنم. لینک ریپوی گیت‌هاب پروژه:

https://github.com/abbas-oveissi/Charter

تصاویر اپ
تصاویر اپ


نکاتی که میخواستم بگم اینا هست:

گرفتن اطلاعات بلیت

برای گرفتن اطلاعات بلیت از WorkerManager استفاده کردم. APIشو خیلی خوب درست کردن و کار باهاش خیلی ساده هست. برای این پروژه از هر دو مدل PeriodicWorkRequest و OneTimeWorkRequest استفاده کردم. برای PeriodicWorkRequest از حداقل intervalش یعنی ۱۵ دقیقه استفاده کردم ولی اگر گوشی به حالت Doze بره، این interval دیگه رعایت نمیشه و هر زمان گوشی اجازه بده، عملیات سینک انجام میشه.

ثبت تاریخ و زمان در Room

قبلا از Room استفاده کرده بودم ولی خب این سری میخواستم تاریخ و زمان (یا همون DateTime) رو داخلش ذخیره کنم. دنبال یه روش اصولی بودم، خود Room برای ذخیره DateTime تایپ نداره ولی یه چیزی به اسم TypeConverter که میشه ازش توی این قضیه کمک گرفت. توی سرچ‌هام به یه مقاله‌ی عالی از chris bans رسیدم که عنوانش Room + Time هست. پیاده‌سازی رو از روی این مقاله انجام دادم. حتی اگر هنوز SQLite استفاده میکنید باز توصیه میکنم که حتما بخونیدش.

Room + Time - Chris Banes

استفاده از کتابخونه‌ی Paging

خیلی وقت بود که دلم میخواست از Paging استفاده کنم ولی فرصتش نبود. این پروژه باعث شد که سراغ این کتابخونه‌ برم و برای نمایش لیست تاریخچه ازش استفاده کنم. یه چیزی که در مورد Paging میخوندم و برام جالب بود، ایدشون در مورد Placeholderها بود که توی لینک زیر توضیح دادن. خودشون معایب و مزایاشو گفتن.

PagedList - Placeholders

کتابخونه‌ی Paging به سه مدل معماری میتونه کار کنه (شکل زیر). من از مدل وسطی استفاده کردم. باید توی یه پروژه‌ی دیگه روش ترکیبی Network و Databaseش رو امتحان کنم، بنظر خیلی باحاله. اینجوری هست که از دیتابیس اطلاعات رو توی لیست نمایش میده، وقتی دیگه داخل دیتابیس چیزی نمونده باشه، خبرتون میکنه تا از Network اطلاعات جدید رو بگیرید و دوباره توی دیتابیس بریزید تا Paging دوباره نشونشون بده.

وابستگی UI به دیتابیس

ترکیب استفاده از WorkerManager و Room و LiveData و Paging باعث شد که یجورایی اپ بصورت offline first بشه. برنامه اینطوری کار میکنه که UIش اطلاعات رو بوسیله‌ی LiveData از Room میگیره و نشون میده، یعنی هرموقع برنامه رو باز کنید، آخرین اطلاعات رو میبنید. از اونطرف WorkerManager هر بار اطلاعات جدید رو از وب‌سرویس میگیره، دیتابیس رو آپدیت میکنه و کاری با UI نداره. اما چون از LiveData استفاده شده، خود Room به UI اطلاعات به روز شده رو میده تا UI هم آپدیت بشه.

وب‌سرویس کاتلینی!

اول که برنامه رو نوشته بودم، مستقیم اطلاعات رو از سایت اصلی بلیت میگرفت. بعد یکی از دوستان سایت liara.ir رو بهم معرفی کرد. سرویسشون اینقدر جذاب بود که تصمیم گرفتم برای تست کردنش یه وب‌سرویس درست کنم و روی سرویسشون دیپلوی کنم تا ببینم کارشون چطوره. وب‌سرویس هم اینجوری کار میکنه که یه واسط بین اپ اندروید و سایت اصلی هست. اطلاعات رو از سایت اصلی میگیره و با فرمت بهتری به اپ میده. برای ساخت وب‌سرویس از spring boot و gradle و kotlin استفاده کردم. فکر نمیکردم روزی برسه که با دست‌های خودم به پروژه gradle wrapper اضافه کنم تا کارم راحت‌تر بشه :)) وب‌سرویس رو از روی لینک زیر پیاده کردم

How to build a Simple REST API with Kotlin and Spring Boot

مزیت کاتلین و گریدل بودن وب‌سرویس این بود که یه سری کدهارو همراه با کتابخونه‌ها (مثل Gson و OkHttp) کپی کردم و عینا توی وب‌سرویس استفاده کردم. خیلی حال داد.

البته برای اینکه وب‌سرویس رو روی liara دیپلوی کنم، براش Dockerfile ساختم. توضیحات بیشتر در مورد داکرایز کردن رو میتونید از پست قبلیم توی ویرگول بخونید.

پینوشت ۱: بخش نمایش ناتیفیکیشن رو فرصت نکردم پیاده کنم، خیلی کار سختی نیست، فقط کافیه یه شرط گذاشته بشه تا هرموقع بلیت از فلان قیمت پایینتر بود، یه ناتیفیکیشن نشون داده بشه.