Android पर डेटा पढ़ें और लिखें

इस दस्तावेज़ में, Firebase डेटा को पढ़ने और उसमें डेटा जोड़ने के बारे में बुनियादी जानकारी दी गई है.

Firebase डेटा को FirebaseDatabase रेफ़रंस में लिखा जाता है और रेफ़रंस में असाइनोसाइनस लिसनर को अटैच करके उसे वापस पाया जाता है. डेटा की शुरुआती स्थिति के लिए, listener को एक बार ट्रिगर किया जाता है. इसके बाद, डेटा में बदलाव होने पर भी listener को ट्रिगर किया जाता है.

(ज़रूरी नहीं) Firebase Local Emulator Suite की मदद से प्रोटोटाइप बनाना और जांच करना

आपका ऐप्लिकेशन Realtime Database से कैसे पढ़ता है और उसमें कैसे लिखता है, इस बारे में बात करने से पहले, Realtime Database की सुविधाओं के प्रोटोटाइप बनाने और उनकी जांच करने के लिए इस्तेमाल किए जा सकने वाले टूल के बारे में बताते हैं: Firebase Local Emulator Suite. अगर आपको अलग-अलग डेटा मॉडल आज़माने हैं, सुरक्षा नियमों को ऑप्टिमाइज़ करना है या बैक-एंड के साथ इंटरैक्ट करने का सबसे किफ़ायती तरीका ढूंढना है, तो लाइव सेवाओं को डिप्लॉय किए बिना स्थानीय तौर पर काम करना एक अच्छा आइडिया हो सकता है.

Realtime Database एमुलेटर, Local Emulator Suite का हिस्सा होता है. इससे आपके ऐप्लिकेशन को, एमुलेट किए गए डेटाबेस के कॉन्टेंट और कॉन्फ़िगरेशन के साथ-साथ, एमुलेट किए गए प्रोजेक्ट के रिसॉर्स (फ़ंक्शन, अन्य डेटाबेस, और सुरक्षा नियम) के साथ इंटरैक्ट करने में मदद मिलती है.

Realtime Database एमुलेटर का इस्तेमाल करने के लिए, ये चरण पूरे करें:

  1. एम्युलेटर से कनेक्ट करने के लिए, अपने ऐप्लिकेशन के टेस्ट कॉन्फ़िगरेशन में कोड की एक लाइन जोड़ना.
  2. अपनी लोकल प्रोजेक्ट डायरेक्ट्री के रूट से, firebase emulators:start चलाएं.
  3. अपने ऐप्लिकेशन के प्रोटोटाइप कोड से कॉल करना. इसके लिए, Realtime Database प्लैटफ़ॉर्म SDK का इस्तेमाल हमेशा की तरह करें या Realtime Database REST API का इस्तेमाल करें.

Realtime Database और Cloud Functions से जुड़ा ज़्यादा जानकारी वाला वॉकथ्रू उपलब्ध है. आपको Local Emulator Suite के बारे में जानकारी भी देखनी चाहिए.

DatabaseReference पाना

डेटाबेस से डेटा पढ़ने या उसमें डेटा लिखने के लिए, आपके पास DatabaseReference का एक इंस्टेंस होना चाहिए:

Kotlin+KTX

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+KTX

@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+KTX

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+KTX

database.child("users").child(userId).child("username").setValue(name)

Java

mDatabase.child("users").child(userId).child("username").setValue(name);

डेटा पढ़ना

लगातार सुनने वाले लोगों का डेटा पढ़ना

किसी पाथ पर डेटा पढ़ने और बदलावों को सुनने के लिए, DatabaseReference में ValueEventListener जोड़ने के लिए addValueEventListener() तरीका इस्तेमाल करें.

श्रोता इवेंट कॉलबैक आम तौर पर इस्तेमाल
ValueEventListener onDataChange() किसी पाथ के पूरे कॉन्टेंट में हुए बदलावों को पढ़ना और सुनना.

onDataChange() तरीके का इस्तेमाल करके, किसी दिए गए पाथ पर मौजूद कॉन्टेंट का स्टैटिक स्नैपशॉट पढ़ा जा सकता है. यह स्नैपशॉट, इवेंट के समय मौजूद कॉन्टेंट का होता है. यह तरीका, ऑडियंस को अटैच करने पर एक बार ट्रिगर होता है. इसके बाद, जब भी डेटा में बदलाव होता है, तो यह फिर से ट्रिगर होता है. इसमें बच्चों का डेटा भी शामिल है. इवेंट कॉलबैक को एक स्नैपशॉट भेजा जाता है. इसमें उस जगह का सारा डेटा होता है. इसमें चाइल्ड डेटा भी शामिल होता है. अगर कोई डेटा नहीं है, तो स्नैपशॉट में exists() कॉल करने पर false और getValue() कॉल करने पर null दिखेगा.

यहां दिए गए उदाहरण में, डेटाबेस से किसी पोस्ट की जानकारी हासिल करने वाले सोशल ब्लॉगिंग ऐप्लिकेशन के बारे में बताया गया है:

Kotlin+KTX

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+KTX

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

Listener का इस्तेमाल करके एक बार पढ़ना

कुछ मामलों में, हो सकता है कि आप सर्वर पर अपडेट की गई वैल्यू की जांच करने के बजाय, स्थानीय कैश मेमोरी से वैल्यू तुरंत पाना चाहें. ऐसे मामलों में, addListenerForSingleValueEvent का इस्तेमाल करके, डिस्क कैश मेमोरी से तुरंत डेटा पाया जा सकता है.

यह उस डेटा के लिए फ़ायदेमंद है जिसे सिर्फ़ एक बार लोड करना होता है और जिसे बार-बार बदलने की ज़रूरत नहीं होती या जिसे सुनने की ज़रूरत नहीं होती. उदाहरण के लिए, पिछले उदाहरणों में दिया गया ब्लॉगिंग ऐप्लिकेशन, इस तरीके का इस्तेमाल करके उपयोगकर्ता की प्रोफ़ाइल को लोड करता है. ऐसा तब होता है, जब उपयोगकर्ता कोई नई पोस्ट लिखना शुरू करता है.

डेटा अपडेट करना या मिटाना

चुनिंदा फ़ील्ड अपडेट करना

किसी नोड के दूसरे चाइल्ड नोड को ओवरराइट किए बिना, एक साथ कई चाइल्ड नोड में लिखने के लिए, updateChildren() तरीके का इस्तेमाल करें.

updateChildren() को कॉल करते समय, कुंजी के लिए पाथ तय करके, कम लेवल की चाइल्ड वैल्यू अपडेट की जा सकती हैं. अगर डेटा को बेहतर तरीके से स्केल करने के लिए, एक से ज़्यादा जगहों पर सेव किया गया है, तो डेटा फ़ैन-आउट का इस्तेमाल करके, उस डेटा के सभी इंस्टेंस अपडेट किए जा सकते हैं. उदाहरण के लिए, किसी सोशल ब्लॉगिंग ऐप्लिकेशन में Post क्लास इस तरह की हो सकती है:

Kotlin+KTX

@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+KTX

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 पर उपयोगकर्ता की पोस्ट में दूसरी एंट्री बनाई जा सकती है.

इन पाथ का इस्तेमाल करके, updateChildren() को एक बार कॉल करके, JSON ट्री में एक साथ कई जगहों पर अपडेट किए जा सकते हैं. उदाहरण के लिए, यह उदाहरण दोनों जगहों पर नई पोस्ट कैसे बनाता है. इस तरह एक साथ किए गए अपडेट ऐटॉमिक होते हैं: या तो सभी अपडेट पूरे हो जाते हैं या सभी अपडेट पूरे नहीं होते.

प्रोसेस पूरी होने पर कॉलबैक जोड़ना

अगर आपको यह जानना है कि आपका डेटा कब कमिट किया गया है, तो आपके पास 'पूरा होने पर सुनने वाला' जोड़ने का विकल्प है. setValue() और updateChildren(), दोनों में एक वैकल्पिक 'पूरा होने की सूचना देने वाला फ़ंक्शन' होता है. इसे तब कॉल किया जाता है, जब डेटाबेस में डेटा को लिखने की प्रोसेस पूरी हो जाती है. अगर कॉल पूरा नहीं हो पाता है, तो listener को एक गड़बड़ी ऑब्जेक्ट भेजा जाता है. इससे पता चलता है कि कॉल पूरा न होने की वजह क्या है.

Kotlin+KTX

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+KTX

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+KTX

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 क्लाइंट उस डेटा को रिमोट डेटाबेस सर्वर और अन्य क्लाइंट के साथ "बेहतरीन तरीके" से सिंक करता है.

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

कनेक्शन फिर से चालू होने के बाद, आपके ऐप्लिकेशन को इवेंट का सही सेट मिलता है, ताकि क्लाइंट किसी कस्टम कोड को लिखे बिना, सर्वर की मौजूदा स्थिति के साथ सिंक हो सके.

हम ऑनलाइन और ऑफ़लाइन सुविधाओं के बारे में ज़्यादा जानें लेख में, ऑफ़लाइन व्यवहार के बारे में ज़्यादा बात करेंगे.

अगले चरण