مسلما برنامه نویسی واکنشگرا یا برنامه نویسی Reactive یکی از اصول مهمیه که شما به عنوان یک توسعه دهنده اپِ موبایل باید بلد باشید ، حتما (یا احتمالا (یا شایدم اصلا)) با RxJava آشنایی دارید ، ما با RxJava (یا RxAndroid یا RxKotlin ، هر دوشون بر پایه RxJava هستند) میتونیم خیلی کارا بکنیم ، میتونیم اونو وصل کنیم به سرویس های API مون ، میتونیم باهاش برنامه های Event Base (برنامه هایی که نسبت به یک عمل قراره واکنش بدند) بنویسم ، میتونیم تو معماری MVVM استفاده کنیم و هزاران استفاده دیگه (البته هزاران که خالی بندیه) .
اما LiveData چطور ؟ با وجود LiveData آیا ما لازمه بازم با RxJava کار کنیم ؟ جواب یک کلمه هستش : "بله" ، شما به هر حال به عنوان یه توسعه دهنده باید RxJava رو بلد باشید ، خیلی از پروژه های قدیمی هستند که بر پایه RxJava نوشته شدند و ممکنه شما توی شرکتی بری که نیاز باشه از اونا پشتیبانی کنی ، به اضافه اینکه خیلی از کارایی که با RxJava میشه کرد رو نمیشه با LiveData کرد .
تو این مقاله میخوایم بررسی کنیم ببینم تکلیفمون چیه و چطوری باید از این دو تا در کنار هم استفاده کنیم .
خب بذارید یه توضیح خیلی خیلی خیلی کوتاه در مورد خود LiveData بدم :
بر طبق مستندات گوگل ، LiveData یه کلاس بر پایه دیزان پترنِ Observable هستش ولی برعکس حالت عادی که ما داریم LiveData به چرخه حیات (lifecycle) مجهزه ، برعکس RxJava نیازی به استفاده از Disposable ها برای آزادسازی مموری نیست ، نیازی نیست مثل RxJava ما خودمون بیایم بهش حالی کنیم که دیتا تغییر کرده و خودش به محض تغییرات دیتامون عکس العمل نشون میده .
شما باید LiveData تون رو در کلاس ViewModel داشته باشید و بعدا اونو توی قسمت View برای بروزرسانی صفحه یا هر کار دیگه ای استفاده کنید ، یه کد ساده که مال خود داکِ گوگله رو با هم ببینیم :
همون طوری که میبینید ما با استفاده از تغییراتِ LiveData میتونیم نسبت به وضعیت Aware یا آگاه بشیم و مطابق با اون واکنش نشون بدیم (یه نکته هم بگم ، اون کلمه lazy یعنی تا موقعی که شی ما صدا نشه نمیایم بهش حافظه اختصاص بدیم)
اگه بخوایم یه لیست از مزایا و معیاب LiveData نسبت به Rx بگیم میتونیم به اینا اشاره کنیم :
خب الان ما مزایا و معایب رو لیست کردیم ، الان چیکار کنیم ؟ Rx رو بندازیم دور یا LiveData رو پرت کنیم تو سطح آشغال ؟ "هیچکدوم" ! جفتشو ترکیب میکنیم !!!! الان میخوایم یه اپی رو بنویسم که جفتش رو با هم به کار ببریم (کدی که برای این قضیه نوشتم رو آخر پست براتون میذارم)
هدف اینه که یه لیست از حیوانات رو از سرور بگیریم و نشون بدیم ، معماری این اپ رو با MVVM جلو میبریم ، اولین گام تعریف کلاس دیتامون هست :
data class Animal (
val name : String?,
val taxonomy: Taxonomy?,
val location: String?,
val speed : Speed?,
val diet : String?,
//@SerializedName("lifespan") optional if you use correct spell
val lifespan : String?,
val image: String?
)
بعد از اون باید کلاس ViewModel رو ایجاد کنیم :
class ListViewModel(appliaction : Application) : AndroidViewModel(appliaction)
توی این کلاس ما سه پارامتر از LiveData استفاده میکنیم و جنس اون LiveData رو هم مشخص میکنیم ، یکی داده ها ، یکی برای ارور و یکی هم برای loading ، دو پارامتر دیگه یکی disposable هست که برای ترکیب Rx نیاز داریم تا بتونیم بعدا Rx رو clear کنیم ، یکی هم که باید api مون رو به برنامه با Dagger تزریق (Inject) کنیم (اگه شما با Dagger آشنایی نداری اصلا مهم نیست ، با همین چند تا تیکه کد میتونید بفهمید اینجا چیکار داریم میکنیم و بعدا خودتون تو اندروید استدیو اپ مشابه رو بزنید) :
val animals by lazy { MutableLiveData<List<Animal>>() }
val loadError by lazy { MutableLiveData<Boolean>() }
val loading by lazy { MutableLiveData<Boolean>() }
privateval disposable = CompositeDisposable()
@Inject
lateinit var apiService : AnimalApiService
واضحه که ما در View نسبت به مقادیری که این پارامتر ها دارن قراره واکنش نشون بدیم و UI رو بروز کنیم خب کاری که من میخوام بکنم اینه ، میخوایم حیواناتی از لیست رو داشته باشیم که تو اسمشون حتما حرف "o" وجود داشته باشه ، پس باید از اپراتور filter استفاده کنیم :
disposable.add(apiService.getAnimals(key)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map {
it.filter {
it.name!!.toLowerCase().contains("o")
}
}
.subscribe(
{
loading.value = false
loadError.value = false
animals.value = it
},
{
it.printStackTrace()
loadError.value = true
loading.value = false
}
))
بقیه کار به راحتی توی لایه View قابل پیاده سازیه ، ما اونجا اول باید یک شی از viewModel داشته باشیم :
private lateinit var viewModel : ListViewModel
بعدش باید بیاییم متناسب با کارایی که میخوایم بکنیم یک سری Observer تعریف کنیم و بعدش عملیات هامون رو بنویسم ، در یکی باید لیست تشکیل بشه ، در یکی باید علامت Loading بیاد (پروگرسبار) و در یکی هم باید خطا نمایش داده بشه (این قضیه it?let اینه که میاد بررسی میکنه اگه it مخالف null باشه کد پایینش رو اجرا میکنه ، اجرای این کد سوای اینکه منوط به null نبودن it هست به animalListDataObserver هم بستگی داره و فقط وقتی اجرا میشه که ما animalListDataObserver رو تغییر بدی) :
private var animalListDataObersver = Observer<List<Animal>>()
{
it?.let{
list_animals.visibility = View.VISIBLE
adapterList.updateAnimalList(it)
}
}
private var loadingObersver = Observer<Boolean>()
{
loadingView.visibility = if(it) View.VISIBLE else View.GONE
list_animals.visibility = if(it) View.GONE else list_animals.visibility
txt_error.visibility = if(it) View.GONE else list_animals.visibility
}
private var errorObersver = Observer<Boolean>()
{
txt_error.visibility = if(it) View.VISIBLE else View.GONE
}
حالا توی onCreate (البته اینجا من تو onViewCreated نوشتم چون فرگمنته) باید پارامتر های viewModel رو به این Observer هایی که تعریف کردیم وصل کنیم :
viewModel = ViewModelProviders.of(this).get(ListViewModel::class.java)
viewModel.animals.observe(this,animalListDataObersver)
viewModel.loading.observe(this,loadingObersver)
viewModel.loadError.observe(this,errorObersver)
viewModel.refresh()
تهش میبینیم که همچین خروجی داریم و همشون کاراکتر "o" رو دارند :
خب اینم از این ، دوستان اگر سوال ، انتقاد یا ایراد علمی نسبت به این مقاله دارید حتما در کامنت منو مطلع کنید ، موفق باشید !
Code Sample (MVVM + RxJava + LiveData +Dagger)