4/5 (6)

سلام خدمت شما دوستان و همراهان سایت SmartDevelopers . تو این آموزش میخام در مورد ViewModel و LiveData صحبت کنم و شما رو با مفاهیم این ها و نحوه استفاده ازشون آشنا کنم .

 

آموزش ViewModel و LiveData در اندروید

ViewModel چیست ؟

کلاس ViewModel برای نگهداری و مدیریت داده های مرتبط با کامپوننت های UI بدون وابستگی به چرخه عمر این کامپوننت ها ساخته شده . این کلاس داده ها رو در طی تغییرات configuration ، مثل چرخش صفحه بدون از بین رفتن این داده های حفظ میکند.

چرخه عمر UI controller ها توسط فریمورک اندروید مدیریت میشه و گاهی ممکنه سیستم تصمیم بگیره که این کنترلر هارو براساس برخی شرایط یا در واکنش به عمل های کاربر ازبین ببره یا دوباره از نو بسازه ، برای مثال وقتی صفحه گوشی میچرخه ، سیستم اکتیویتی رو destroy میکنه و دوباره از اول میسازه که در این زمان متد onCreate دوباره فراخوانی میشه .

خب وقتی سیستم این UI Controller هارو از بین میبره یا دوباره از نو میسازه تمام اطلاعاتی که داخل این کنترلر ها تنظیم کرده بودید از بین میره . برای مثال ممکنه یکی از اکتیویتی های برنامتون شامل لیستی از User ها باشه که وقتی صفحه میچرخه و اکتیویتی دوباره از نو ساخته میشه برنامه مجبوره دوباره این لیست رو از اول دریافت کنه . برای داده های سبک میشه این مشکل رو با استفاده از متد onSaveInstanceState و بازگرداندن اطلاعات توسط bundle داخل متد onCreate  حل کرد . اما این روش برای اطلاعات سبک و کم حجم مفیده و برای اطلاعات سنگین تر مثل لیستی از bitmap ها یا user ها مفید نیست .

یکی دیگه از مشکلات هم اینه که گاهی اوقات این UI controller ها نیاز دارند که دستورات رو به صورت غیر همزمان اجرا کنند که این کار نیاز به گذراندن مدتی برای اجرای این دستورات داره ، که باید  مدیریت این کارها و همینطور مدیریت حافظه رو هم  خود UI controller برعهده بگیره !

برای حل این مشکلات کلاس ViewModel ساخته شده که وظیفه آماده کردن داده هارو برای UI controller ها برعهده بگیره . آبجکت های ViewModel در طی تغییرات configuration ، دچار تغییر نمیشن و داده ای رو که نگهداری میکنند بلافاصله بعد از اینکه UI controller هایی مثل اکتیویتی یا فرگمنت ها  دوباره به چرخه حیات برگشتن ، در اختیار این کنترلر ها قرار میدن . برای مثال اگر شما میخواهید لیستی از user ها رو داخل اکتیویتی به نمایش در بیارید بهتره که این لیست رو درون یک ViewModel قرار بدید تا خود اکتیویتی . 

خب اجازه بدید به طور عملی نحوه ایجاد یک ViewModel و ذخیره اطلاعات داخلش و دریافت اطلاعات ازش رو با هم ببینیم .

و برای دسترسی به لیست از داخل اکتیویتی به این صورت عمل میکنیم :

وقتی اکتیویتی دوباره از نو ساخته میشه همان MyViewModel ی رو دریافت میکنه که اولین بار موقع ساخته شدن اکتیویتی ایجاد شده بود . و قتی هم که اکتیویتی finish میشه فریمورک متد onCleared مربوط به ViewModel رو فراخوانی میکنه و باعث پاکسازی حافظه میشه . 

اگر داخل کلاس ViewModel مون نیاز به کلاس Application داشته باشیم به جای اکستند کردن از ViewModel میتونیم از AndroidViewModel اکستند کنیم که در متد سازندش یک شی از جنس Application دریافت میکنه . با استفاده از این کلاس Application میتونیم به شی context دسترسی داشته باشیم .

LiveData چیست ؟

کلاس LiveData یک نوع کلاس نگه دارنده داده ست که قابلیت observable ی داره . برخلاف Observable های رایج ، این کلاس از چرخه عمر UI controller ها باخبره و این  به این معناست که زمانی اطلاعات رو در اختیار UI controller ها قرار میده که آنها در حالت اکتیو خودشون باشند .

LiveData یک observer ی رو در نظر گرفته که زمانی در حالت اکتیو قرار میگیره که lifecycle ش در حالت STARTED یا RESUMED باشه . و LiveData هم فقط observer هایی رو از آپدیت شدن باخبر میکنه که در حالت اکتیو باشند . observer هایی که برای دریافت تغییرات LiveData رجیستر شدن اگر در حالت اکتیو نباشند ، از این تغییرات با خبر نمیشوند .

شما میتونید observer هارو به همراه object هایی که از اینترفیس LifecycleOwner مشتق شده اند ، رجیستر کنید . ارتباط بین این دو باعث میشه که وقتی state  متناظر با  آبجکت Lifecycle به حالت DESTROYED رفت ، observer  هم از لیست رجیستر شده ها حذف بشه . این امر مخصوصا برای اکتیویتی ها و فرگمنت ها خیلی مفیده که از نشت حافظه ( memory leak )جلوگیری میکنه .

مزایای استفاده از LiveData :

اطمینان از مطابقت UI شما با داده هایتان :

از آنجایی که LiveData از دیزاین پترن observer پیروی میکنه ، LiveData فقط زمانی که آبجکت lifecycle در حالت اکتیو باشه observer رو باخبر میکنه. پس شما به جای اینکه هروقت داده های اپ‌تون تغییر کرد بخواید UI خودتون رو آپیدت کنید ، داخل این observer ها این کار رو انجام بدید، با اینکار خود observer وقتی داده تغییر کردن UI رو آپدیت میکنه .

جلوگیری از نشت حافظه :

وقتی آبجکت lifecylce که با observer جفت شده به حالت DESTROYED بره ، observer متناظرش هم از حافظه پاک میشه.

جلوگیری از کرش برنامه وقتی اکتیویتی متوقف شده :

وقتی lifecycle مربوط به observer در حالت غیر اکتیو باشه دیگه متد onChanged ش فراخوانی نمیشه و عملکرد های داخل این متد که ممکنه روی آبجکت های null انجام بشن ، اجرا نمیشن

عدم نیاز به مدیریت چرخه عمر برای برنامه نویس :

خب این مورد رو هم بارها در موردش صحبت کردیم که خود LiveData این مورد رو بر عهده گرفته و نیاز به توضیح بیشتر نیست .

داده های همیشه به روز :

وقتی یک lifecycle غیر فعال میشه ( به حالت غیر اکتیو میره ) ،زمانی که دوباره به حالت اکتیو برگشت آخرین آپدیت رو دریافت خواهد کرد. برای مثال وقتی یک اکتیویتی از دید خارج شده زمانی که دوباره توی صفحه ظاهر میشه آخرین به روز رسانی داده رو دریافت میکنه.

مناسب برای تغیرات configuration  :

وقتی یک اکتیویتی یا فرگمنت در طی تغییرات configuration دوباره از نو ساخته میشوند  برای مثال چرخش صفحه ، بلافاصله آخرین داده های موجود رو دریافت میکنند .

چگونه از LiveData استفاده کنیم ؟

کلاس LiveData یک “ظرف داده” ست که میتونه انواع مختلف داده رو نگه داری کنه مثل List ها . آبجکت های LiveData معمولا داخل کلاس های ViewModel نگهداری میشن ، و با استفاده از متدهای Getter میشه بهشون دسترسی داشت . مثال زیر رو ببینید :

در ابتدا آبجکت liveData هیچ داده نداره . 

نکته : اگر با اکتیویتی یا فرگمنت کار میکنید حتما سعی کنید که آبجکت های LiveData را در کلاس ViewModel نگه داری کنید . دو دلیل داره :

    • اکتیویتی ها و فرگمت ها وظیفشون در این حالت فقط نمایش داده ها میشه و دیگه وظیفه نگهداری داده ها رو برعهده نمیگیرند .
    • جدا سازی آبجکت LiveData از فرگمنت و اکتیویتی . که باعث میشه LiveData به اکتیویتی یا فرگمنت وابستگی نداشه باشه و در خلال تغییرات Configuration داده های خودش رو حفظ کنه .

با خبر شدن از تغییرات آبجکت LiveData :

در بیشتر مواقع متد onCreate بهترین جا برای این کار است . خب دوتا دلیل داره :

  1. برای جلوگیری از فراخوانی های اضافه توسط سیستم در متد onResume 
  2. اطمینان از اینکه در کمترین زمان ممکنه اکتیوتی یا فرگمنت داده ای برای نمایش در اختیارشون هست .

به طور کلی LiveData فقط زمانی به روزرسانی ها رو ارسال میکنه که داده تغییر کرده باشه و همچنین  observer هم در حالت اکتیو باشه . اما یک استثنا وجود داره و اونم اینه که observer ها بروزرسانی ها رو زمانی که از حالت غیر اکتیو به حالت اکتیو در میان هم دریافت میکنند .  بنابراین observer ها اگه برای دومین بار از حالت غیر اکتیو به حالت اکتیو برن فقط زمانی بروزرسانی ها رو دریافت میکنند که داده شون تغییر کرده باشه  ( متد onChanged فقط در صورتی فراخوانی میشه که داده تغییر پیدا کرده باشه ) البته این موارد در مورد به روز رسانی ها بود نه تغییرات configuration ، اگه configuration تغییر کنه متد onChanged فراخوانی میشه و آخرین داده به این متد پاس میشه .

خب یک مثال عملی از LiveData برای با خبر شدن آپدیت ها ببینیم :

نکته : بعد از اینکه متد ()observe با پارامتر nameObserver ی که بهش پاس شده فراخوانی شد، متد ()onChanged بلافاصله با آخرین داده ای که در پارامتر currentName قرار داره فراخوانی میشه . و اگه currentName  داده ای نداشته باشه متد ()onChanged  فراخوانی نمیشه .
نکته : با توجه به نکته بالا اگه شما روی یک آبجکت LiveData برای مثال currentName متد observe رو صدا بزنید و اگه از قبل این currentName  داده ای تو خودش داشته باشه ، اول یک بار مقدار قبلی ای که توی این currentName وجود داشته به متد ()onChanged پاس میشه و این متد فراخوانی میشه و وقتی داده آپدیدت میشه هم یک بار دیگه متد ()onChanged فراخوانی میشه و این بار داده جدید بهش پاس میشه . و دلیل اینکه شما گاهی اوقات دوبار متد onChanged تون دوبار فراخوانی میشه همین موضوعه . 
نکته : برای instantiate کردن ViewModelProvider به این شکل و همچنین کارایی بهتر کلاس ViewModel باید dependency های مربوط به lifecycle رو در پروژه برنامه تون اضافه کنید . از این لینک میتونید این dependecy ها رو پیدا کنید .

آپدیت کردن داده ی آبجکت های LiveData :

خود کلاس LiveData متد public ی برای آپدیت کردن داده ها نداره . در عوض کلاس MutableLiveData که زیرمجموعه کلاس LiveData هست متد های  setValue و postValue رو برای این کار در اختیار ما میذاره . مثال زیر رو ببینید :

استفاده از متد setValue در این مثال باعث میشه که متد onChanged با مقدار جدید SmartDevelopers فراخوانی بشه . در این مثال متد setValue برای تغییر اسم استفاده شده اما این متد میتونه برای موارد کاربردی تری مثل واکنش به جوابی که از سرور دریافت شده یا اطلاعات لود شده از دیتابیس استفاده بشه .

نکته : از متد setValue  در Threadاصلی برنامه برای آپدیت کردن داده ها استفاده کنید . واگه میخواهید در Thread ی غیر از main Thread داده ها رو آپدیت کنید از متد postValue استفاده کنید .

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

 

 

به این مطلب چه امتیازی میدهید ؟

اشتراک
باخبر شدن از
guest

3 نظرات
قدیمی ترین
جدیدترین بیشترین امتیاز
Inline Feedbacks
نمایش همه دیدگاه ها
سارا

خیلی خوب .موفق یاشی

diako

ممنون بابت توضیحات خوبتون