ترفندهای RxJava در اندروید - قسمت اول

اسم کتابخانه RxJava 2 رو احتمالا تا حالا زیاد شنیدید و ممکنه حتی ازش استفاده هم بکنید. بسیاری از برنامه‌نویس بدون اینکه درک درستی از مفاهیم جریان‌های داده (Data Stream) داشته باشن و با مدل برنامه نویسی Reactive آشنا باشن به طور مختصری از RxJava در پروژه‌هاشون استفاده می‌کنند.

در حالیکه قدرت اصلی RxJava در متودهای عملگر (Operator Methods) اون هست. تو این پست سعی دارم تعدادی از این عملگرها رو با مثال عملی توضیح بدم و نشون بدم که اگر درست از RxJava و اپراتورهاش استفاده کنید چقدر برنامه‌نویسی براتون جذاب‌تر، کدهاتون خواناتر و حالتون بهتر خواهد بود.


Reactive Extentions (Rx)
Reactive Extentions (Rx)

یادآوری: تمامی مثال‌های این پست به زبان Kotlin خواهد بود.


۱- ترکیب ۲ جریان در یک جریان

گاهی اوقات لازم میشه که ما اطلاعاتی رو از دو جریان متفاوت بگیریم. ولی هنگام نمایش باید این ها رو به صورت همزمان و یا ترکیب شده نمایش بدیم.

تصور کنید که ما ۲ endpoint در سرور داریم که یکی فهرست پست‌های کاربر و دومی فهرست همه پست‌ها رو بر می‌گردونه. به صورت زیر:

@GET("posts/mine")
fun getMyPosts(): Single<List<Post>>

@GET("posts")
fun getAllPosts(): Single<List<Post>>

حالا برای ترکیب این دو تا جریان به صورتی که بعد از اینکه حتما هر دو جواب داشته باشن از از اپراتور `zipWith` استفاده می‌کنیم. متود zipWith رو یک جریان عمل می‌کنه و به عنوان ورودی جریان دوم و تابعی رو دریافت می‌کنه بهش می‌گه چجوری دو تا جریان رو ترکیب کنه.

api.getAllPosts()
        .zipWith(api.getMyPosts(), BiFunction<List<Post>, List<Post>, List<Post>> { t1, t2 ->
            mutableListOf<Post>().apply {
                addAll(t1)
                addAll(t2)
            }
        })
        .subscribe({
            // We now have all the posts as a single result
        }, {
            // log error just in case
        })


۲- کنترل سرعت ارسال داده

مطمئنا پیش اومده که لازم بوده بر اساس یکی از رویدادهای مربوط به کاربر مثلا کلیک یا تایپ بخواید عملیاتی رو انجام بدید. برای مثال گاهی لازمه بر اساس حروفی که کاربر در یک فیلد تایپ می‌کنه به صورت لحظه‌ای درخواست‌هایی برای سرور بک‌اند شما ارسال بشه. در این مثال اگر کاربر خیلی سریع عملیات تایپ رو انجام بده ممکنه در کمتر از ثانیه تعداد زیادی درخواست به سمت سرور شما ارسال بشه که در تعداد کاربر بالا مسلما باعث فشار بیش از اندازه به سرور میشه.

حالا برای اینکه بتونیم این مشکل رو برطرف کنیم باید مثلا طوری پیاده سازی انجام بشه که فقط زمانی که از آخرین تایپ کاربر ۵۰۰ میلی‌ثانیه گذشت درخواست ارسال بشه.

در RxJava به راحتی با استفاده از اپراتور debounce می‌تونید این کار رو انجام بدید.

typingObservable.debounce(500, TimeUnit.MILLISECONDS)
.subscribe { 
    // 500 miliseconds is passed
}


۳- چطور جریان مخصوص خودمون رو درست کنیم

حالا شاید براتون سوال باشه که ما چطور میتونیم جریان (Observable) مخصوص خودمون رو درست کنیم. در اکثر موارد شما به اینکار نیاز پیدا نمی‌کنید و معمولا از observable هایی که به روشهای دیگه به دستتون رسیده استفاده می‌کنید ولی گاهی اوقات لازمه شما هم یک observable مخصوص خودتون داشته باشید.

برای مثال من زمانی که می‌خوام کار سنگینی رو روی thread پشت زمینه انجام بدم یک observable اختصاصی درست می‌کنم.

به این صورت:

Flowable.create<String>({ e ->
    // some heavy and long running operation
    // run on background thread
}, BackpressureStrategy.DROP)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
     .subscribe {
            // We are now on Main Thread
            // Do UI stuff
        }

دقت کنید که در تکه کد بالا به استفاده از متود subscribeOn(Schedulers.io()) انجام کار رو روی thread پس زمینه انجام دادیم و با استفاده از observeOn(AndroidSchedulers.mainThread()) نتایجش رو در ترد اصلی دریافت کردیم.


۴- میانبر subscribeOn و observeOn

بطور کلی خیلی پیش میاد که شما تو استفاده از RxJava بخواهید کاری رو در thread پس زمینه انجام بدید و نتیجش رو در ترد اصلی داشته باشید. و معمولا کدتون پر میشه از زنجیره زیر:

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

حالا برای اینکه ما از تکرار و ازدیاد دوری کنیم خیلی راحت می‌تونیم از یه قابلیت عالی زبان Kotlin استفاده کنیم به اسم Extentions به این صورت که تابعی به صورت زیر تعریف می‌کنیم.

fun <T> Single<T>.iomain(): Single<T> = this.compose {
    it.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
}

حالا فقط کافیه همیشه به جای

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

از

.iomain()

استفاده کنیم. به این صورت:

api.getAllPosts()
        .iomain()
        .subscribe({
            // We now have all the posts as a single result
        }, {
            // log error just in case
        })

تمام کدهایی که در این پست به کار رفته از طریق این گیتهاب در دسترسه. منتظر قسمت‌های بعدی این پست باشید و اگر فکر می‌کنید نکته جالبی رو بلدید که دوست دارید بقیه هم بدونن حتما تو بلاگ خودتون بنویسید یا به من بگید بنویسم.