جایگزین کردن AsyncTask با rxjava به منظور پیاده سازی multithreading

آموزش multithreading در rxjava

یکی از راه های پیاده سازی multithreading در اندروید استفاده از کلاس AsyncTask است. اگه تو اپتون از چند تا AsyncTask استفاده کرده باشین که همزمان با هم execute بشن همگی به صورت سریال و پشت سر هم اجرا میشن که این خیلی بده. برای اینکه چند تا thread بک گراند به صورت موازی کار کنن میتونین از rxjava استفاده کنین. پیاده سازی multithreading در rxjava خیلی راحت است. پس از خواندن این مقاله شما میتونید جواب سوال های زیر رو بدونید.

  • چطوری multithreading رو با استفاده از rxjava انجام بدیم؟
  • چطوری به جای AsyncTask از rxjava استفاده کنیم؟
  • چطوری با استفاده از rxjava یه کاری رو در ترد بک گراند و یه کاری رو در ترد اصلی اپ انجام بدیم؟

مفاهیم مقدماتی برای شروع کار با rxjava رو قبلا تو مقاله آموزش مقدماتی RxJava در اندروید گفتم. اگه با مفاهیم مقدماتی آشنا نیستین میتونین اول اون مقاله رو بخونید.

اضافه کردن dependency های مورد نیاز

برای استفاده از rxjava در اندروید اول باید dependency های مربوطه رو به پروژه اضافه کنین. برای اینکار کدهای زیر رو به فایل build.gradle ماژول اپ پروژتون اضافه کنین. موقع نوشتن این مقاله آخرین ورژن برای کتابخانه ها همان هایی هست که نوشتم، ممکنه شما لازم باشه ورژن بالاتری رو بنویسین. درصورتی که همین کدها رو استفاده کنین، خود اندروید استودیو اگه آپدیتی برای کتابخانه اومده باشه بهتون میگه و میتونین ورژن بالاتر رو بنویسین.

استفاده از AsyncTask برای پیاده سازی multithreading

من یه fragment درست کردم که در متد onViewCreated با استفاده از AsyncTask یه api کال انجام میده و دیتایی رو به صورت json میگیره و بعد از پارس کردن json اطلاعاتی رو در textview نشون میده. Api کالی که داخل doInBackground انجام شده با استفاده از کتابخانه OkHttp انجام شده. دقت کنید که برای کارهای تستی از این کتابخانه استفاده میکنم ولی در پروژه های واقعی از کتابخانه retrofit استفاده میکنم. در اینجا هدف ما یادگیری rxjava هست بنابراین اینکه api کال با استفاده از چه کتابخانه ای انجام بشه برامون اهمیتی نداره.

 

همانطور که در مقاله آموزش RxJava یا برنامه نویسی reactive در اندروید گفتم یکی از کاربردهای rxjava پیاده سازی multithreading هست. یعنی ما به جای اینکه از روشهایی مثل استفاده از AsyncTask استفاده کنیم میتونیم با rxjava دیتا رو با استفاده از thread بک گراند از یه api بخونیم و در ترد main از دیتای گرفته شده استفاده کنیم.در ادامه میخوام یاد بدم که چطوری این Api کالی که در مثال بالا با استفاده از AsyncTask انجام شده رو با rxjava جایگزین کنیم.

ممکنه شما بگین چه کاریه از همون AsyncTask استفاده میکنم. در جواب این سوال باید بگم که اگه تو اپتون از چند تا AsyncTask استفاده کرده باشید و همزمان با هم execute بشن همگی به صورت پشت سر هم و سریال اجرا میشن که این خیلی بده. خیلی وقت ها ما نیاز داریم که چند تا thread بک گراند با هم به صورت موازی کار کنن که این مورد با استفاده از rxjava به راحتی قابل پیاده سازی است.

استفاده از rxjava به جای AsyncTask

برای اینکه به جای AsyncTask از rxjava استفاده کنم اون قسمت کد که مربوط به صدا زدن api بود رو داخل یه متد به اسم getGist بردم که در کد زیر نشون داده شده.

استفاده از  Observable.just

یه متد درست کردم به اسم getGistObservable که Gist رو به صورت Observable برمیگردونه. راحت ترین روش برای اینکه یه چیزی رو به صورت observable برگردونیم استفاده از متد Observable.just است. متد just صرف نظر از اینکه ورودیش چه نوعی باشه اونو به صورت observable برمیگردونه. یعنی هر چی بهش پاس بدیم اونو به صورت Observable برمیگردونه. الان تو این مثال من خروجی getGist رو بهش پاس دادم. تا اینجای کار متد getGistObservable به صورت کد زیر شد.

استفاده از Observable.deffer

تا اینجای کار یه مشکلی وجود داره و اونم این هست که متد Observable.just به محض اینکه صدا زده میشه اجرا میشه و میره سراغ آماده کردن observable. این مساله باعث میشه که api کال در ترد main انجام بشه و در نتیجه ui برنامه بلاک بشه. در حالیکه ما همچین چیزی رو نمیخوایم و میخوایم صدا زدن api در ترد بک گراند انجام بشه. برای اینکار من لازم دارم که متد Observable.just اجراش به تعویق بیفته تا زمانیکه subscribe انجام بشه. یعنی تا زمانیکه بهش subscribe نکردیم نره سراغ آماده کردن observable. برای اینکار راه های زیادی وجود داره که در این مثال من از متد Observable.difer استفاده میکنم. کلمه انگلیسی differ یعنی به تعویق انداختن. متد differ به عنوان ورودی یه interface میگیره در نتیجه من کلاس anonymous به عنوان ورودی بهش دادم. تا اینجای کار متد getGistObservable به صورت کد زیر شد.

subscribe کردن به یک observable

حالا میتونیم به Observable ای که به عنوان خروجی توسط متد getGistObservable برمیگرده subscribe کنیم. برای اینکار کد زیر رو نوشتم.

یه subscriber به متد subscribe پاس دادم. Subscriber سه تا متد داره:

  • onCompleted
  • onError
  • onNext

وقتی اروری اتفاق بیفته متد onError صدا زده میشه. یه سری از observable ها هستن که بیشتر از یه آیتم خروجی رو برمیگردونن این موقع ها به ازای هر آیتم خروحی onNext اجرا میشه و وقتی همه آیتم های خروجی برگردونده شدن متد onCompleted صدا زده میشه. در مثال ما خروجی متد getGist به عنوان آیتم observable برگردونده میشه که یه آیتم بیشتر نیست. در نتیجه بلافاصله بعد از onNext متد onCompleted صدا زده میشه. کدی که تو متد onPostExecute نوشته شده بود رو به متد onNext منتقل کردم و AsyncTask رو پاک کردم چون دیگه نیازی بهش نداریم.

استفاده از Schedulers در rxjava یا به عبارت دیگه multithreading در rxjava

یه نکته مهم این هست که به صورت پیش فرض وقتی ما به یه Observable ساب اسکرایب میکنیم. همه کارها در ترد main انجام میشه مگه اینکه خودمون بهش بگیم. موقعی که به یه observable ساب اسکرایب میکنیم میتونیم بگیم که subscribe در ترد بک گراند انجام بشه و observe در ترد main انجام بشه. منظور از subscribe کاری است که observable برای تولید خروجی انجام میده که در این مثال میشه صدا زدن api. منظور از observe هم کاری است که در متدهای onNext و  onCompleted و onError انجام میشه که در این مثال میشه گرفتن gist و نمایش اطلاعات در TextView.

برای اینکه ترد مورد نظر رو مشخص کنیم از schedulers استفاده میکنیم. برای اینکه بگیم subscribe در چه تردی انجام بشه از متد subscribeOn استفاده میکنیم و برای اینکه بگیم observer در چه تردی انجام بشه از متد observeOn استفاده میکنیم. کد زیر کامل شده آنچه در متد onviewCreated نوشته شده رو نشون میده.

همان طور که در کد مشخص است گفتیم آماده سازی دیتای observable در ترد بک گراند (Schedulers.io) انجام بشه و پردازش داده برگردانده شده در ترد  main یا اصلی (AndroidSchedulers.mainThread) انجام بشه. اگه همچین کدی رو بنویسین و اجرا کنین میبینین که دیتا از API گرفته میشه و در TextViewنمایش داده میشه.

 جلوگیری از memory leak هنگام استفاده از rxjava

کدی که تا اینجا نوشتیم یه مشکلی داره و اون هم این هست که باعث memory leak میشه. Memory leak موقعی اتفاق میوفته که از اکتیویتی یا fragment خارج بشیم ولی همچنان یه ترد بک گراند در حال اجرا وجود داشته باشه که به ui نیاز داشته باشه، در نتیجه ui ها همچنان در حافظه باقی میمانند و باعث memory lead میشن.

الان توی این کد observable تا زمانیکه که دیتا آماده بشه Textview ای که قراره خروجی رو نشون بده نگه میداره در نتیجه اگه از فرگمنت خارج بشیم memory leak اتفاق میوفته. برای حل این مشکل باید بگیم که وقتی از فرگمنت خارج شدیم دیگه نیازی نداریم که دیتای برگردانده شده توسط observable رو استفاده کنیم در نتیجه میخوایم unsubscribe کنیم. متد subscribe خروجی اش از نوع کلاس subscription است که از آن میشه برای unsubscribe کردن استفاده کرد. موقعی که فرگمنت یا اکتیویتی داره destroy میشه چک میکنیم که اگه هنوز به observable ساب اسکرایب هستیم unsubscribe کنیم. کد فرگمنتی که تا الان داشتیم روش کار میکردیم به صورت زیر درمیاد. این کد دیگه memory leak نداره.

 

هر سوالی که در رابطه با multithreading در rxjava داشتید تو کامنت بپرسید، من پاسخگوی سوالاتتون هستم.

درباره نویسنده

پست های مرتبط

5 نظر

پاسخ

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

نوزده − 18 =