ऑफ़लाइन क्षमताएं चालू करना

अगर आपके ऐप्लिकेशन का नेटवर्क कनेक्शन कुछ समय के लिए टूट जाता है, तब भी 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);

डिफ़ॉल्ट रूप से, पहले सिंक किए गए 10 एमबी डेटा को कैश मेमोरी में सेव किया जाता है. ज़्यादातर ऐप्लिकेशन के लिए, यह काफ़ी होना चाहिए. अगर कैश मेमोरी का साइज़, कॉन्फ़िगर किए गए साइज़ से ज़्यादा हो जाता है, तो 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 रीयल टाइम डेटाबेस क्लाइंट, सर्वर के साथ सिंक हो जाता है और ऐप्लिकेशन के लिए, 'चाइल्ड ऐडेड' के आखिरी दो इवेंट और 'वैल्यू' इवेंट मिल जाते हैं.

ऑफ़लाइन होने पर लेन-देन मैनेज करना

ऐप्लिकेशन के ऑफ़लाइन होने पर किए गए सभी लेन-देन, सूची में शामिल किए जाते हैं. ऐप्लिकेशन के फिर से कनेक्ट होने पर, लेन-देन रीयलटाइम डेटाबेस सर्वर को भेज दिए जाते हैं.

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 रीयलटाइम डेटाबेस सर्वर पर काम करती है. सर्वर, सुरक्षा की जांच करके यह पक्का करता है कि उपयोगकर्ता, अनुरोध किया गया सेव करने का इवेंट पूरा कर सकता है या नहीं. साथ ही, अगर यह अमान्य है, तो आपके ऐप्लिकेशन को इसकी जानकारी देता है. इसके बाद, सर्वर कनेक्शन की निगरानी करता है. अगर किसी भी समय कनेक्शन टाइम आउट हो जाता है या रीयलटाइम डेटाबेस क्लाइंट, कनेक्शन को बंद कर देता है, तो सर्वर दूसरी बार सुरक्षा की जांच करता है. इससे यह पक्का किया जाता है कि कार्रवाई अब भी मान्य है. इसके बाद, इवेंट को ट्रिगर किया जाता है.

try {
    await presenceRef.onDisconnect().remove();
} catch (error) {
    debugPrint("Could not establish onDisconnect event: $error");
}

.cancel() को कॉल करके, onDisconnect इवेंट को रद्द भी किया जा सकता है:

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 रीयलटाइम डेटाबेस क्लाइंट, सर्वर के समय का अनुमान लगाने के लिए, स्थानीय तौर पर रिपोर्ट किए गए समय (मिलीसेकंड में epoch टाइम) में इस वैल्यू को जोड़ते हैं. ध्यान दें कि इस ऑफ़सेट की सटीक जानकारी, नेटवर्क के इंतज़ार के समय से प्रभावित हो सकती है. इसलिए, यह मुख्य रूप से घड़ी के समय में बड़े (> 1 सेकंड) अंतर का पता लगाने के लिए काम का है.

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);
  }
});