برنامههای Firebase حتی اگر برنامه شما موقتاً اتصال شبکهاش را ازدست بدهد، کار میکنند. علاوهبراین، Firebase ابزارهایی برای ماندگاری دادهها بهصورت محلی، مدیریت حضور، و رسیدگی به تأخیر ارائه میدهد.
پایداری دیسک
برنامههای Firebase بهطور خودکار وقفههای موقت شبکه را مدیریت میکنند. دادههای ذخیرهشده در حافظه پنهان در حالت آفلاین دردسترس هستند و Firebase هرگونه نوشتن را هنگام بازیابی اتصال شبکه دوباره ارسال میکند.
وقتی پایداری دیسک را فعال میکنید، برنامهتان دادهها را بهصورت محلی در دستگاه مینویسد تا برنامهتان بتواند وضعیت را درحالت آفلاین حفظ کند، حتی اگر کاربر یا سیستمعامل برنامه را بازراهاندازی کند.
میتوانید پایداری دیسک را فقط با یک خط کد فعال کنید.
FirebaseDatabase.instance.setPersistenceEnabled(true);
رفتار پایداری
با فعال کردن پایداری، هر دادهای که کارخواه «پایگاه داده بیدرنگ Firebase» در حالت آنلاین همگامسازی میکند در دیسک پایدار میماند و در حالت آفلاین دردسترس است، حتی وقتی کاربر یا سیستمعامل برنامه را بازراهاندازی میکند. این یعنی برنامه شما بااستفاده از دادههای محلی ذخیرهشده در حافظه نهان، همانند حالت آنلاین کار میکند. فراخوانیهای برگشتی شنونده همچنان برای بهروزرسانیهای محلی فعال خواهند بود.
کارخواه «پایگاه داده بیدرنگ Firebase» بهطور خودکار صفی از همه عملیات نوشتن که درحین آفلاین بودن برنامه شما انجام میشود نگه میدارد. وقتی پایداری فعال باشد، این صف در دیسک نیز پایدار میشود تا همه نوشتههای شما هنگام راهاندازی مجدد برنامه توسط کاربر یا سیستمعامل دردسترس باشد. وقتی برنامه دوباره متصل میشود، همه عملیات به سرور «پایگاه داده بیدرنگ Firebase» ارسال میشود.
اگر برنامهتان از اصالتسنجی Firebase استفاده میکند، کارخواه «پایگاه داده بیدرنگ Firebase» کد اصالتسنجی کاربر را در راهاندازیهای مجدد برنامه حفظ میکند. اگر توکن اصالتسنجی درحالیکه برنامهتان آفلاین است منقضی شود، کارخواه عملیات نوشتن را تا زمانی که برنامهتان کاربر را دوباره اصالتسنجی کند موقتاً متوقف میکند، درغیراینصورت عملیات نوشتن ممکن است بهدلیل قوانین امنیتی ناموفق باشد.
بهروز نگه داشتن دادهها
«پایگاه داده بیدرنگ Firebase» نسخه محلی دادهها را برای شنوندگان فعال همگامسازی و ذخیره میکند. علاوهبراین، میتوانید مکانهای خاصی را همگامسازی کنید.
final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.keepSynced(true);
کارخواه «پایگاه داده بیدرنگ Firebase» بهطور خودکار دادهها را در این مکانها بارگیری میکند و آنها را همگامسازی میکند، حتی اگر مرجع شنونده فعالی نداشته باشد. با خط کد زیر میتوانید همگامسازی را دوباره خاموش کنید.
scoresRef.keepSynced(false);
بهطور پیشفرض، ۱۰ مگابایت از دادههای قبلاً همگامسازیشده در حافظه پنهان ذخیره میشود. این مقدار باید برای اکثر برنامهها کافی باشد. اگر اندازه حافظه نهان از اندازه پیکربندیشده آن بیشتر شود، «پایگاه داده بیدرنگ 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» بااستفاده از حافظه نهان پایدار، رویدادهای «فرزند اضافهشده» را برای دو دایناسور با بالاترین امتیاز ایجاد میکند. اما رویداد «مقدار» را ایجاد نخواهد کرد، زیرا برنامه هرگز آن پُرسمان را درحالت آنلاین اجرا نکرده است.
اگر برنامه درحالت آفلاین شش مورد آخر را درخواست کند، رویدادهای «فرزند اضافه شد» را برای چهار مورد ذخیرهشده در حافظه نهان بلافاصله دریافت میکند. وقتی دستگاه دوباره آنلاین میشود، کارخواه «پایگاه داده بیدرنگ Firebase» با سرور همگامسازی میشود و دو رویداد نهایی «فرزند اضافه شد» و «مقدار» را برای برنامه دریافت میکند.
مدیریت تراکنشها بهصورت آفلاین
هر تراکنشی که درحین آفلاین بودن برنامه انجام شود در صف قرار میگیرد. وقتی برنامه دوباره به شبکه متصل میشود، تراکنشها به سرور Realtime Database ارسال میشود.
«پایگاه داده بیدرنگ Firebase» ویژگیهای زیادی برای مقابله با سناریوهای آفلاین و اتصال شبکه دارد. بقیه این راهنما برای برنامه شما اعمال میشود، چه پایداری را فعال کرده باشید یا نه.
مدیریت حضور
در برنامههای همزمان، اغلب مفید است که بدانید مشتریان چه زمانی متصل و قطع میشوند. برای مثال، ممکن است بخواهید وقتی کارخواه کاربر قطع میشود، او را بهعنوان «آفلاین» علامتگذاری کنید.
کلاینتهای «پایگاه داده Firebase» دستورات اولیهای را ارائه میدهند که میتوانید از آنها برای نوشتن در پایگاه داده هنگام قطع اتصال کلاینت از سرورهای «پایگاه داده Firebase» استفاده کنید. این بهروزرسانیها چه کارخواه بهدرستی قطع شود یا نه، اتفاق میافتند، بنابراین میتوانید برای پاک کردن دادهها به آنها اعتماد کنید، حتی اگر اتصال قطع شود یا کارخواه ازکار بیفتد. همه عملیات نوشتن، ازجمله تنظیم، بهروزرسانی، و برداشتن، میتواند پساز قطع اتصال انجام شود.
در اینجا نمونه سادهای از نوشتن دادهها هنگام قطع اتصال بااستفاده از
onDisconnect اولیه آورده شده است:
final presenceRef = FirebaseDatabase.instance.ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");
نحوه عملکرد onDisconnect
وقتی عملیات onDisconnect() را برقرار میکنید، این عملیات
در سرور «پایگاه داده بیدرنگ Firebase» قرار میگیرد. سرور امنیت را بررسی میکند تا
مطمئن شود کاربر میتواند رویداد نوشتن درخواستشده را انجام دهد، و اگر نامعتبر باشد
به برنامه شما اطلاع میدهد. سپس سرور
اتصال را پایش میکند. اگر در هر نقطهای اتصال قطع شود یا
بهطور فعال توسط کارخواه Realtime Database بسته شود، سرور امنیت را برای
بار دوم بررسی میکند (تا مطمئن شود عملیات همچنان معتبر است) و سپس
رویداد را فراخوانی میکند.
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 را نادرست بخواند، این
تضمینی نیست که کارخواه جداگانهای نیز نادرست بخواند.
تأخیر در رسیدگی
مُهرهای زمان سرور
سرورهای «پایگاه داده بیدرنگ 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;
});
برنامه نمونه حضور
با ترکیب عملیات قطع اتصال با نظارت بر وضعیت اتصال و مُهرهای زمان سرور، میتوانید سیستم حضور کاربر بسازید. در این سیستم، هر کاربر دادهها را در مکانی از پایگاه داده ذخیره میکند تا نشان دهد که آیا کارخواه «پایگاه داده همزمان» آنلاین است یا نه. مشتریان وقتی آنلاین میشوند این مکان را روی درست تنظیم میکنند و وقتی اتصالشان قطع میشود مُهر زمان میزنند. این مُهر زمان نشاندهنده آخرین باری است که کاربر موردنظر آنلاین بوده است.
توجه داشته باشید که برنامهتان باید عملیات قطع اتصال را قبلاز اینکه کاربر بهعنوان آنلاین علامتگذاری شود در صف قرار دهد تا درصورت قطع اتصال شبکه کارخواه قبلاز اینکه هر دو فرمان به سرور ارسال شود، از هرگونه شرایط رقابتی جلوگیری شود.
// 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);
}
});