אפליקציות Firebase פועלות גם אם האפליקציה מאבדת זמנית את החיבור לרשת. בנוסף, Firebase מספקת כלים לאחסון נתונים באופן מקומי, לניהול נוכחות אונליין ולטיפול בזמן טעינה.
התמדה של דיסקים
באפליקציות Firebase, הפרעות זמניות ברשת מטופלות באופן אוטומטי. הנתונים ששמורים במטמון זמינים במצב אופליין, ו-Firebase שולח מחדש את כל הפעולות של כתיבת נתונים כשהחיבור לרשת משוחזר.
כשמפעילים את ההגדרה 'שמירת נתונים בדיסק', האפליקציה כותבת את הנתונים באופן מקומי למכשיר, כך שהאפליקציה יכולה לשמור את המצב שלה גם כשהיא במצב אופליין, גם אם המשתמש או מערכת ההפעלה מפעילים מחדש את האפליקציה.
אפשר להפעיל את התכונה 'התמדה של דיסקים' באמצעות שורת קוד אחת בלבד.
FirebaseDatabase.instance.setPersistenceEnabled(true);
התנהגות של התמדה
הפעלת העקביות מאפשרת לנתונים שהלקוח של מסד נתונים בזמן אמת ב-Firebase מסנכרן כשהוא במצב אונליין להישמר בדיסק ולהיות זמינים במצב אופליין, גם אם המשתמש או מערכת ההפעלה מפעילים מחדש את האפליקציה. כלומר, האפליקציה פועלת כמו שהיא פועלת במצב אונליין, באמצעות הנתונים המקומיים שמאוחסנים במטמון. התקשרות חוזרת (callback) של מאזינים תמשיך לפעול לעדכונים מקומיים.
הלקוח של מסד נתונים בזמן אמת ב-Firebase שומר באופן אוטומטי תור של כל פעולות הכתיבה שמבוצעות בזמן שהאפליקציה במצב אופליין. כשהתכונה 'התמדה' מופעלת, התור הזה נשמר גם בדיסק, כך שכל הפעולות של הכתיבה זמינות כשהמשתמש או מערכת ההפעלה מפעילים מחדש את האפליקציה. כשהאפליקציה מתחברת מחדש, כל הפעולות נשלחות לשרת של מסד נתונים בזמן אמת ב-Firebase.
אם האפליקציה שלכם משתמשת באימות ב-Firebase, לקוח מסד נתונים בזמן אמת ב-Firebase שומר את טוקן האימות של המשתמש גם אחרי הפעלה מחדש של האפליקציה. אם תוקף אסימון ההרשאה פג בזמן שהאפליקציה במצב אופליין, הלקוח משהה את פעולות הכתיבה עד שהאפליקציה מאמתת מחדש את המשתמש. אחרת, פעולות הכתיבה עלולות להיכשל בגלל כללי האבטחה.
שמירה על עדכניות הנתונים
מסד הנתונים בזמן אמת ב-Firebase מסנכרן ומאחסן עותק מקומי של הנתונים עבור מאזינים פעילים. בנוסף, אתם יכולים לסנכרן מיקומים ספציפיים.
final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.keepSynced(true);
הלקוח של מסד הנתונים בזמן אמת ב-Firebase מוריד את הנתונים באופן אוטומטי במיקומים האלה ושומר על סנכרון שלהם גם אם להפניה אין מאזינים פעילים. אפשר להשבית את הסנכרון באמצעות שורת הקוד הבאה.
scoresRef.keepSynced(false);
כברירת מחדל, נשמרים במטמון 10MB של נתונים שסונכרנו בעבר. ההגדרה הזו אמורה להספיק לרוב האפליקציות. אם המטמון גדל מעבר לגודל שהוגדר לו, מסד נתונים בזמן אמת ב-Firebase מוחק נתונים שהשימוש בהם היה הכי מזמן. נתונים שנשמרים בסנכרון לא נמחקים מהמטמון.
שליחת שאילתות לנתונים ממקורות אופליין
מסד הנתונים בזמן אמת ב-Firebase מאחסן נתונים שמוחזרים משאילתה לשימוש במצב אופליין. לגבי שאילתות שנוצרו במצב אופליין, מסד הנתונים בזמן אמת ב-Firebase ממשיך לפעול עבור נתונים שנפרסו בעבר. אם הנתונים המבוקשים לא נטענו, מסד הנתונים בזמן אמת ב-Firebase טוען נתונים מהמטמון המקומי. כשהחיבור לרשת יחזור, הנתונים ייטענו ויוצגו בהתאם לשאילתה.
לדוגמה, הקוד הזה שולח שאילתה לגבי ארבעת הפריטים האחרונים במסד נתונים של ציונים:
final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.orderByValue().limitToLast(4).onChildAdded.listen((event) {
debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});
נניח שהחיבור של המשתמש נותק, הוא עבר למצב אופליין והפעיל מחדש את האפליקציה. בזמן שהוא עדיין במצב אופליין, האפליקציה שולחת שאילתה לגבי שני הפריטים האחרונים מאותו מיקום. השאילתה הזו תחזיר בהצלחה את שני הפריטים האחרונים כי האפליקציה טענה את כל ארבעת הפריטים בשאילתה שלמעלה.
scoresRef.orderByValue().limitToLast(2).onChildAdded.listen((event) {
debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});
בדוגמה הקודמת, הלקוח של מסד הנתונים בזמן אמת ב-Firebase מעלה אירועים מסוג child added (נוסף צאצא) עבור שני הדינוזאורים עם הניקוד הכי גבוה, באמצעות מטמון שנשמר. אבל לא יופעל אירוע מסוג 'value', כי האפליקציה מעולם לא ביצעה את השאילתה הזו כשהיא הייתה מחוברת לאינטרנט.
אם האפליקציה תבקש את ששת הפריטים האחרונים כשהיא במצב אופליין, היא תקבל מיד את האירועים 'child added' (הוספת ילד) עבור ארבעת הפריטים שנשמרו במטמון. כשהמכשיר חוזר למצב אונליין, הלקוח של מסד נתונים בזמן אמת ב-Firebase מסתנכרן עם השרת ומקבל את שני האירועים האחרונים מסוג child added ואת האירוע value עבור האפליקציה.
טיפול בעסקאות אופליין
כל העסקאות שמבוצעות כשהאפליקציה במצב אופליין מתווספות לתור. אחרי שהאפליקציה מתחברת מחדש לרשת, העסקאות נשלחות לשרת של מסד הנתונים בזמן אמת.
למסד נתונים בזמן אמת ב-Firebase יש הרבה תכונות להתמודדות עם תרחישים אופליין ועם קישוריות לרשת. שאר ההנחיות במדריך הזה רלוונטיות לאפליקציה שלכם, בין אם הפעלתם את התכונה 'התמדה' ובין אם לא.
ניהול הנוכחות
באפליקציות בזמן אמת, לעיתים קרובות כדאי לזהות מתי לקוחות מתחברים ומתנתקים. לדוגמה, יכול להיות שתרצו לסמן משתמש כ 'לא מקוון' כשהלקוח שלו מתנתק.
לקוחות של Firebase Database מספקים פרימיטיבים פשוטים שאפשר להשתמש בהם כדי לכתוב למסד הנתונים כשלקוח מתנתק משרתי Firebase Database. העדכונים האלה מתרחשים גם אם הלקוח מתנתק בצורה תקינה וגם אם לא, כך שאפשר להסתמך עליהם כדי לנקות את הנתונים גם אם החיבור נותק או שהלקוח קורס. אפשר לבצע את כל פעולות הכתיבה, כולל הגדרה, עדכון והסרה, גם כשהחיבור מנותק.
דוגמה פשוטה לכתיבת נתונים לאחר ניתוק באמצעות הפרימיטיב onDisconnect:
final presenceRef = FirebaseDatabase.instance.ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");
איך פועלת הפונקציה onDisconnect
כשמבצעים פעולת onDisconnect(), הפעולה מתבצעת בשרת של מסד נתונים בזמן אמת ב-Firebase. השרת בודק את האבטחה כדי לוודא שהמשתמש יכול לבצע את אירוע הכתיבה המבוקש, ומודיע לאפליקציה אם הוא לא תקין. השרת עוקב אחרי החיבור. אם החיבור נכשל בגלל פסק זמן או אם הוא נסגר באופן פעיל על ידי לקוח מסד נתונים בזמן אמת, השרת בודק את האבטחה בפעם השנייה (כדי לוודא שהפעולה עדיין תקפה) ואז מפעיל את האירוע.
try {
await presenceRef.onDisconnect().remove();
} catch (error) {
debugPrint("Could not establish onDisconnect event: $error");
}
אפשר גם לבטל אירוע onDisconnect באמצעות הקריאה .cancel():
final onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.set("I disconnected");
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel();
זיהוי מצב החיבור
בהרבה תכונות שקשורות לנוכחות, חשוב שהאפליקציה תדע מתי היא אונליין או אופליין. מסד נתונים בזמן אמת ב-Firebase
מספק מיקום מיוחד בכתובת /.info/connected שמתעדכן
בכל פעם שמצב החיבור של הלקוח של מסד הנתונים בזמן אמת ב-Firebase משתנה. לדוגמה:
final connectedRef = FirebaseDatabase.instance.ref(".info/connected");
connectedRef.onValue.listen((event) {
final connected = event.snapshot.value as bool? ?? false;
if (connected) {
debugPrint("Connected.");
} else {
debugPrint("Not connected.");
}
});
/.info/connected הוא ערך בוליאני שלא מסונכרן בין לקוחות מסד נתונים בזמן אמת כי הערך תלוי במצב הלקוח. במילים אחרות, אם לקוח אחד קורא את /.info/connected כ-false, אין זה אומר שלקוח נפרד יקרא גם הוא false.
זמן האחזור של הטיפול
חותמות זמן של השרת
השרתים של מסד נתונים בזמן אמת ב-Firebase מספקים מנגנון להוספת חותמות זמן שנוצרות בשרת כנתונים. התכונה הזו, בשילוב עם onDisconnect, מספקת דרך קלה ומהימנה לציין את השעה שבה לקוח של מסד נתונים בזמן אמת התנתק:
final userLastOnlineRef =
FirebaseDatabase.instance.ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(ServerValue.timestamp);
הבדל בשעונים
אף על פי שהשיטה ServerValue.timestamp מדויקה הרבה יותר, ועדיפה לרוב פעולות הקריאה/הכתיבה, לפעמים כדאי להשתמש בה כדי להעריך את ההטיה בשעון של הלקוח ביחס לשרתים של מסד נתונים בזמן אמת ב-Firebase. אפשר לצרף קריאה חוזרת למיקום /.info/serverTimeOffset כדי לקבל את הערך, באלפיות השנייה, שמוסיפים לקוחות של מסד נתונים בזמן אמת ב-Firebase לזמן המקומי שדווח (זמן עידן באלפיות השנייה) כדי להעריך את זמן השרת. חשוב לזכור שדיוק ההפרש הזה יכול להיות מושפע מהשהיית הרשת, ולכן הוא שימושי בעיקר לגילוי פערים גדולים (מעל שנייה אחת) בשעון.
final offsetRef = FirebaseDatabase.instance.ref(".info/serverTimeOffset");
offsetRef.onValue.listen((event) {
final offset = event.snapshot.value as num? ?? 0.0;
final estimatedServerTimeMs =
DateTime.now().millisecondsSinceEpoch + offset;
});
אפליקציה לדוגמה של נוכחות
אפשר לשלב פעולות ניתוק עם מעקב אחרי מצב החיבור וחותמות זמן של השרת כדי ליצור מערכת לזיהוי נוכחות משתמשים. במערכת הזו, כל משתמש מאחסן נתונים במיקום במסד נתונים כדי לציין אם לקוח של מסד נתונים בזמן אמת מחובר לאינטרנט או לא. הלקוחות מגדירים את המיקום הזה כ-true כשהם מתחברים לאינטרנט, וחותמת זמן כשהם מתנתקים. חותמת הזמן הזו מציינת את הפעם האחרונה שהמשתמש היה אונליין.
שימו לב: האפליקציה צריכה להוסיף את פעולות הניתוק לתור לפני שהמשתמש מסומן כאונליין, כדי למנוע מצבי מירוץ במקרה של אובדן החיבור לרשת של הלקוח לפני ששתי הפקודות נשלחות לשרת.
// Since I can connect from multiple devices, we store each connection
// instance separately any time that connectionsRef's value is null (i.e.
// has no children) I am offline.
final myConnectionsRef =
FirebaseDatabase.instance.ref("users/joe/connections");
// Stores the timestamp of my last disconnect (the last time I was seen online)
final lastOnlineRef =
FirebaseDatabase.instance.ref("/users/joe/lastOnline");
final connectedRef = FirebaseDatabase.instance.ref(".info/connected");
connectedRef.onValue.listen((event) {
final connected = event.snapshot.value as bool? ?? false;
if (connected) {
final con = myConnectionsRef.push();
// When this device disconnects, remove it.
con.onDisconnect().remove();
// When I disconnect, update the last time I was seen online.
lastOnlineRef.onDisconnect().set(ServerValue.timestamp);
// Add this device to my connections list.
// This value could contain info about the device or a timestamp too.
con.set(true);
}
});