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

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

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

सैंपल Presence ऐप्लिकेशन

डिसकनेक्ट करने की कार्रवाइयों को कनेक्शन की स्थिति की निगरानी और सर्वर टाइमस्टैंप के साथ जोड़कर, उपयोगकर्ता की मौजूदगी का सिस्टम बनाया जा सकता है. इस सिस्टम में, हर उपयोगकर्ता डेटाबेस की किसी जगह पर डेटा सेव करता है. इससे पता चलता है कि रीयलटाइम डेटाबेस क्लाइंट ऑनलाइन है या नहीं. क्लाइंट, ऑनलाइन होने पर इस जगह को 'सही है' पर सेट करते हैं. साथ ही, डिसकनेक्ट होने पर टाइमस्टैंप सेट करते हैं. इस टाइमस्टैंप से पता चलता है कि उपयोगकर्ता पिछली बार कब ऑनलाइन था.

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

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