इस दस्तावेज़ में, Firebase डेटा को पढ़ने और लिखने के बारे में बुनियादी जानकारी दी गई है.
Firebase डेटा को FirebaseDatabase
रेफ़रंस में लिखा जाता है. साथ ही, रेफ़रंस में एसिंक्रोनस लिसनर अटैच करके इसे वापस पाया जाता है. लिसनर, डेटा की शुरुआती स्थिति के लिए एक बार ट्रिगर होता है. इसके बाद, जब भी डेटा में बदलाव होता है, तब यह ट्रिगर होता है.
(ज़रूरी नहीं) Firebase Local Emulator Suite के साथ प्रोटोटाइप बनाना और उसकी जांच करना
इससे पहले कि हम यह बताएं कि आपका ऐप्लिकेशन, Realtime Database से डेटा कैसे पढ़ता है और उसमें डेटा कैसे लिखता है, आइए हम आपको उन टूल के बारे में बताते हैं जिनका इस्तेमाल करके, Realtime Database की सुविधाओं का प्रोटोटाइप बनाया जा सकता है और उन्हें टेस्ट किया जा सकता है: Firebase Local Emulator Suite. अगर आपको अलग-अलग डेटा मॉडल आज़माने हैं, सुरक्षा नियमों को ऑप्टिमाइज़ करना है या बैक-एंड के साथ इंटरैक्ट करने का सबसे किफ़ायती तरीका ढूंढना है, तो लाइव सेवाओं को डिप्लॉय किए बिना स्थानीय तौर पर काम करना एक अच्छा विकल्प हो सकता है.
Realtime Database एम्युलेटर, Local Emulator Suite का हिस्सा है. इसकी मदद से, आपका ऐप्लिकेशन, एम्युलेट किए गए डेटाबेस के कॉन्टेंट और कॉन्फ़िगरेशन के साथ-साथ, एम्युलेट किए गए प्रोजेक्ट के संसाधनों (फ़ंक्शन, अन्य डेटाबेस, और सुरक्षा के नियम) के साथ इंटरैक्ट कर सकता है. हालांकि, ऐसा करना ज़रूरी नहीं है.
Realtime Database एम्युलेटर का इस्तेमाल करने के लिए, बस कुछ ही चरणों को पूरा करना होता है:
- एम्युलेटर से कनेक्ट करने के लिए, अपने ऐप्लिकेशन की टेस्ट कॉन्फ़िगरेशन में कोड की एक लाइन जोड़ें.
- अपनी लोकल प्रोजेक्ट डायरेक्ट्री के रूट से,
firebase emulators:start
चलाएं. - Realtime Database प्लैटफ़ॉर्म SDK का इस्तेमाल करके, अपने ऐप्लिकेशन के प्रोटोटाइप कोड से कॉल करें. इसके अलावा, Realtime Database REST API का इस्तेमाल करके भी कॉल किए जा सकते हैं.
Realtime Database और Cloud Functions को शामिल करके, ज़्यादा जानकारी वाला वॉकट्रू उपलब्ध है. आपको Local Emulator Suite के बारे में जानकारी भी देखनी चाहिए.
DatabaseReference पाना
डेटाबेस से डेटा पढ़ने या उसमें डेटा लिखने के लिए, आपको DatabaseReference
के इंस्टेंस की ज़रूरत होगी:
Kotlin
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
डेटा लिखने की अनुमति दें
लिखने की बुनियादी कार्रवाइयां
लिखने से जुड़ी बुनियादी कार्रवाइयों के लिए, setValue()
का इस्तेमाल करके डेटा को किसी खास रेफ़रंस में सेव किया जा सकता है. इससे उस पाथ पर मौजूद मौजूदा डेटा बदल जाता है. इस तरीके का इस्तेमाल करके, ये काम किए जा सकते हैं:
- पास के टाइप, जो उपलब्ध JSON टाइप से मेल खाते हैं. ये इस तरह हैं:
String
Long
Double
Boolean
Map<String, Object>
List<Object>
- अगर उसे तय करने वाली क्लास में ऐसा डिफ़ॉल्ट कंस्ट्रक्टर है जो कोई आर्ग्युमेंट नहीं लेता है और असाइन की जाने वाली प्रॉपर्टी के लिए सार्वजनिक गेटर हैं, तो एक कस्टम Java ऑब्जेक्ट पास करें.
Java ऑब्जेक्ट का इस्तेमाल करने पर, आपके ऑब्जेक्ट का कॉन्टेंट अपने-आप मैप हो जाता है. यह कॉन्टेंट, नेस्ट किए गए फ़ॉर्मैट में चाइल्ड लोकेशन पर मैप होता है. Java ऑब्जेक्ट का इस्तेमाल करने से, आम तौर पर आपका कोड ज़्यादा आसानी से पढ़ा जा सकता है और उसे बनाए रखना आसान हो जाता है. उदाहरण के लिए, अगर आपके पास बुनियादी उपयोगकर्ता प्रोफ़ाइल वाला कोई ऐप्लिकेशन है, तो आपका User
ऑब्जेक्ट इस तरह दिख सकता है:
Kotlin
@IgnoreExtraProperties data class User(val username: String? = null, val email: String? = null) { // Null default values create a no-argument default constructor, which is needed // for deserialization from a DataSnapshot. }
Java
@IgnoreExtraProperties public class User { public String username; public String email; public User() { // Default constructor required for calls to DataSnapshot.getValue(User.class) } public User(String username, String email) { this.username = username; this.email = email; } }
setValue()
की भूमिका वाले उपयोगकर्ता को इस तरह जोड़ा जा सकता है:
Kotlin
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
Java
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
setValue()
का इस तरह इस्तेमाल करने से, तय की गई जगह पर मौजूद डेटा मिट जाता है. इसमें चाइल्ड नोड भी शामिल हैं. हालांकि, पूरे ऑब्जेक्ट को फिर से लिखे बिना भी किसी बच्चे की जानकारी अपडेट की जा सकती है. अगर आपको उपयोगकर्ताओं को अपनी प्रोफ़ाइल अपडेट करने की अनुमति देनी है, तो उपयोगकर्ता नाम को इस तरह अपडेट करें:
Kotlin
database.child("users").child(userId).child("username").setValue(name)
Java
mDatabase.child("users").child(userId).child("username").setValue(name);
डेटा पढ़ने की अनुमति दें
लगातार काम करने वाले लिसनर की मदद से डेटा पढ़ना
किसी पाथ पर मौजूद डेटा को पढ़ने और उसमें होने वाले बदलावों को सुनने के लिए, addValueEventListener()
तरीके का इस्तेमाल करके DatabaseReference
में ValueEventListener
जोड़ें.
श्रोता | इवेंट कॉलबैक | आम तौर पर इस्तेमाल |
---|---|---|
ValueEventListener |
onDataChange() |
किसी पाथ के पूरे कॉन्टेंट में हुए बदलावों को पढ़ना और सुनना. |
onDataChange()
तरीके का इस्तेमाल करके, किसी दिए गए पाथ पर मौजूद कॉन्टेंट का स्टैटिक स्नैपशॉट पढ़ा जा सकता है. यह स्नैपशॉट, इवेंट के समय मौजूद कॉन्टेंट का होता है. यह तरीका, लिसनर के अटैच होने पर एक बार ट्रिगर होता है. इसके बाद, यह हर बार तब ट्रिगर होता है, जब डेटा में बदलाव होता है. इसमें बच्चों का डेटा भी शामिल है. इवेंट कॉलबैक को एक स्नैपशॉट पास किया जाता है. इसमें उस जगह का सारा डेटा होता है. इसमें चाइल्ड डेटा भी शामिल होता है. अगर कोई डेटा नहीं है, तो exists()
पर कॉल करने पर स्नैपशॉट false
दिखाएगा और getValue()
पर कॉल करने पर null
दिखाएगा.
यहां दिए गए उदाहरण में, सोशल ब्लॉगिंग ऐप्लिकेशन को डेटाबेस से पोस्ट की जानकारी मिलती है:
Kotlin
val postListener = object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { // Get Post object and use the values to update the UI val post = dataSnapshot.getValue<Post>() // ... } override fun onCancelled(databaseError: DatabaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()) } } postReference.addValueEventListener(postListener)
Java
ValueEventListener postListener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Get Post object and use the values to update the UI Post post = dataSnapshot.getValue(Post.class); // .. } @Override public void onCancelled(DatabaseError databaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()); } }; mPostReference.addValueEventListener(postListener);
लिसनर को एक DataSnapshot
मिलता है. इसमें इवेंट के समय, डेटाबेस में बताई गई जगह पर मौजूद डेटा होता है. स्नैपशॉट पर getValue()
को कॉल करने पर, डेटा का Java ऑब्जेक्ट फ़ॉर्मैट मिलता है. अगर किसी जगह का कोई डेटा मौजूद नहीं है, तो getValue()
को कॉल करने पर null
मिलता है.
इस उदाहरण में, ValueEventListener
onCancelled()
मेथड को भी तय करता है. इस मेथड को तब कॉल किया जाता है, जब पढ़ने की प्रोसेस रद्द कर दी जाती है. उदाहरण के लिए, अगर क्लाइंट के पास Firebase डेटाबेस की किसी जगह से डेटा पढ़ने की अनुमति नहीं है, तो पढ़ने की प्रोसेस को रद्द किया जा सकता है. इस
तरीके में DatabaseError
ऑब्जेक्ट पास किया जाता है. इससे पता चलता है कि अनुरोध पूरा क्यों नहीं किया जा सका.
डेटा को एक बार पढ़ना
get() का इस्तेमाल करके एक बार पढ़ना
एसडीके को डेटाबेस सर्वर के साथ इंटरैक्शन मैनेज करने के लिए डिज़ाइन किया गया है. इससे कोई फ़र्क़ नहीं पड़ता कि आपका ऐप्लिकेशन ऑनलाइन है या ऑफ़लाइन.
आम तौर पर, आपको ValueEventListener
ऊपर बताई गई तकनीकों
का इस्तेमाल करके डेटा पढ़ना चाहिए, ताकि बैकएंड से डेटा में होने वाले अपडेट के बारे में सूचना मिल सके. लिसनर की तकनीकों से, आपके ऐप्लिकेशन के इस्तेमाल और बिलिंग में कमी आती है. साथ ही, इन्हें इस तरह से ऑप्टिमाइज़ किया जाता है कि जब उपयोगकर्ता ऑनलाइन और ऑफ़लाइन हों, तब उन्हें बेहतरीन अनुभव मिल सके.
अगर आपको डेटा सिर्फ़ एक बार चाहिए, तो डेटाबेस से डेटा का स्नैपशॉट पाने के लिए, get()
का इस्तेमाल करें. अगर किसी वजह से get()
, सर्वर वैल्यू नहीं दिखा पाता है, तो क्लाइंट लोकल स्टोरेज कैश मेमोरी की जांच करेगा. अगर वैल्यू अब भी नहीं मिलती है, तो वह गड़बड़ी का मैसेज दिखाएगा.
get()
का ज़रूरत से ज़्यादा इस्तेमाल करने पर, बैंडविथ का इस्तेमाल बढ़ सकता है और परफ़ॉर्मेंस में गिरावट आ सकती है. इससे बचने के लिए, ऊपर दिखाए गए तरीके से रीयलटाइम लिसनर का इस्तेमाल करें.
Kotlin
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
Java
mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
@Override
public void onComplete(@NonNull Task<DataSnapshot> task) {
if (!task.isSuccessful()) {
Log.e("firebase", "Error getting data", task.getException());
}
else {
Log.d("firebase", String.valueOf(task.getResult().getValue()));
}
}
});
लिसनर का इस्तेमाल करके एक बार पढ़ना
कुछ मामलों में, आपको सर्वर पर अपडेट की गई वैल्यू की जांच करने के बजाय, लोकल कैश मेमोरी से वैल्यू तुरंत वापस चाहिए हो सकती है. ऐसे मामलों में, addListenerForSingleValueEvent
का इस्तेमाल करके लोकल डिस्क कैश से डेटा तुरंत पाया जा सकता है.
यह उस डेटा के लिए फ़ायदेमंद है जिसे सिर्फ़ एक बार लोड करना होता है. साथ ही, जिसमें बार-बार बदलाव होने की संभावना नहीं होती या जिसे सुनने के लिए, डिवाइस के चालू रहने की ज़रूरत नहीं होती. उदाहरण के लिए, पिछले उदाहरणों में दिया गया ब्लॉगिंग ऐप्लिकेशन, इस तरीके का इस्तेमाल करके उपयोगकर्ता की प्रोफ़ाइल लोड करता है. ऐसा तब होता है, जब उपयोगकर्ता कोई नई पोस्ट लिखना शुरू करता है.
डेटा अपडेट करना या मिटाना
चुनिंदा फ़ील्ड अपडेट करना
अगर आपको किसी नोड के कुछ बच्चों को एक साथ लिखना है और अन्य चाइल्ड नोड को नहीं बदलना है, तो updateChildren()
तरीके का इस्तेमाल करें.
updateChildren()
को कॉल करते समय, कुंजी के लिए पाथ तय करके, निचले लेवल की चाइल्ड वैल्यू अपडेट की जा सकती हैं. अगर डेटा को बेहतर तरीके से स्केल करने के लिए, उसे एक से ज़्यादा जगहों पर सेव किया जाता है, तो डेटा फ़ैन-आउट का इस्तेमाल करके, उस डेटा के सभी इंस्टेंस को अपडेट किया जा सकता है. उदाहरण के लिए, सोशल ब्लॉगिंग ऐप्लिकेशन में Post
क्लास इस तरह से हो सकती है:
Kotlin
@IgnoreExtraProperties data class Post( var uid: String? = "", var author: String? = "", var title: String? = "", var body: String? = "", var starCount: Int = 0, var stars: MutableMap<String, Boolean> = HashMap(), ) { @Exclude fun toMap(): Map<String, Any?> { return mapOf( "uid" to uid, "author" to author, "title" to title, "body" to body, "starCount" to starCount, "stars" to stars, ) } }
Java
@IgnoreExtraProperties public class Post { public String uid; public String author; public String title; public String body; public int starCount = 0; public Map<String, Boolean> stars = new HashMap<>(); public Post() { // Default constructor required for calls to DataSnapshot.getValue(Post.class) } public Post(String uid, String author, String title, String body) { this.uid = uid; this.author = author; this.title = title; this.body = body; } @Exclude public Map<String, Object> toMap() { HashMap<String, Object> result = new HashMap<>(); result.put("uid", uid); result.put("author", author); result.put("title", title); result.put("body", body); result.put("starCount", starCount); result.put("stars", stars); return result; } }
पोस्ट बनाने और उसे एक साथ हाल ही की गतिविधि वाले फ़ीड और पोस्ट करने वाले उपयोगकर्ता की गतिविधि वाले फ़ीड में अपडेट करने के लिए, ब्लॉगिंग ऐप्लिकेशन इस तरह के कोड का इस्तेमाल करता है:
Kotlin
private fun writeNewPost(userId: String, username: String, title: String, body: String) { // Create new post at /user-posts/$userid/$postid and at // /posts/$postid simultaneously val key = database.child("posts").push().key if (key == null) { Log.w(TAG, "Couldn't get push key for posts") return } val post = Post(userId, username, title, body) val postValues = post.toMap() val childUpdates = hashMapOf<String, Any>( "/posts/$key" to postValues, "/user-posts/$userId/$key" to postValues, ) database.updateChildren(childUpdates) }
Java
private void writeNewPost(String userId, String username, String title, String body) { // Create new post at /user-posts/$userid/$postid and at // /posts/$postid simultaneously String key = mDatabase.child("posts").push().getKey(); Post post = new Post(userId, username, title, body); Map<String, Object> postValues = post.toMap(); Map<String, Object> childUpdates = new HashMap<>(); childUpdates.put("/posts/" + key, postValues); childUpdates.put("/user-posts/" + userId + "/" + key, postValues); mDatabase.updateChildren(childUpdates); }
इस उदाहरण में, push()
का इस्तेमाल करके, /posts/$postid
पर मौजूद सभी उपयोगकर्ताओं के लिए पोस्ट वाले नोड में पोस्ट बनाई गई है. साथ ही, getKey()
की मदद से कुंजी को एक साथ वापस पाया गया है. इसके बाद, इस कुंजी का इस्तेमाल करके उपयोगकर्ता की पोस्ट में दूसरा एंट्री पॉइंट बनाया जा सकता है. यह एंट्री पॉइंट /user-posts/$userid/$postid
पर दिखेगा.
इन पाथ का इस्तेमाल करके, JSON ट्री में मौजूद एक से ज़्यादा जगहों की जानकारी को एक साथ अपडेट किया जा सकता है. इसके लिए, updateChildren()
को सिर्फ़ एक बार कॉल करना होगा. उदाहरण के लिए, इस उदाहरण में दिखाया गया है कि दोनों जगहों के लिए नई पोस्ट कैसे बनाई जाती है. इस तरीके से एक साथ किए गए अपडेट ऐटॉमिक होते हैं: या तो सभी अपडेट पूरे हो जाते हैं या सभी अपडेट पूरे नहीं होते.
कॉम्प्लीशन कॉलबैक जोड़ना
अगर आपको यह जानना है कि आपका डेटा कब सेव हुआ, तो पूरा होने की सूचना देने वाला लिसनर जोड़ा जा सकता है. setValue()
और updateChildren()
, दोनों में एक वैकल्पिक कंप्लीशन लिसनर होता है. इसे तब कॉल किया जाता है, जब डेटाबेस में डेटा को लिखने की प्रोसेस पूरी हो जाती है. अगर कॉल पूरा नहीं हुआ, तो लिसनर को गड़बड़ी वाला ऑब्जेक्ट पास किया जाता है. इससे पता चलता है कि कॉल पूरा क्यों नहीं हुआ.
Kotlin
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
Java
mDatabase.child("users").child(userId).setValue(user) .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { // Write was successful! // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Write failed // ... } });
डेटा मिटाना
डेटा मिटाने का सबसे आसान तरीका यह है कि उस डेटा के रेफ़रंस पर removeValue()
कॉल करें.
setValue()
या updateChildren()
जैसे किसी दूसरे राइट ऑपरेशन के लिए, वैल्यू के तौर पर null
तय करके भी मिटाया जा सकता है. इस तकनीक का इस्तेमाल updateChildren()
के साथ किया जा सकता है. इससे एक ही एपीआई कॉल में कई बच्चों के खातों को मिटाया जा सकता है.
लिसनर अलग करना
अपने Firebase डेटाबेस रेफ़रंस पर removeEventListener()
तरीके को कॉल करके, कॉलबैक हटाए जाते हैं.
अगर किसी डेटा लोकेशन में लिसनर को कई बार जोड़ा गया है, तो हर इवेंट के लिए उसे कई बार कॉल किया जाता है. इसे पूरी तरह से हटाने के लिए, आपको इसे उतनी ही बार अलग करना होगा जितनी बार इसे जोड़ा गया था.
माता-पिता के लिसनर पर removeEventListener()
को कॉल करने से, उसके चाइल्ड नोड पर रजिस्टर किए गए लिसनर अपने-आप नहीं हटते; कॉलबैक हटाने के लिए, किसी भी चाइल्ड लिसनर पर भी removeEventListener()
को कॉल करना होगा.
डेटा को लेन-देन के तौर पर सेव करना
अगर आपको ऐसे डेटा के साथ काम करना है जिसमें एक साथ कई बदलाव किए जा सकते हैं, तो लेन-देन के ऑपरेशन का इस्तेमाल किया जा सकता है. जैसे, इंक्रीमेंटल काउंटर. इस ऑपरेशन में दो आर्ग्युमेंट दिए जाते हैं: अपडेट फ़ंक्शन और पूरा होने पर कॉल करने वाला वैकल्पिक फ़ंक्शन. अपडेट फ़ंक्शन, डेटा की मौजूदा स्थिति को एक आर्ग्युमेंट के तौर पर लेता है. साथ ही, वह नई स्थिति दिखाता है जिसे आपको लिखना है. अगर आपकी नई वैल्यू के सेव होने से पहले कोई दूसरा क्लाइंट लोकेशन में बदलाव करता है, तो अपडेट फ़ंक्शन को नई वैल्यू के साथ फिर से कॉल किया जाता है. इसके बाद, बदलाव को सेव करने की कोशिश की जाती है.
उदाहरण के लिए, सोशल ब्लॉगिंग ऐप्लिकेशन में, उपयोगकर्ताओं को पोस्ट को स्टार करने और स्टार हटाने की अनुमति दी जा सकती है. साथ ही, यह ट्रैक किया जा सकता है कि किसी पोस्ट को कितने स्टार मिले हैं. इसके लिए, यह तरीका अपनाएं:
Kotlin
private fun onStarClicked(postRef: DatabaseReference) { // ... postRef.runTransaction(object : Transaction.Handler { override fun doTransaction(mutableData: MutableData): Transaction.Result { val p = mutableData.getValue(Post::class.java) ?: return Transaction.success(mutableData) if (p.stars.containsKey(uid)) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1 p.stars.remove(uid) } else { // Star the post and add self to stars p.starCount = p.starCount + 1 p.stars[uid] = true } // Set value and report transaction success mutableData.value = p return Transaction.success(mutableData) } override fun onComplete( databaseError: DatabaseError?, committed: Boolean, currentData: DataSnapshot?, ) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError!!) } }) }
Java
private void onStarClicked(DatabaseReference postRef) { postRef.runTransaction(new Transaction.Handler() { @NonNull @Override public Transaction.Result doTransaction(@NonNull MutableData mutableData) { Post p = mutableData.getValue(Post.class); if (p == null) { return Transaction.success(mutableData); } if (p.stars.containsKey(getUid())) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1; p.stars.remove(getUid()); } else { // Star the post and add self to stars p.starCount = p.starCount + 1; p.stars.put(getUid(), true); } // Set value and report transaction success mutableData.setValue(p); return Transaction.success(mutableData); } @Override public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot currentData) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError); } }); }
लेन-देन का इस्तेमाल करने से, स्टार की संख्या गलत नहीं होती. ऐसा तब होता है, जब कई उपयोगकर्ता एक ही समय में एक ही पोस्ट को स्टार करते हैं या क्लाइंट के पास पुराना डेटा होता है. अगर लेन-देन अस्वीकार कर दिया जाता है, तो सर्वर क्लाइंट को मौजूदा वैल्यू दिखाता है. इसके बाद, क्लाइंट अपडेट की गई वैल्यू के साथ लेन-देन को फिर से शुरू करता है. यह प्रोसेस तब तक दोहराई जाती है, जब तक लेन-देन स्वीकार नहीं हो जाता या बहुत ज़्यादा कोशिशें नहीं हो जातीं.
सर्वर-साइड से होने वाली एटॉमिक बढ़ोतरी
ऊपर दिए गए इस्तेमाल के उदाहरण में, हम डेटाबेस में दो वैल्यू लिख रहे हैं: पोस्ट को स्टार/अनस्टार करने वाले उपयोगकर्ता का आईडी और स्टार की बढ़ी हुई संख्या. अगर हमें पहले से पता है कि उपयोगकर्ता ने पोस्ट को स्टार किया है, तो हम लेन-देन के बजाय ऐटॉमिक इंक्रीमेंट ऑपरेशन का इस्तेमाल कर सकते हैं.
Kotlin
private fun onStarClicked(uid: String, key: String) { val updates: MutableMap<String, Any> = hashMapOf( "posts/$key/stars/$uid" to true, "posts/$key/starCount" to ServerValue.increment(1), "user-posts/$uid/$key/stars/$uid" to true, "user-posts/$uid/$key/starCount" to ServerValue.increment(1), ) database.updateChildren(updates) }
Java
private void onStarClicked(String uid, String key) { Map<String, Object> updates = new HashMap<>(); updates.put("posts/"+key+"/stars/"+uid, true); updates.put("posts/"+key+"/starCount", ServerValue.increment(1)); updates.put("user-posts/"+uid+"/"+key+"/stars/"+uid, true); updates.put("user-posts/"+uid+"/"+key+"/starCount", ServerValue.increment(1)); mDatabase.updateChildren(updates); }
यह कोड, लेन-देन की कार्रवाई का इस्तेमाल नहीं करता. इसलिए, अगर कोई अपडेट टकराता है, तो यह अपने-आप फिर से नहीं चलता. हालांकि, इंक्रीमेंट ऑपरेशन सीधे तौर पर डेटाबेस सर्वर पर होता है. इसलिए, टकराव की कोई संभावना नहीं होती.
अगर आपको ऐप्लिकेशन से जुड़ी समस्याओं का पता लगाना है और उन्हें ठीक करना है, तो आपको इस्तेमाल के इस उदाहरण के लिए सुरक्षा के कस्टम नियम लिखने चाहिए. जैसे, किसी उपयोगकर्ता ने ऐसी पोस्ट को स्टार किया है जिसे वह पहले ही स्टार कर चुका है.
डेटा के साथ ऑफ़लाइन काम करना
अगर किसी क्लाइंट का नेटवर्क कनेक्शन बंद हो जाता है, तो आपका ऐप्लिकेशन ठीक से काम करता रहेगा.
Firebase डेटाबेस से कनेक्ट किया गया हर क्लाइंट, उस डेटा का अपना इंटरनल वर्शन बनाए रखता है जिस पर लिसनर का इस्तेमाल किया जा रहा है या जिसे सर्वर के साथ सिंक करने के लिए फ़्लैग किया गया है. डेटा को पढ़ने या लिखने के दौरान, डेटा के इस लोकल वर्शन का इस्तेमाल सबसे पहले किया जाता है. इसके बाद, Firebase क्लाइंट उस डेटा को रिमोट डेटाबेस सर्वर और अन्य क्लाइंट के साथ "बेस्ट-एफ़र्ट" के आधार पर सिंक करता है.
इस वजह से, डेटाबेस में किए गए सभी बदलाव, सर्वर के साथ किसी भी इंटरैक्शन से पहले ही लोकल इवेंट को ट्रिगर कर देते हैं. इसका मतलब है कि नेटवर्क की स्पीड या कनेक्टिविटी कैसी भी हो, आपका ऐप्लिकेशन काम करता रहेगा.
कनेक्टिविटी फिर से चालू होने पर, आपके ऐप्लिकेशन को इवेंट का सही सेट मिलता है. इससे क्लाइंट, सर्वर की मौजूदा स्थिति के साथ सिंक हो जाता है. इसके लिए, आपको कोई कस्टम कोड लिखने की ज़रूरत नहीं होती.
हम ऑनलाइन और ऑफ़लाइन सुविधाओं के बारे में ज़्यादा जानें लेख में, ऑफ़लाइन व्यवहार के बारे में ज़्यादा जानकारी देंगे.