אפליקציות Firebase פועלות גם אם האפליקציה מאבדת את הרשת באופן זמני חיבור כזה. בנוסף, Firebase מספק כלים לשמירת נתונים באופן מקומי, לניהול נוכחות ולטיפול בזמן אחזור.
אחסון מתמיד (persistent disk)
אפליקציות Firebase מטפלות באופן אוטומטי בהפרעות זמניות ברשת. הנתונים ששמורים במטמון זמינים במצב אופליין, ו-Firebase שולח מחדש את כל הנתונים שהוספתם כשהחיבור לרשת יתחדש.
כשמפעילים את התכונה 'עמידות בדיסק', האפליקציה כותבת את הנתונים באופן מקומי במכשיר כדי שהיא תוכל לשמור את המצב שלה במצב אופליין, גם אם המשתמש או מערכת ההפעלה מפעילים מחדש את האפליקציה.
אפשר להפעיל את העמידות בדיסק באמצעות שורה אחת של קוד בלבד.
FirebaseDatabase.instance.setPersistenceEnabled(true);
התנהגות התמדה
כשמפעילים את התכונה 'שמירה', כל הנתונים שסונכרנו על ידי לקוח Firebase Realtime Database בזמן החיבור לאינטרנט נשמרים בדיסק וזמינים במצב אופליין, גם אם המשתמש או מערכת ההפעלה מפעילים מחדש את האפליקציה. כלומר, האפליקציה פועלת כמו שהיא פועלת באינטרנט, באמצעות הנתונים המקומיים שמאוחסנים במטמון. קריאות חזרה של מאזינים ימשיכו להתרחש לגבי עדכונים מקומיים.
לקוח Firebase Realtime Database שומר באופן אוטומטי תור של כל פעולות הכתיבה שמבוצעות כשהאפליקציה במצב אופליין. כשהתכונה 'שמירה' מופעלת, התור הזה נשמר גם בדיסק, כך שכל פעולות הכתיבה יהיו זמינות כשהמשתמש או מערכת ההפעלה יפעילו מחדש את האפליקציה. כשהאפליקציה תקבל שוב חיבור, כל הפעולות יישלחו לשרת של Firebase Realtime Database.
אם באפליקציה שלכם נעשה שימוש באימות ב-Firebase, אסימון האימות של המשתמש נשמר על ידי לקוח Firebase Realtime Database בכל הפעלה מחדש של האפליקציה. אם התוקף של אסימון האימות יפוג כשהאפליקציה במצב אופליין, הלקוח יושהה פעולות כתיבה עד שהאפליקציה תאמת מחדש את המשתמש. אחרת פעולות כתיבה עלולות להיכשל בגלל כללי אבטחה.
שמירה על עדכניות הנתונים
מסד הנתונים בזמן אמת ב-Firebase מסנכרן ומאחסן עותק מקומי של נתונים של מאזינים פעילים. בנוסף, אפשר לסנכרן מיקומים ספציפיים.
final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.keepSynced(true);
לקוח מסד הנתונים בזמן אמת ב-Firebase מוריד את הנתונים במיקומים האלה באופן אוטומטי ומסנכרן אותם, גם אם אין למקור מאזינים פעילים. ניתן להשבית את הסנכרון מחדש באמצעות את שורת הקוד הבאה.
scoresRef.keepSynced(false);
כברירת מחדל, 10MB של נתונים שסונכרנו בעבר נשמרים במטמון. זה צריך להיות מספיק עבור רוב האפליקציות. אם המטמון יתרחב מעבר לגודל שהוגדר, מערכת Firebase Realtime Database תנקה נתונים שבהם לא נעשה שימוש לאחרונה. נתונים שנשמרים בסנכרון לא נמחקים מהמטמון באופן סופי.
שליחת שאילתות על הנתונים במצב אופליין
מסד הנתונים בזמן אמת ב-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 מגדיל את 'נוסף צאצא' לאירועים של שני הדינוזאורים שקיבלו את הדירוג הכי גבוה, באמצעות המטמון הקבוע. אבל לא יופיע אירוע 'value', כי האפליקציה אף פעם לא ביצעה את השאילתה הזו בזמן שהיא הייתה אונליין.
אם האפליקציה תבקש את ששת הפריטים האחרונים במצב אופליין, היא תקבל 'נוסף צאצא' אירועים לארבעת הפריטים שנשמרו במטמון באופן מיידי. כאשר המכשיר חוזר לאינטרנט, הלקוח של מסד הנתונים בזמן אמת ב-Firebase מסנכרן עם השרת ומקבל את שני הערכים הבאים של 'הילד או הילדה' את הרצף 'value' אירועים לאפליקציה.
טיפול בעסקאות במצב אופליין
כל העסקאות שמתבצעות כשהאפליקציה במצב אופליין, מועברות לתור. כשהאפליקציה מתחברת מחדש לרשת, העסקאות נשלחות לשרת של Realtime Database.
למסד הנתונים בזמן אמת ב-Firebase יש תכונות רבות לטיפול בתרחישים אופליין ובקישוריות לרשת. שאר המדריך הזה רלוונטי באפליקציה שלכם, גם אם הפעלתם את ההגדרה 'עקביות' וגם אם לא.
ניהול הנוכחות
באפליקציות בזמן אמת, לרוב כדאי לזהות מתי לקוחות מתחברים ומתי הם מתנתקים. לדוגמה, יכול להיות שתרצו לסמן משתמש כ'לא מחובר' כשהלקוח שלו מתנתק.
הלקוחות של Firebase Databases מספקים רכיבים פשוטים שאפשר להשתמש בהם כתיבה למסד הנתונים כשלקוח מתנתק ממסד הנתונים של Firebase שרתים. העדכונים האלה מתרחשים בין אם הלקוח מתנתק באופן נקי או לא, כך שאפשר להסתמך עליהם כדי שינקו נתונים גם אם החיבור נותק או שלקוח קורס. כל פעולות הכתיבה, כולל הגדרה, אפשר לבצע עדכון והסרה אחרי ניתוק.
דוגמה פשוטה לכתיבה של נתונים לאחר ניתוק באמצעות הפרימיטיב onDisconnect
:
final presenceRef = FirebaseDatabase.instance.ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");
איך התכונה 'ניתוק' פועלת
כשיוצרים פעולה ב-onDisconnect()
, הפעולה
נמצא בשרת של מסד הנתונים בזמן אמת של Firebase. השרת בודק את האבטחה כדי
לוודא שהמשתמש יכול לבצע את אירוע הכתיבה המבוקש, ומודיע
האפליקציה לא תקינה. לאחר מכן השרת
עוקב אחר החיבור. אם בשלב כלשהו חל הזמן הקצוב לתפוגה של החיבור, או שהוא נסגר באופן פעיל על ידי לקוח Realtime Database, השרת בודק את האבטחה בפעם השנייה (כדי לוודא שהפעולה עדיין תקפה) ואז מפעיל את האירוע.
try {
await presenceRef.onDisconnect().remove();
} catch (error) {
debugPrint("Could not establish onDisconnect event: $error");
}
אפשר לבטל אירוע onניתוק גם באמצעות התקשרות אל .cancel()
:
final onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.set("I disconnected");
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel();
זיהוי מצב החיבור
כדי להשתמש בתכונות רבות שקשורות לנוכחות, חשוב שהאפליקציה תדע מתי היא אונליין ומתי היא אופליין. ב-Firebase Realtime Database יש מיקום מיוחד ב-/.info/connected
שמתעדכן בכל פעם שמצב החיבור של לקוח Firebase Realtime Database משתנה. לדוגמה:
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.
זמן האחזור בטיפול
חותמות זמן של שרת
השרתים של מסד הנתונים בזמן אמת ב-Firebase מספקים מנגנון להוספה
חותמות הזמן שנוצרות בשרת בתור נתונים. התכונה הזו, בשילוב עם
onDisconnect
, מספק דרך קלה לתעד באופן אמין
השעה שבה לקוח של מסד נתונים בזמן אמת התנתק:
final userLastOnlineRef =
FirebaseDatabase.instance.ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(ServerValue.timestamp);
Clock Skew
הערך של ServerValue.timestamp
מדויק הרבה יותר ומומלץ לרוב פעולות הקריאה/הכתיבה, אבל לפעמים כדאי להעריך את הסטייה של השעון של הלקוח ביחס לשרתים של Firebase Realtime Database. שלך
יכול לצרף קריאה חוזרת למיקום /.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;
});
אפליקציית נוכחות לדוגמה
על ידי שילוב של פעולות ניתוק עם מעקב אחר מצב החיבור את חותמות הזמן של השרת, אפשר ליצור מערכת נוכחות של משתמשים. במערכת הזו, כל משתמש מאחסן נתונים במיקום במסד נתונים כדי לציין אם לקוח של Realtime Database מחובר או לא. לקוחות מגדירים את המיקום הזה כ-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);
}
});