סקירה כללית על LiveData חלק מ-Android Jetpack.
LiveData
היא סוג של מחזיק נתונים שניתן לצפות בו. בניגוד למשתנה רגיל שניתן לצפות בו, משתנה LiveData מודע למחזור החיים, כלומר הוא מתייחס למחזור החיים של רכיבים אחרים באפליקציה, כמו פעילויות, קטעים או שירותים. כך מוודאים ש-LiveData מעדכן רק משגיחים על רכיבי האפליקציה שנמצאים במצב פעיל במחזור החיים.
ב-LiveData, משתמש שמתבונן (observer) שמיוצג על ידי הכיתה Observer
נחשב כפעיל אם מחזור החיים שלו נמצא בסטטוס STARTED
או בסטטוס RESUMED
. LiveData מעדכן רק משתמשים פעילים על עדכונים. משתמשים לא פעילים שנרשמו למעקב אחרי אובייקטים מסוג LiveData
לא יקבלו התראות על שינויים.
אפשר לרשום משתמש שמתבונן (observer) עם אובייקט שמטמיע את הממשק LifecycleOwner
. הקשר הזה מאפשר להסיר את הצופה כשהסטטוס של האובייקט התואם Lifecycle
משתנה ל-DESTROYED
.
האפשרות הזו שימושית במיוחד לפעילויות ולקטעים, כי הם יכולים לצפות בעצמם באובייקטים מסוג LiveData
בלי לדאוג לדליפות – הפעילויות והקטעים מבטלים את ההרשמה באופן מיידי כשמחסלים את מחזורי החיים שלהם.
מידע נוסף על השימוש ב-LiveData זמין במאמר עבודה עם אובייקטים של LiveData.
היתרונות של השימוש ב-LiveData
היתרונות של שימוש ב-LiveData:
- מוודאים שממשק המשתמש תואם למצב הנתונים
- LiveData פועל לפי תבנית הצופה. כשהנתונים הבסיסיים משתנים, LiveData מעדכנת את האובייקטים מסוג
Observer
. אפשר לאחד את הקוד כדי לעדכן את ממשק המשתמש באובייקטים האלה שלObserver
. כך לא תצטרכו לעדכן את ממשק המשתמש בכל פעם שנתוני האפליקציה משתנים, כי הצופה יעשה זאת בשבילכם. - ללא דליפות זיכרון
- המעקבים מקושרים לאובייקטים מסוג
Lifecycle
, ומבצעים ניקוי אחרי עצמם כשמחזור החיים המשויך להם נהרס. - אין קריסות בגלל פעילויות שהופסקו
- אם מחזור החיים של הצופה לא פעיל, למשל במקרה של פעילות ב-back stack, הוא לא מקבל אירועי LiveData.
- אין יותר ניהול ידני של מחזור החיים
- רכיבי ממשק המשתמש רק צופים בנתונים הרלוונטיים ולא מפסיקים או ממשיכים את המעקב. LiveData מנהלת את כל זה באופן אוטומטי, כי היא מודעת לשינויים הרלוונטיים בסטטוס מחזור החיים במהלך המעקב.
- נתונים תמיד עדכניים
- אם מחזור חיים הופך ללא פעיל, הוא מקבל את הנתונים העדכניים כשהוא הופך שוב לפעיל. לדוגמה, פעילות שהיתה ברקע מקבלת את הנתונים העדכניים מיד אחרי שהיא חוזרת לחזית.
- שינויים מתאימים בהגדרות
- אם פעילות או קטע נוצרים מחדש בגלל שינוי בהגדרות, כמו רוטציה של המכשיר, הם מקבלים מיד את הנתונים העדכניים ביותר שזמינים.
- שיתוף משאבים
- אפשר להרחיב אובייקט
LiveData
באמצעות תבנית ה-singleton כדי לעטוף שירותי מערכת, כך שאפשר יהיה לשתף אותם באפליקציה. אובייקטLiveData
מתחבר לשירות המערכת פעם אחת, ואז כל משתמש שזקוק למשאב יכול פשוט לצפות באובייקטLiveData
. מידע נוסף זמין במאמר הרחבת LiveData.
עבודה עם אובייקטים של LiveData
כדי לעבוד עם אובייקטים מסוג LiveData
:
- יצירת מופע של
LiveData
לאחסון סוג מסוים של נתונים. בדרך כלל עושים זאת בכיתהViewModel
. - יוצרים אובייקט
Observer
שמגדיר את השיטהonChanged()
, שמאפשרת לקבוע מה יקרה כשהנתונים שמאוחסנים באובייקטLiveData
ישתנו. בדרך כלל יוצרים אובייקטObserver
בבורר של ממשק המשתמש, כמו פעילות או קטע. מחברים את האובייקט
Observer
לאובייקטLiveData
באמצעות השיטהobserve()
. השיטהobserve()
מקבלת אובייקטLifecycleOwner
. הפעולה הזו מביאה להרשמה של האובייקטObserver
לאובייקטLiveData
, כדי שיקבל התראות על שינויים. בדרך כלל מחברים את האובייקטObserver
למסוף UI, כמו פעילות או קטע.
כשמעדכנים את הערך שנשמר באובייקט LiveData
, הוא מפעיל את כל המשקיפים הרשומים כל עוד ה-LifecycleOwner
המצורף נמצא במצב פעיל.
LiveData מאפשרת למשתמשי UI controller להירשם לעדכונים. כשהנתונים שמאוחסנים באובייקט LiveData
משתנים, ממשק המשתמש מתעדכן באופן אוטומטי בתגובה.
יצירת אובייקטים של LiveData
LiveData הוא מעטפת שאפשר להשתמש בה עם כל נתונים, כולל אובייקטים שמטמיעים את Collections
, כמו List
. אובייקט LiveData
מאוחסן בדרך כלל בתוך אובייקט ViewModel
, והגישה אליו מתבצעת באמצעות method של getter, כפי שמתואר בדוגמה הבאה:
Kotlin
class NameViewModel : ViewModel() { // Create a LiveData with a String val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() } // Rest of the ViewModel... }
Java
public class NameViewModel extends ViewModel { // Create a LiveData with a String private MutableLiveData<String> currentName; public MutableLiveData<String> getCurrentName() { if (currentName == null) { currentName = new MutableLiveData<String>(); } return currentName; } // Rest of the ViewModel... }
בהתחלה, הנתונים באובייקט LiveData
לא מוגדרים.
מידע נוסף על היתרונות של השימוש בכיתה ViewModel
זמין במדריך ל-ViewModel.
מעקב אחרי אובייקטים של LiveData
ברוב המקרים, השיטה onCreate()
של רכיב האפליקציה היא המקום הנכון להתחיל את המעקב אחרי אובייקט LiveData
, מהסיבות הבאות:
- כדי לוודא שהמערכת לא מבצעת קריאות מיותרות מה-method
onResume()
של פעילות או של קטע. - כדי לוודא שלפעילות או לקטע יש נתונים שאפשר להציג ברגע שהם הופכים לפעילים. ברגע שרכיב באפליקציה נמצא בסטטוס
STARTED
, הוא מקבל את הערך העדכני ביותר מהאובייקטים מסוגLiveData
שהוא עוקב אחריהם. זה קורה רק אם אובייקטLiveData
שרוצים לעקוב אחריו הוגדר.
בדרך כלל, LiveData מעביר עדכונים רק כשהנתונים משתנים, ורק למשתמשי מעקב פעילים. חריג להתנהגות הזו הוא שגם משתמשים צופים מקבלים עדכון כשהם עוברים מסטטוס לא פעיל לסטטוס פעיל. בנוסף, אם המשתנה של הצופה עובר מ'לא פעיל' ל'פעיל' בפעם השנייה, הוא מקבל עדכון רק אם הערך השתנה מאז הפעם האחרונה שהוא הפך לפעיל.
דוגמת הקוד הבאה מראה איך מתחילים לעקוב אחרי אובייקט LiveData
:
Kotlin
class NameActivity : AppCompatActivity() { // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact private val model: NameViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Other code to setup the activity... // Create the observer which updates the UI. val nameObserver = Observer<String> { newName -> // Update the UI, in this case, a TextView. nameTextView.text = newName } // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer. model.currentName.observe(this, nameObserver) } }
Java
public class NameActivity extends AppCompatActivity { private NameViewModel model; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Other code to setup the activity... // Get the ViewModel. model = new ViewModelProvider(this).get(NameViewModel.class); // Create the observer which updates the UI. final Observer<String> nameObserver = new Observer<String>() { @Override public void onChanged(@Nullable final String newName) { // Update the UI, in this case, a TextView. nameTextView.setText(newName); } }; // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer. model.getCurrentName().observe(this, nameObserver); } }
אחרי שמפעילים את observe()
עם הערך nameObserver
שמוענק כפרמטר, onChanged()
מופעל באופן מיידי ומספק את הערך האחרון שנשמר ב-mCurrentName
.
אם לא הוגדר ערך באובייקט LiveData
בשדה mCurrentName
, לא תתבצע קריאה ל-onChanged()
.
עדכון אובייקטים של LiveData
ל-LiveData אין שיטות זמינות לכולם לעדכון הנתונים השמורים. בכיתה MutableLiveData
מוצגים באופן ציבורי השיטות setValue(T)
ו-postValue(T)
, וצריך להשתמש בהן אם רוצים לערוך את הערך שמאוחסן באובייקט LiveData
. בדרך כלל משתמשים ב-MutableLiveData
ב-ViewModel
, ואז ה-ViewModel
חושף למתבוננים רק אובייקטים LiveData
שלא ניתן לשנות אותם.
אחרי שמגדירים את קשר הצופה, אפשר לעדכן את הערך של האובייקט LiveData
, כפי שמתואר בדוגמה הבאה, שמפעילה את כל הצופים כשהמשתמש מקשיב על לחצן:
Kotlin
button.setOnClickListener { val anotherName = "John Doe" model.currentName.setValue(anotherName) }
Java
button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String anotherName = "John Doe"; model.getCurrentName().setValue(anotherName); } });
קריאה ל-setValue(T)
בדוגמה הזו גורמת למשתמשי הצפייה להפעיל את השיטות שלהם onChanged()
עם הערך John Doe
. בדוגמה מוצגת לחיצה על לחצן, אבל אפשר להפעיל את setValue()
או את postValue()
כדי לעדכן את mName
מסיבות שונות, כולל בתגובה לבקשת רשת או להשלמת טעינת מסד נתונים. בכל המקרים, הקריאה ל-setValue()
או ל-postValue()
מפעילה את המשגיחים ומעדכנת את ממשק המשתמש.
שימוש ב-LiveData עם Room
ספריית העקביות של Room תומכת בשאילתות שניתן לצפות בהן, שמחזירות אובייקטים מסוג LiveData
.
שאילתות שניתן לצפות בהן נכתבות כחלק מאובייקט גישה למסד נתונים (DAO).
כשמתעדכנת מסד נתונים, Room יוצר את כל הקוד הנדרש כדי לעדכן את האובייקט LiveData
. הקוד שנוצר מפעיל את השאילתה באופן אסינכרוני בשרשור רקע, לפי הצורך. התבנית הזו שימושית כדי לשמור על סנכרון בין הנתונים שמוצגים בממשק המשתמש לבין הנתונים שמאוחסנים במסד נתונים. מידע נוסף על Room ו-DAO זמין במדריך לספרייה הקבועה של Room.
שימוש ב-coroutines עם LiveData
LiveData
כולל תמיכה ב-coroutines של Kotlin. למידע נוסף, ראו שימוש ב-coroutines של Kotlin עם רכיבי הארכיטקטורה של Android.
שימוש ב-LiveData בארכיטקטורה של אפליקציה
LiveData
מודע למחזור החיים, ועוקב אחרי מחזור החיים של ישויות כמו פעילויות ופרטי מידע. משתמשים ב-LiveData
כדי לתקשר בין בעלי מחזור החיים האלה לבין אובייקטים אחרים עם משך חיים שונה, כמו אובייקטים מסוג ViewModel
.
התפקיד העיקרי של ViewModel
הוא לטעון ולנהל נתונים שקשורים לממשק המשתמש, ולכן הוא מתאים מאוד לאחסון אובייקטים מסוג LiveData
. יוצרים אובייקטים מסוג LiveData
ב-ViewModel
ומשתמשים בהם כדי לחשוף את המצב לשכבת ממשק המשתמש.
אסור לאחסן בפעילויות ובקטעים עותקים של LiveData
, כי התפקיד שלהם הוא להציג נתונים, ולא לאחסן מצב. בנוסף, כשפעילות ופרפאג'טים לא שומרים נתונים, קל יותר לכתוב בדיקות יחידה.
יכול להיות שתתפתתו לעבוד עם אובייקטים מסוג LiveData
בכיתה של שכבת הנתונים, אבל LiveData
לא תוכנן לטיפול בזרמים אסינכרונים של נתונים. אפשר להשתמש בטרנספורמציות של LiveData
וב-MediatorLiveData
כדי להשיג את זה, אבל יש לשיטה הזו חסרונות: היכולת לשלב בין מקורות נתונים מוגבלת מאוד וכל האובייקטים מסוג LiveData
(כולל אלה שנוצרו באמצעות טרנספורמציות) נצפים בשרשור הראשי. הקוד שבהמשך הוא דוגמה לאופן שבו החזקה של LiveData
ב-Repository
יכולה לחסום את הליבה:
Kotlin
class UserRepository { // DON'T DO THIS! LiveData objects should not live in the repository. fun getUsers(): LiveData<List<User>> { ... } fun getNewPremiumUsers(): LiveData<List<User>> { return getUsers().map { users -> // This is an expensive call being made on the main thread and may // cause noticeable jank in the UI! users .filter { user -> user.isPremium } .filter { user -> val lastSyncedTime = dao.getLastSyncedTime() user.timeCreated > lastSyncedTime } } }
Java
class UserRepository { // DON'T DO THIS! LiveData objects should not live in the repository. LiveData<List<User>> getUsers() { ... } LiveData<List<User>> getNewPremiumUsers() { return Transformations.map(getUsers(), // This is an expensive call being made on the main thread and may cause // noticeable jank in the UI! users -> users.stream() .filter(User::isPremium) .filter(user -> user.getTimeCreated() > dao.getLastSyncedTime()) .collect(Collectors.toList())); } }
אם אתם צריכים להשתמש בזרמי נתונים בשכבות אחרות של האפליקציה, מומלץ להשתמש ב-Kotlin Flows ולאחר מכן להמיר אותם ל-LiveData
ב-ViewModel
באמצעות asLiveData()
.
בקודלאב הזה מוסבר איך משתמשים ב-Kotlin Flow
עם LiveData
.
בקוד בסיס שנוצר ב-Java, מומלץ להשתמש ב-Executors בשילוב עם פונקציות קריאה חוזרת או RxJava
.
הרחבת LiveData
מערכת LiveData מתייחסת למשתמש שמתבונן כפעיל אם מחזור החיים שלו נמצא באחד מהמצבים STARTED
או RESUMED
. הקוד לדוגמה הבא מראה איך להרחיב את הכיתה LiveData
:
Kotlin
class StockLiveData(symbol: String) : LiveData<BigDecimal>() { private val stockManager = StockManager(symbol) private val listener = { price: BigDecimal -> value = price } override fun onActive() { stockManager.requestPriceUpdates(listener) } override fun onInactive() { stockManager.removeUpdates(listener) } }
Java
public class StockLiveData extends LiveData<BigDecimal> { private StockManager stockManager; private SimplePriceListener listener = new SimplePriceListener() { @Override public void onPriceChanged(BigDecimal price) { setValue(price); } }; public StockLiveData(String symbol) { stockManager = new StockManager(symbol); } @Override protected void onActive() { stockManager.requestPriceUpdates(listener); } @Override protected void onInactive() { stockManager.removeUpdates(listener); } }
ההטמעה של מאזין המחירים בדוגמה הזו כוללת את השיטות החשובות הבאות:
- השיטה
onActive()
נקראת כשיש למשתנהLiveData
משתמש פעיל. כלומר, צריך להתחיל לעקוב אחרי עדכוני מחירי המניות באמצעות השיטה הזו. - השיטה
onInactive()
נקראת כשלאובייקטLiveData
אין משקיפים פעילים. מכיוון שאין משתמשים נוספים שמאזינים, אין סיבה להישאר מחוברים לשירותStockManager
. - השיטה
setValue(T)
מעדכנת את הערך של מופעLiveData
ומודיעה על השינוי לכל המשקיפים הפעילים.
אפשר להשתמש בכיתה StockLiveData
באופן הבא:
Kotlin
public class MyFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val myPriceListener: LiveData<BigDecimal> = ... myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? -> // Update the UI. }) } }
Java
public class MyFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); LiveData<BigDecimal> myPriceListener = ...; myPriceListener.observe(getViewLifecycleOwner(), price -> { // Update the UI. }); } }
השיטה observe()
מעבירה את LifecycleOwner
שמשויך לתצוגה של החלק כארגומנטים הראשון. הפעולה הזו מציינת שהצופה הזה קשור לאובייקט Lifecycle
שמשויך לבעלים, כלומר:
- אם האובייקט
Lifecycle
לא במצב פעיל, לא מתבצעת קריאה למעקב גם אם הערך משתנה. - אחרי שהאובייקט
Lifecycle
נהרס, המתבונן מוסר באופן אוטומטי.
העובדה שאובייקטים מסוג LiveData
מותאמים למחזור החיים שלהם מאפשרת לשתף אותם בין כמה פעילויות, קטעים ושירותים. כדי שהדוגמה תהיה פשוטה, אפשר להטמיע את הכיתה LiveData
כ-singleton באופן הבא:
Kotlin
class StockLiveData(symbol: String) : LiveData<BigDecimal>() { private val stockManager: StockManager = StockManager(symbol) private val listener = { price: BigDecimal -> value = price } override fun onActive() { stockManager.requestPriceUpdates(listener) } override fun onInactive() { stockManager.removeUpdates(listener) } companion object { private lateinit var sInstance: StockLiveData @MainThread fun get(symbol: String): StockLiveData { sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol) return sInstance } } }
Java
public class StockLiveData extends LiveData<BigDecimal> { private static StockLiveData sInstance; private StockManager stockManager; private SimplePriceListener listener = new SimplePriceListener() { @Override public void onPriceChanged(BigDecimal price) { setValue(price); } }; @MainThread public static StockLiveData get(String symbol) { if (sInstance == null) { sInstance = new StockLiveData(symbol); } return sInstance; } private StockLiveData(String symbol) { stockManager = new StockManager(symbol); } @Override protected void onActive() { stockManager.requestPriceUpdates(listener); } @Override protected void onInactive() { stockManager.removeUpdates(listener); } }
אפשר להשתמש בו בקוד הבא:
Kotlin
class MyFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? -> // Update the UI. }) }
Java
public class MyFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> { // Update the UI. }); } }
מספר קטעים ופעילויות יכולים לצפות במכונה MyPriceListener
.
LiveData מתחבר לשירות המערכת רק אם אחד או יותר מהם גלוי ופעיל.
טרנספורמציה של LiveData
יכול להיות שתרצו לבצע שינויים בערך שמאוחסן באובייקט LiveData
לפני ששולחים אותו למתבוננים, או שתצטרכו להחזיר מופע LiveData
אחר על סמך הערך של מופע אחר. החבילה Lifecycle
כוללת את הכיתה Transformations
, שכוללת שיטות עזר שתומכות בתרחישים האלה.
Transformations.map()
- מחילה פונקציה על הערך שמאוחסן באובייקט
LiveData
ומפיצה את התוצאה במורד הזרם.
Kotlin
val userLiveData: LiveData<User> = UserLiveData() val userName: LiveData<String> = userLiveData.map { user -> "${user.name} ${user.lastName}" }
Java
LiveData<User> userLiveData = ...; LiveData<String> userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName });
Transformations.switchMap()
- בדומה ל-
map()
, מחילה פונקציה על הערך שמאוחסן באובייקטLiveData
, פותחת את האריזה של התוצאה ושולחת אותה במורד הזרם. הפונקציה שמעבירים ל-switchMap()
חייבת להחזיר אובייקטLiveData
, כפי שמתואר בדוגמה הבאה:
Kotlin
private fun getUser(id: String): LiveData<User> { ... } val userId: LiveData<String> = ... val user = userId.switchMap { id -> getUser(id) }
Java
private LiveData<User> getUser(String id) { ...; } LiveData<String> userId = ...; LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
אפשר להשתמש בשיטות טרנספורמציה כדי לשאת מידע במהלך מחזור החיים של הצופה. הטרנספורמציות לא מחושבות אלא אם משתמש צופה באובייקט LiveData
המוחזר. מאחר שהטרנספורמציות מחושבות באופן עצל, ההתנהגות שקשורה למחזור החיים מועברת באופן משתמע בלי צורך בקריאות או בתלות נוספות באופן מפורש.
אם אתם חושבים שאתם צריכים אובייקט Lifecycle
בתוך אובייקט ViewModel
, כנראה שתהליך טרנספורמציה הוא פתרון טוב יותר. לדוגמה, נניח שיש לכם רכיב UI שמקבל כתובת ומחזיר את המיקוד של אותה כתובת. אפשר להטמיע את ViewModel
הפשוט לרכיב הזה, כפי שמתואר בדוגמת הקוד הבאה:
Kotlin
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() { private fun getPostalCode(address: String): LiveData<String> { // DON'T DO THIS return repository.getPostCode(address) } }
Java
class MyViewModel extends ViewModel { private final PostalCodeRepository repository; public MyViewModel(PostalCodeRepository repository) { this.repository = repository; } private LiveData<String> getPostalCode(String address) { // DON'T DO THIS return repository.getPostCode(address); } }
לאחר מכן, רכיב ממשק המשתמש צריך לבטל את הרישום מהאובייקט הקודם של LiveData
ולהירשם למכונה החדשה בכל פעם שהוא קורא ל-getPostalCode()
. בנוסף, אם רכיב ממשק המשתמש נוצר מחדש, הוא מפעיל קריאה נוספת ל-method repository.getPostCode()
במקום להשתמש בתוצאה של הקריאה הקודמת.
במקום זאת, אפשר להטמיע את בדיקת המיקוד כטרנספורמציה של קלט הכתובת, כפי שמוצג בדוגמה הבאה:
Kotlin
class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() { private val addressInput = MutableLiveData<String>() val postalCode: LiveData<String> = addressInput.switchMap { address -> repository.getPostCode(address) } private fun setInput(address: String) { addressInput.value = address } }
Java
class MyViewModel extends ViewModel { private final PostalCodeRepository repository; private final MutableLiveData<String> addressInput = new MutableLiveData(); public final LiveData<String> postalCode = Transformations.switchMap(addressInput, (address) -> { return repository.getPostCode(address); }); public MyViewModel(PostalCodeRepository repository) { this.repository = repository } private void setInput(String address) { addressInput.setValue(address); } }
במקרה כזה, השדה postalCode
מוגדר כטרנספורמציה של השדה addressInput
. כל עוד יש באפליקציה משתמש פעיל שמשויך לשדה postalCode
, הערך של השדה מחושב מחדש ומאוחזר בכל פעם ש-addressInput
משתנה.
המנגנון הזה מאפשר לרמות נמוכות יותר באפליקציה ליצור אובייקטים מסוג LiveData
שמחושבים באיטרציה על פי דרישה. אובייקט ViewModel
יכול לקבל בקלות הפניות לאובייקטים מסוג LiveData
, ולאחר מכן להגדיר כללי טרנספורמציה מעליהם.
יצירת טרנספורמציות חדשות
יש כמה עשרות טרנספורמציות ספציפיות שיכולות להיות שימושיות באפליקציה, אבל הן לא זמינות כברירת מחדל. כדי להטמיע טרנספורמציה משלכם, תוכלו להשתמש בכיתה MediatorLiveData
, שמקשיבה לאובייקטים אחרים מסוג LiveData
ומעבדת אירועים שהם פולטים. MediatorLiveData
מעביר את המצב שלו בצורה נכונה לאובייקט המקור LiveData
. מידע נוסף על התבנית הזו זמין במסמכי העזרה של הכיתה Transformations
.
מיזוג של כמה מקורות של LiveData
MediatorLiveData
הוא תת-סוג של LiveData
שמאפשר למזג כמה מקורות של LiveData. לאחר מכן, משתמשי הצפייה באובייקטים מסוג MediatorLiveData
מופעלים בכל פעם שאחד מהאובייקטים המקוריים של מקור הנתונים של LiveData משתנה.
לדוגמה, אם יש לכם אובייקט LiveData
בממשק המשתמש שאפשר לעדכן ממסד נתונים מקומי או מרשת, תוכלו להוסיף את המקורות הבאים לאובייקט MediatorLiveData
:
- אובייקט
LiveData
שמשויך לנתונים שמאוחסנים במסד הנתונים. - אובייקט
LiveData
שמשויך לנתונים שאליהם מתבצעת גישה מהרשת.
הפעילות שלכם צריכה רק לצפות באובייקט MediatorLiveData
כדי לקבל עדכונים משני המקורות. דוגמה מפורטת מופיעה בקטע נספח: חשיפת סטטוס הרשת במדריך לארכיטקטורת אפליקציות.
מקורות מידע נוספים
למידע נוסף על הכיתה LiveData
, אפשר לעיין במקורות המידע הבאים.
דוגמיות
- Sunflower, אפליקציית הדגמה שממחישה שיטות מומלצות לשימוש ברכיבי ארכיטקטורה
Codelabs
- Android Room with a View (Java) (Kotlin)
- שימוש מתקדם ב-coroutines באמצעות Kotlin Flow ו-LiveData
בלוגים
- ViewModels ו-LiveData: דפוסים ואנטי-דפוסים
- שימוש ב-LiveData מעבר ל-ViewModel — תבניות רספונסיביות באמצעות Transformations ו-MediatorLiveData
- LiveData עם SnackBar, ניווט ואירועים אחרים (המקרה של SingleLiveEvent)
סרטונים
מומלץ עבורך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- שימוש ב-coroutines של Kotlin עם רכיבים מודעים למחזור חיים
- טיפול במחזורי חיים באמצעות רכיבים מודעים למחזור החיים
- בדיקת ההטמעה של דפים