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

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

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

(ज़रूरी नहीं) Firebase लोकल एम्युलेटर सुइट की मदद से प्रोटोटाइप बनाएं और टेस्ट करें

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

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

रीयलटाइम डेटाबेस एम्युलेटर का इस्तेमाल करने के लिए, आपको कुछ ही चरण पूरे करने होंगे:

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

रीयल टाइम डेटाबेस और Cloud Functions के बारे में ज़्यादा जानकारी उपलब्ध है. आपको लोकल एम्युलेटर सुइट के बारे में जानकारी भी देखनी चाहिए.

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 ऑब्जेक्ट का इस्तेमाल किया जाता है, तो आपके ऑब्जेक्ट का कॉन्टेंट, नेस्ट की गई जगहों पर अपने-आप मैप हो जाता है. आम तौर पर, 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);

डेटा पढ़ें

स्थायी लिसनर के साथ डेटा पढ़ें

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

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

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

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

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() का इस्तेमाल करके एक बार पढ़ें

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

आम तौर पर, बैकएंड से डेटा के अपडेट होने की सूचना पाने के लिए, आपको ऊपर बताई गई 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()));
        }
    }
});

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

कुछ मामलों में, हो सकता है कि आप सर्वर पर अपडेट की गई वैल्यू की जांच करने के बजाय, लोकल कैश मेमोरी से वैल्यू तुरंत लौटाना चाहें. ऐसे मामलों में, 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);
}

इस उदाहरण में /posts/$postid के सभी उपयोगकर्ताओं के लिए पोस्ट वाले नोड में एक पोस्ट बनाने और getKey() की मदद से कुंजी पाने के लिए, push() का इस्तेमाल किया गया है. इसके बाद, /user-posts/$userid/$postid पर उपयोगकर्ता की पोस्ट में दूसरी एंट्री बनाने के लिए, कुंजी का इस्तेमाल किया जा सकता है.

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

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

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

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() को कॉल किया जाए.

वैल्यू के तौर पर null को तय करके भी मिटाया जा सकता है. जैसे, setValue() या updateChildren(). इस तकनीक का इस्तेमाल 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 क्लाइंट उस डेटा को रिमोट डेटाबेस सर्वर और अन्य क्लाइंट के साथ "बेहतर तरीके से" सिंक करता है.

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

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

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

अगले चरण