এই দস্তাবেজটি 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
রেফারেন্সের একটি উদাহরণ প্রয়োজন:
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
ডেটা লিখুন
মৌলিক লেখার ক্রিয়াকলাপ
মৌলিক লেখার ক্রিয়াকলাপের জন্য, আপনি একটি নির্দিষ্ট রেফারেন্সে ডেটা সংরক্ষণ করতে setValue()
ব্যবহার করতে পারেন, সেই পথে বিদ্যমান ডেটা প্রতিস্থাপন করতে পারেন। আপনি এই পদ্ধতিটি ব্যবহার করতে পারেন:
- নিম্নরূপ উপলব্ধ JSON প্রকারের সাথে সঙ্গতিপূর্ণ পাস প্রকার:
-
String
-
Long
-
Double
-
Boolean
-
Map<String, Object>
-
List<Object>
-
- একটি কাস্টম জাভা অবজেক্ট পাস করুন, যদি ক্লাস যেটি সংজ্ঞায়িত করে তার একটি ডিফল্ট কনস্ট্রাক্টর থাকে যা কোনও আর্গুমেন্ট নেয় না এবং বৈশিষ্ট্যগুলি বরাদ্দ করার জন্য পাবলিক গেটার থাকে।
আপনি যদি একটি জাভা অবজেক্ট ব্যবহার করেন, আপনার অবজেক্টের বিষয়বস্তু স্বয়ংক্রিয়ভাবে নেস্টেড ফ্যাশনে চাইল্ড লোকেশনে ম্যাপ করা হয়। একটি জাভা অবজেক্ট ব্যবহার করা সাধারণত আপনার কোডকে আরও পঠনযোগ্য এবং বজায় রাখা সহজ করে তোলে। উদাহরণস্বরূপ, যদি আপনার কাছে একটি মৌলিক ব্যবহারকারী প্রোফাইল সহ একটি অ্যাপ থাকে, তাহলে আপনার User
অবজেক্টটি নিম্নরূপ দেখতে পারে:
@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. }
@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()
সহ একজন ব্যবহারকারী যুক্ত করতে পারেন:
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
এইভাবে setValue()
ব্যবহার করে যেকোন চাইল্ড নোড সহ নির্দিষ্ট স্থানে ডেটা ওভাররাইট করে। যাইহোক, আপনি এখনও সম্পূর্ণ বস্তুটি পুনরায় লেখা ছাড়াই একটি শিশুকে আপডেট করতে পারেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইল আপডেট করার অনুমতি দিতে চান তবে আপনি নিম্নরূপ ব্যবহারকারীর নাম আপডেট করতে পারেন:
database.child("users").child(userId).child("username").setValue(name)
mDatabase.child("users").child(userId).child("username").setValue(name);
ডেটা পড়ুন
অবিরাম শ্রোতাদের সাথে ডেটা পড়ুন
একটি পাথে ডেটা পড়তে এবং পরিবর্তনগুলি শুনতে, একটি DatabaseReference
একটি ValueEventListener
যোগ করতে addValueEventListener()
পদ্ধতিটি ব্যবহার করুন।
শ্রোতা | ইভেন্ট কলব্যাক | সাধারণ ব্যবহার |
---|---|---|
ValueEventListener | onDataChange() | একটি পথের সম্পূর্ণ বিষয়বস্তুর পরিবর্তনের জন্য পড়ুন এবং শুনুন। |
আপনি একটি প্রদত্ত পাথে বিষয়বস্তুর একটি স্ট্যাটিক স্ন্যাপশট পড়ার জন্য onDataChange()
পদ্ধতিটি ব্যবহার করতে পারেন, যেমনটি ইভেন্টের সময় বিদ্যমান ছিল। এই পদ্ধতিটি একবার ট্রিগার করা হয় যখন শ্রোতা সংযুক্ত থাকে এবং আবার প্রতিবার শিশু সহ ডেটা পরিবর্তিত হয়। ইভেন্ট কলব্যাক একটি স্ন্যাপশট পাস করা হয় যেখানে শিশু ডেটা সহ সেই অবস্থানের সমস্ত ডেটা রয়েছে৷ যদি কোনো ডেটা না থাকে, তাহলে আপনি exists()
কল করলে স্ন্যাপশট false
হবে এবং যখন getValue()
কল করবেন তখন null
।
নিম্নলিখিত উদাহরণটি একটি সামাজিক ব্লগিং অ্যাপ্লিকেশন প্রদর্শন করে যা ডাটাবেস থেকে একটি পোস্টের বিবরণ পুনরুদ্ধার করে:
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)
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()
কল করা ডেটার জাভা অবজেক্ট উপস্থাপনা প্রদান করে। অবস্থানে কোনো ডেটা না থাকলে, getValue()
কল করলে null
ফেরত আসে।
এই উদাহরণে, ValueEventListener
onCancelled()
পদ্ধতিটিও সংজ্ঞায়িত করে যা রিড বাতিল হলে বলা হয়। উদাহরণস্বরূপ, যদি ক্লায়েন্টের ফায়ারবেস ডাটাবেস অবস্থান থেকে পড়ার অনুমতি না থাকে তবে একটি রিড বাতিল করা যেতে পারে। এই পদ্ধতিটি একটি DatabaseError
অবজেক্ট পাস করা হয়েছে যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।
একবার ডেটা পড়ুন
get() ব্যবহার করে একবার পড়ুন
আপনার অ্যাপ অনলাইন হোক বা অফলাইন হোক ডাটাবেস সার্ভারের সাথে মিথস্ক্রিয়া পরিচালনা করার জন্য SDK ডিজাইন করা হয়েছে।
সাধারণত, ব্যাকএন্ড থেকে ডেটার আপডেটের বিজ্ঞপ্তি পেতে ডেটা পড়ার জন্য আপনার উপরে বর্ণিত ValueEventListener
কৌশলগুলি ব্যবহার করা উচিত। শ্রোতা কৌশলগুলি আপনার ব্যবহার এবং বিলিং হ্রাস করে এবং আপনার ব্যবহারকারীদের অনলাইন এবং অফলাইনে যাওয়ার সাথে সাথে তাদের সেরা অভিজ্ঞতা দেওয়ার জন্য অপ্টিমাইজ করা হয়েছে৷
আপনার যদি শুধুমাত্র একবার ডেটার প্রয়োজন হয়, আপনি ডাটাবেস থেকে ডেটার একটি স্ন্যাপশট পেতে get()
ব্যবহার করতে পারেন। কোনো কারণে get()
সার্ভারের মান ফেরত দিতে অক্ষম হলে, ক্লায়েন্ট স্থানীয় স্টোরেজ ক্যাশে অনুসন্ধান করবে এবং মানটি এখনও খুঁজে না পাওয়া গেলে একটি ত্রুটি ফেরত দেবে।
get()
এর অপ্রয়োজনীয় ব্যবহার ব্যান্ডউইথের ব্যবহার বাড়াতে পারে এবং কর্মক্ষমতা হারাতে পারে, যা উপরে দেখানো হিসাবে রিয়েলটাইম লিসেনার ব্যবহার করে প্রতিরোধ করা যেতে পারে।
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
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
ক্লাস এই রকম থাকতে পারে:
@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, ) } }
@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; } }
একটি পোস্ট তৈরি করতে এবং একই সাথে সাম্প্রতিক কার্যকলাপ ফিড এবং পোস্টিং ব্যবহারকারীর কার্যকলাপ ফিডে আপডেট করতে, ব্লগিং অ্যাপ্লিকেশনটি এইরকম কোড ব্যবহার করে:
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) }
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()
উভয়ই একটি ঐচ্ছিক সমাপ্তি শ্রোতা নেয় যা ডাটাবেসের সাথে লেখা সফলভাবে প্রতিশ্রুতিবদ্ধ হলে বলা হয়। যদি কলটি ব্যর্থ হয়, তাহলে শ্রোতাকে একটি ত্রুটি অবজেক্ট পাস করা হয় যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
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
উল্লেখ করেও মুছে ফেলতে পারেন। আপনি একটি একক API কলে একাধিক শিশু মুছে ফেলার জন্য updateChildren()
এর সাথে এই কৌশলটি ব্যবহার করতে পারেন।
শ্রোতাদের বিচ্ছিন্ন করুন
আপনার ফায়ারবেস ডাটাবেস রেফারেন্সে removeEventListener()
পদ্ধতিতে কল করার মাধ্যমে কলব্যাকগুলি সরানো হয়।
যদি কোনও শ্রোতাকে একটি ডেটা অবস্থানে একাধিকবার যুক্ত করা হয়, তবে প্রতিটি ইভেন্টের জন্য এটি একাধিকবার বলা হয় এবং এটি সম্পূর্ণরূপে অপসারণ করতে আপনাকে অবশ্যই একই সংখ্যক বার আলাদা করতে হবে।
একটি অভিভাবক শ্রোতাকে removeEventListener()
কল করা স্বয়ংক্রিয়ভাবে তার চাইল্ড নোডগুলিতে নিবন্ধিত শ্রোতাদের সরিয়ে দেয় না; removeEventListener()
যেকোন শিশু শ্রোতাদের কলব্যাক অপসারণের জন্যও ডাকতে হবে।
লেনদেন হিসাবে ডেটা সংরক্ষণ করুন
ক্রমবর্ধমান কাউন্টারগুলির মতো সমসাময়িক পরিবর্তন দ্বারা দূষিত হতে পারে এমন ডেটা নিয়ে কাজ করার সময়, আপনি একটি লেনদেন অপারেশন ব্যবহার করতে পারেন। আপনি এই অপারেশনটিকে দুটি আর্গুমেন্ট দেন: একটি আপডেট ফাংশন এবং একটি ঐচ্ছিক সমাপ্তি কলব্যাক৷ আপডেট ফাংশন একটি যুক্তি হিসাবে ডেটার বর্তমান অবস্থা নেয় এবং আপনি লিখতে চান এমন নতুন পছন্দসই অবস্থা প্রদান করে। আপনার নতুন মান সফলভাবে লেখার আগে যদি অন্য ক্লায়েন্ট লোকেশনে লেখে, আপনার আপডেট ফাংশনটি নতুন বর্তমান মান সহ আবার কল করা হবে এবং লেখাটি পুনরায় চেষ্টা করা হবে।
উদাহরণস্বরূপ, সোশ্যাল ব্লগিং অ্যাপের উদাহরণে, আপনি ব্যবহারকারীদের পোস্ট স্টার এবং আনস্টার করার অনুমতি দিতে পারেন এবং একটি পোস্ট কতগুলি স্টার পেয়েছে তার ট্র্যাক রাখতে পারেন:
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!!) } }) }
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); } }); }
একটি লেনদেন ব্যবহার করা স্টার গণনাকে ভুল হতে বাধা দেয় যদি একাধিক ব্যবহারকারী একই পোস্টে একই সময়ে স্টার করেন বা ক্লায়েন্টের পুরানো ডেটা থাকে। যদি লেনদেন প্রত্যাখ্যান করা হয়, সার্ভার ক্লায়েন্টকে বর্তমান মান ফেরত দেয়, যা আপডেট করা মান দিয়ে আবার লেনদেন চালায়। লেনদেন গৃহীত না হওয়া পর্যন্ত বা অনেক প্রচেষ্টা করা না হওয়া পর্যন্ত এটি পুনরাবৃত্তি হয়।
পারমাণবিক সার্ভার-সাইড বৃদ্ধি
উপরোক্ত ব্যবহারের ক্ষেত্রে আমরা ডাটাবেসে দুটি মান লিখছি: পোস্টে তারকাচিহ্নিত/তারামুক্তকারী ব্যবহারকারীর আইডি এবং বর্ধিত তারকা গণনা। যদি আমরা ইতিমধ্যেই জানি যে ব্যবহারকারী পোস্টটি তারকাচিহ্নিত করছে, আমরা একটি লেনদেনের পরিবর্তে একটি পারমাণবিক বৃদ্ধি অপারেশন ব্যবহার করতে পারি।
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) }
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 ডেটা একটি 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
রেফারেন্সের একটি উদাহরণ প্রয়োজন:
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
ডেটা লিখুন
মৌলিক লেখার ক্রিয়াকলাপ
মৌলিক লেখার ক্রিয়াকলাপের জন্য, আপনি একটি নির্দিষ্ট রেফারেন্সে ডেটা সংরক্ষণ করতে setValue()
ব্যবহার করতে পারেন, সেই পথে বিদ্যমান ডেটা প্রতিস্থাপন করতে পারেন। আপনি এই পদ্ধতিটি ব্যবহার করতে পারেন:
- নিম্নরূপ উপলব্ধ JSON প্রকারের সাথে সঙ্গতিপূর্ণ পাস প্রকার:
-
String
-
Long
-
Double
-
Boolean
-
Map<String, Object>
-
List<Object>
-
- একটি কাস্টম জাভা অবজেক্ট পাস করুন, যদি ক্লাস যেটি সংজ্ঞায়িত করে তার একটি ডিফল্ট কনস্ট্রাক্টর থাকে যা কোনও আর্গুমেন্ট নেয় না এবং বৈশিষ্ট্যগুলি বরাদ্দ করার জন্য পাবলিক গেটার থাকে।
আপনি একটি জাভা অবজেক্ট ব্যবহার করলে, আপনার অবজেক্টের বিষয়বস্তু স্বয়ংক্রিয়ভাবে নেস্টেড ফ্যাশনে চাইল্ড লোকেশনে ম্যাপ করা হয়। একটি জাভা অবজেক্ট ব্যবহার করা সাধারণত আপনার কোডকে আরও পঠনযোগ্য এবং বজায় রাখা সহজ করে তোলে। উদাহরণস্বরূপ, যদি আপনার কাছে একটি মৌলিক ব্যবহারকারী প্রোফাইল সহ একটি অ্যাপ থাকে, তাহলে আপনার User
অবজেক্টটি নিম্নরূপ দেখতে পারে:
@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. }
@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()
সহ একজন ব্যবহারকারী যুক্ত করতে পারেন:
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
এইভাবে setValue()
ব্যবহার করে যেকোন চাইল্ড নোড সহ নির্দিষ্ট স্থানে ডেটা ওভাররাইট করে। যাইহোক, আপনি এখনও সম্পূর্ণ বস্তুটি পুনরায় লেখা ছাড়াই একটি শিশুকে আপডেট করতে পারেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইল আপডেট করার অনুমতি দিতে চান তবে আপনি নিম্নরূপ ব্যবহারকারীর নাম আপডেট করতে পারেন:
database.child("users").child(userId).child("username").setValue(name)
mDatabase.child("users").child(userId).child("username").setValue(name);
ডেটা পড়ুন
অবিরাম শ্রোতাদের সাথে ডেটা পড়ুন
একটি পাথে ডেটা পড়তে এবং পরিবর্তনগুলি শুনতে, একটি DatabaseReference
একটি ValueEventListener
যোগ করতে addValueEventListener()
পদ্ধতিটি ব্যবহার করুন।
শ্রোতা | ইভেন্ট কলব্যাক | সাধারণ ব্যবহার |
---|---|---|
ValueEventListener | onDataChange() | একটি পথের সম্পূর্ণ বিষয়বস্তুর পরিবর্তনের জন্য পড়ুন এবং শুনুন। |
আপনি একটি প্রদত্ত পাথে বিষয়বস্তুর একটি স্ট্যাটিক স্ন্যাপশট পড়ার জন্য onDataChange()
পদ্ধতিটি ব্যবহার করতে পারেন, যেমনটি ইভেন্টের সময় বিদ্যমান ছিল। এই পদ্ধতিটি একবার ট্রিগার করা হয় যখন শ্রোতা সংযুক্ত থাকে এবং আবার প্রতিবার শিশু সহ ডেটা পরিবর্তিত হয়। ইভেন্ট কলব্যাক একটি স্ন্যাপশট পাস করা হয় যেখানে শিশু ডেটা সহ সেই অবস্থানের সমস্ত ডেটা রয়েছে৷ যদি কোনো ডেটা না থাকে, তাহলে আপনি exists()
কল করলে স্ন্যাপশট false
হবে এবং যখন getValue()
কল করবেন তখন null
।
নিম্নলিখিত উদাহরণটি একটি সামাজিক ব্লগিং অ্যাপ্লিকেশন প্রদর্শন করে যা ডাটাবেস থেকে একটি পোস্টের বিবরণ পুনরুদ্ধার করে:
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)
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()
কল করা ডেটার জাভা অবজেক্ট উপস্থাপনা প্রদান করে। অবস্থানে কোনো ডেটা না থাকলে, getValue()
কল করলে null
ফেরত আসে।
এই উদাহরণে, ValueEventListener
onCancelled()
পদ্ধতিটিও সংজ্ঞায়িত করে যা রিড বাতিল হলে বলা হয়। উদাহরণস্বরূপ, যদি ক্লায়েন্টের ফায়ারবেস ডাটাবেস অবস্থান থেকে পড়ার অনুমতি না থাকে তবে একটি রিড বাতিল করা যেতে পারে। এই পদ্ধতিটি একটি DatabaseError
অবজেক্ট পাস করা হয়েছে যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।
একবার ডেটা পড়ুন
get() ব্যবহার করে একবার পড়ুন
আপনার অ্যাপ অনলাইন হোক বা অফলাইন হোক ডাটাবেস সার্ভারের সাথে মিথস্ক্রিয়া পরিচালনা করার জন্য SDK ডিজাইন করা হয়েছে।
সাধারণত, ব্যাকএন্ড থেকে ডেটার আপডেটের বিজ্ঞপ্তি পেতে ডেটা পড়ার জন্য আপনার উপরে বর্ণিত ValueEventListener
কৌশলগুলি ব্যবহার করা উচিত। শ্রোতা কৌশলগুলি আপনার ব্যবহার এবং বিলিং হ্রাস করে এবং আপনার ব্যবহারকারীদের অনলাইন এবং অফলাইনে যাওয়ার সাথে সাথে তাদের সেরা অভিজ্ঞতা দেওয়ার জন্য অপ্টিমাইজ করা হয়েছে৷
আপনার যদি শুধুমাত্র একবার ডেটার প্রয়োজন হয়, আপনি ডাটাবেস থেকে ডেটার একটি স্ন্যাপশট পেতে get()
ব্যবহার করতে পারেন। কোনো কারণে get()
সার্ভারের মান ফেরত দিতে অক্ষম হলে, ক্লায়েন্ট স্থানীয় স্টোরেজ ক্যাশে অনুসন্ধান করবে এবং মানটি এখনও খুঁজে না পাওয়া গেলে একটি ত্রুটি ফেরত দেবে।
get()
এর অপ্রয়োজনীয় ব্যবহার ব্যান্ডউইথের ব্যবহার বাড়াতে পারে এবং কর্মক্ষমতা হারাতে পারে, যা উপরে দেখানো হিসাবে রিয়েলটাইম লিসেনার ব্যবহার করে প্রতিরোধ করা যেতে পারে।
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
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
ক্লাস এই রকম থাকতে পারে:
@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, ) } }
@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; } }
একটি পোস্ট তৈরি করতে এবং একই সাথে সাম্প্রতিক কার্যকলাপ ফিড এবং পোস্টিং ব্যবহারকারীর কার্যকলাপ ফিডে আপডেট করতে, ব্লগিং অ্যাপ্লিকেশনটি এইরকম কোড ব্যবহার করে:
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) }
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()
উভয়ই একটি ঐচ্ছিক সমাপ্তি শ্রোতা নেয় যা ডাটাবেসের সাথে লেখা সফলভাবে প্রতিশ্রুতিবদ্ধ হলে বলা হয়। যদি কলটি ব্যর্থ হয়, তাহলে শ্রোতাকে একটি ত্রুটি অবজেক্ট পাস করা হয় যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
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
উল্লেখ করেও মুছে ফেলতে পারেন। আপনি একটি একক API কলে একাধিক শিশু মুছে ফেলার জন্য updateChildren()
এর সাথে এই কৌশলটি ব্যবহার করতে পারেন।
শ্রোতাদের বিচ্ছিন্ন করুন
আপনার ফায়ারবেস ডাটাবেস রেফারেন্সে removeEventListener()
পদ্ধতিতে কল করার মাধ্যমে কলব্যাকগুলি সরানো হয়।
যদি কোনও শ্রোতাকে একটি ডেটা অবস্থানে একাধিকবার যুক্ত করা হয়, তবে প্রতিটি ইভেন্টের জন্য এটি একাধিকবার বলা হয় এবং এটি সম্পূর্ণরূপে অপসারণ করতে আপনাকে অবশ্যই একই সংখ্যক বার আলাদা করতে হবে।
একটি অভিভাবক শ্রোতাকে removeEventListener()
কল করা স্বয়ংক্রিয়ভাবে তার চাইল্ড নোডগুলিতে নিবন্ধিত শ্রোতাদের সরিয়ে দেয় না; removeEventListener()
যেকোন শিশু শ্রোতাদের কলব্যাক অপসারণের জন্যও ডাকতে হবে।
লেনদেন হিসাবে ডেটা সংরক্ষণ করুন
ক্রমবর্ধমান কাউন্টারগুলির মতো সমসাময়িক পরিবর্তন দ্বারা দূষিত হতে পারে এমন ডেটা নিয়ে কাজ করার সময়, আপনি একটি লেনদেন অপারেশন ব্যবহার করতে পারেন। আপনি এই অপারেশনটিকে দুটি আর্গুমেন্ট দেন: একটি আপডেট ফাংশন এবং একটি ঐচ্ছিক সমাপ্তি কলব্যাক৷ আপডেট ফাংশন একটি যুক্তি হিসাবে ডেটার বর্তমান অবস্থা নেয় এবং আপনি লিখতে চান এমন নতুন পছন্দসই অবস্থা প্রদান করে। আপনার নতুন মান সফলভাবে লেখার আগে যদি অন্য ক্লায়েন্ট লোকেশনে লেখে, আপনার আপডেট ফাংশনটি নতুন বর্তমান মান সহ আবার কল করা হবে এবং লেখাটি পুনরায় চেষ্টা করা হবে।
উদাহরণস্বরূপ, সোশ্যাল ব্লগিং অ্যাপের উদাহরণে, আপনি ব্যবহারকারীদের পোস্ট স্টার এবং আনস্টার করার অনুমতি দিতে পারেন এবং একটি পোস্ট কতগুলি স্টার পেয়েছে তার ট্র্যাক রাখতে পারেন:
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!!) } }) }
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); } }); }
একটি লেনদেন ব্যবহার করা স্টার গণনাকে ভুল হতে বাধা দেয় যদি একাধিক ব্যবহারকারী একই পোস্টে একই সময়ে স্টার করেন বা ক্লায়েন্টের পুরানো ডেটা থাকে। যদি লেনদেন প্রত্যাখ্যান করা হয়, সার্ভার ক্লায়েন্টকে বর্তমান মান ফেরত দেয়, যা আপডেট করা মান দিয়ে আবার লেনদেন চালায়। লেনদেন গৃহীত না হওয়া পর্যন্ত বা অনেক প্রচেষ্টা করা না হওয়া পর্যন্ত এটি পুনরাবৃত্তি হয়।
পারমাণবিক সার্ভার-সাইড বৃদ্ধি
উপরোক্ত ব্যবহারের ক্ষেত্রে আমরা ডাটাবেসে দুটি মান লিখছি: পোস্টে তারকাচিহ্নিত/তারামুক্তকারী ব্যবহারকারীর আইডি এবং বর্ধিত তারকা গণনা। যদি আমরা ইতিমধ্যেই জানি যে ব্যবহারকারী পোস্টটি তারকাচিহ্নিত করছে, আমরা একটি লেনদেনের পরিবর্তে একটি পারমাণবিক বৃদ্ধি অপারেশন ব্যবহার করতে পারি।
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) }
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 ডেটা একটি 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
রেফারেন্সের একটি উদাহরণ প্রয়োজন:
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
ডেটা লিখুন
মৌলিক লেখার ক্রিয়াকলাপ
মৌলিক লেখার ক্রিয়াকলাপের জন্য, আপনি একটি নির্দিষ্ট রেফারেন্সে ডেটা সংরক্ষণ করতে setValue()
ব্যবহার করতে পারেন, সেই পথে বিদ্যমান ডেটা প্রতিস্থাপন করতে পারেন। আপনি এই পদ্ধতিটি ব্যবহার করতে পারেন:
- নিম্নরূপ উপলব্ধ JSON প্রকারের সাথে সঙ্গতিপূর্ণ পাস প্রকার:
-
String
-
Long
-
Double
-
Boolean
-
Map<String, Object>
-
List<Object>
-
- একটি কাস্টম জাভা অবজেক্ট পাস করুন, যদি ক্লাস যেটি সংজ্ঞায়িত করে তার একটি ডিফল্ট কনস্ট্রাক্টর থাকে যা কোনও আর্গুমেন্ট নেয় না এবং বৈশিষ্ট্যগুলি বরাদ্দ করার জন্য পাবলিক গেটার থাকে।
আপনি যদি একটি জাভা অবজেক্ট ব্যবহার করেন, আপনার অবজেক্টের বিষয়বস্তু স্বয়ংক্রিয়ভাবে নেস্টেড ফ্যাশনে চাইল্ড লোকেশনে ম্যাপ করা হয়। একটি জাভা অবজেক্ট ব্যবহার করা সাধারণত আপনার কোডকে আরও পঠনযোগ্য এবং বজায় রাখা সহজ করে তোলে। উদাহরণস্বরূপ, যদি আপনার কাছে একটি মৌলিক ব্যবহারকারী প্রোফাইল সহ একটি অ্যাপ থাকে, তাহলে আপনার User
অবজেক্টটি নিম্নরূপ দেখতে পারে:
@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. }
@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()
সহ একজন ব্যবহারকারী যুক্ত করতে পারেন:
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
এইভাবে setValue()
ব্যবহার করে যেকোন চাইল্ড নোড সহ নির্দিষ্ট স্থানে ডেটা ওভাররাইট করে। যাইহোক, আপনি এখনও সম্পূর্ণ বস্তুটি পুনরায় লেখা ছাড়াই একটি শিশুকে আপডেট করতে পারেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইল আপডেট করার অনুমতি দিতে চান তবে আপনি নিম্নরূপ ব্যবহারকারীর নাম আপডেট করতে পারেন:
database.child("users").child(userId).child("username").setValue(name)
mDatabase.child("users").child(userId).child("username").setValue(name);
ডেটা পড়ুন
অবিরাম শ্রোতাদের সাথে ডেটা পড়ুন
একটি পাথে ডেটা পড়তে এবং পরিবর্তনগুলি শুনতে, একটি DatabaseReference
একটি ValueEventListener
যোগ করতে addValueEventListener()
পদ্ধতিটি ব্যবহার করুন।
শ্রোতা | ইভেন্ট কলব্যাক | সাধারণ ব্যবহার |
---|---|---|
ValueEventListener | onDataChange() | একটি পথের সম্পূর্ণ বিষয়বস্তুর পরিবর্তনের জন্য পড়ুন এবং শুনুন। |
আপনি একটি প্রদত্ত পাথে বিষয়বস্তুর একটি স্ট্যাটিক স্ন্যাপশট পড়ার জন্য onDataChange()
পদ্ধতিটি ব্যবহার করতে পারেন, যেমনটি ইভেন্টের সময় বিদ্যমান ছিল। এই পদ্ধতিটি একবার ট্রিগার করা হয় যখন শ্রোতা সংযুক্ত থাকে এবং আবার প্রতিবার শিশু সহ ডেটা পরিবর্তিত হয়। ইভেন্ট কলব্যাক একটি স্ন্যাপশট পাস করা হয় যেখানে শিশু ডেটা সহ সেই অবস্থানের সমস্ত ডেটা রয়েছে৷ যদি কোনো ডেটা না থাকে, তাহলে আপনি exists()
কল করলে স্ন্যাপশট false
হবে এবং যখন getValue()
কল করবেন তখন null
।
নিম্নলিখিত উদাহরণটি একটি সামাজিক ব্লগিং অ্যাপ্লিকেশন প্রদর্শন করে যা ডাটাবেস থেকে একটি পোস্টের বিবরণ পুনরুদ্ধার করে:
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)
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()
কল করা ডেটার জাভা অবজেক্ট উপস্থাপনা প্রদান করে। অবস্থানে কোনো ডেটা না থাকলে, getValue()
কল করলে null
ফেরত আসে।
এই উদাহরণে, ValueEventListener
onCancelled()
পদ্ধতিটিও সংজ্ঞায়িত করে যা রিড বাতিল হলে বলা হয়। উদাহরণস্বরূপ, যদি ক্লায়েন্টের ফায়ারবেস ডাটাবেস অবস্থান থেকে পড়ার অনুমতি না থাকে তবে একটি রিড বাতিল করা যেতে পারে। এই পদ্ধতিটি একটি DatabaseError
অবজেক্ট পাস করা হয়েছে যা নির্দেশ করে যে কেন ব্যর্থতা ঘটেছে।
একবার ডেটা পড়ুন
get() ব্যবহার করে একবার পড়ুন
আপনার অ্যাপ অনলাইন হোক বা অফলাইন হোক ডাটাবেস সার্ভারের সাথে মিথস্ক্রিয়া পরিচালনা করার জন্য SDK ডিজাইন করা হয়েছে।
সাধারণত, ব্যাকএন্ড থেকে ডেটার আপডেটের বিজ্ঞপ্তি পেতে ডেটা পড়ার জন্য আপনার উপরে বর্ণিত ValueEventListener
কৌশলগুলি ব্যবহার করা উচিত। শ্রোতা কৌশলগুলি আপনার ব্যবহার এবং বিলিং হ্রাস করে এবং আপনার ব্যবহারকারীদের অনলাইন এবং অফলাইনে যাওয়ার সাথে সাথে তাদের সেরা অভিজ্ঞতা দেওয়ার জন্য অপ্টিমাইজ করা হয়েছে৷
আপনার যদি শুধুমাত্র একবার ডেটার প্রয়োজন হয়, আপনি ডাটাবেস থেকে ডেটার একটি স্ন্যাপশট পেতে get()
ব্যবহার করতে পারেন। কোনো কারণে get()
সার্ভারের মান ফেরত দিতে অক্ষম হলে, ক্লায়েন্ট স্থানীয় স্টোরেজ ক্যাশে অনুসন্ধান করবে এবং মানটি এখনও খুঁজে না পাওয়া গেলে একটি ত্রুটি ফেরত দেবে।
get()
এর অপ্রয়োজনীয় ব্যবহার ব্যান্ডউইথের ব্যবহার বাড়াতে পারে এবং কর্মক্ষমতা হারাতে পারে, যা উপরে দেখানো হিসাবে রিয়েলটাইম লিসেনার ব্যবহার করে প্রতিরোধ করা যেতে পারে।
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
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
ক্লাস এই রকম থাকতে পারে:
@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, ) } }
@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; } }
একটি পোস্ট তৈরি করতে এবং একই সাথে সাম্প্রতিক কার্যকলাপ ফিড এবং পোস্টিং ব্যবহারকারীর কার্যকলাপ ফিডে আপডেট করতে, ব্লগিং অ্যাপ্লিকেশনটি এইরকম কোড ব্যবহার করে:
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) }
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()
উভয়ই একটি al চ্ছিক সমাপ্তি শ্রোতা নেন যা ডাটাবেসে সফলভাবে প্রতিশ্রুতিবদ্ধ হওয়ার সময় বলা হয়। যদি কলটি ব্যর্থ হয় তবে শ্রোতাদের একটি ত্রুটি অবজেক্টটি পাস করা হয় যা ব্যর্থতাটি কেন ঘটেছে তা নির্দেশ করে।
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
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()
সহ এই কৌশলটি ব্যবহার করতে পারেন।
শ্রোতাদের বিচ্ছিন্ন করুন
আপনার ফায়ারবেস ডাটাবেস রেফারেন্সে removeEventListener()
পদ্ধতিটি কল করে কলব্যাকগুলি সরানো হয়।
যদি কোনও শ্রোতাকে কোনও ডেটা স্থানে একাধিকবার যুক্ত করা হয়, তবে এটি প্রতিটি ইভেন্টের জন্য একাধিকবার বলা হয় এবং এটি সম্পূর্ণরূপে অপসারণ করতে আপনাকে অবশ্যই এটি একই সংখ্যক বার আলাদা করতে হবে।
পিতামাতার শ্রোতার কাছে removeEventListener()
কল করা স্বয়ংক্রিয়ভাবে তার শিশু নোডগুলিতে নিবন্ধিত শ্রোতাদের অপসারণ করে না; কলব্যাকটি অপসারণ করতে কোনও শিশু শ্রোতাদের কাছে removeEventListener()
অবশ্যই কল করতে হবে।
লেনদেন হিসাবে ডেটা সংরক্ষণ করুন
ইনক্রিমেন্টাল কাউন্টারগুলির মতো সমবর্তী পরিবর্তনগুলি দ্বারা দূষিত হতে পারে এমন ডেটাগুলির সাথে কাজ করার সময়, আপনি একটি লেনদেন অপারেশন ব্যবহার করতে পারেন। আপনি এই অপারেশন দুটি যুক্তি দিন: একটি আপডেট ফাংশন এবং একটি al চ্ছিক সমাপ্তি কলব্যাক। আপডেট ফাংশনটি ডেটাটির বর্তমান অবস্থাটিকে যুক্তি হিসাবে গ্রহণ করে এবং আপনি লিখতে চান এমন নতুন কাঙ্ক্ষিত রাষ্ট্রকে ফিরিয়ে দেয়। যদি অন্য কোনও ক্লায়েন্ট আপনার নতুন মানটি সফলভাবে লেখার আগে অবস্থানটিতে লেখেন তবে আপনার আপডেট ফাংশনটি নতুন বর্তমান মান দিয়ে আবার ডাকা হবে এবং লেখাটি আবার চেষ্টা করা হয়েছে।
উদাহরণস্বরূপ, উদাহরণস্বরূপ সামাজিক ব্লগিং অ্যাপে, আপনি ব্যবহারকারীদের স্টার এবং আনস্টার পোস্টগুলি করতে এবং কোনও পোস্ট নিম্নলিখিত হিসাবে কতগুলি তারা পেয়েছেন তা ট্র্যাক রাখতে পারবেন:
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!!) } }) }
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); } }); }
একটি লেনদেন ব্যবহার করে স্টার গণনাগুলি ভুল হতে বাধা দেয় যদি একাধিক ব্যবহারকারী একই সময়ে একই পোস্টটি স্টার করে বা ক্লায়েন্টের বাসি ডেটা থাকে। যদি লেনদেনটি প্রত্যাখ্যান করা হয় তবে সার্ভারটি ক্লায়েন্টের কাছে বর্তমান মানটি ফেরত দেয়, যা আপডেট মান সহ আবার লেনদেন চালায়। লেনদেনটি গৃহীত না হওয়া বা অনেকগুলি প্রচেষ্টা না হওয়া পর্যন্ত এটি পুনরাবৃত্তি করে।
পারমাণবিক সার্ভার-সাইড ইনক্রিমেন্টস
উপরোক্ত ব্যবহারের ক্ষেত্রে আমরা ডাটাবেসে দুটি মান লিখছি: ব্যবহারকারী যিনি পোস্টটি স্টার/আনস্টারস এবং ইনক্রিমেন্টেড স্টার কাউন্টের আইডি। যদি আমরা ইতিমধ্যে জানি যে ব্যবহারকারী পোস্টটি অভিনয় করছেন, আমরা লেনদেনের পরিবর্তে একটি পারমাণবিক ইনক্রিমেন্ট অপারেশন ব্যবহার করতে পারি।
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) }
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); }
এই কোডটি কোনও লেনদেন অপারেশন ব্যবহার করে না, সুতরাং কোনও বিরোধী আপডেট থাকলে এটি স্বয়ংক্রিয়ভাবে পুনরায় রান পায় না। তবে, যেহেতু ইনক্রিমেন্ট অপারেশনটি সরাসরি ডাটাবেস সার্ভারে ঘটে, তাই কোনও বিরোধের সম্ভাবনা নেই।
আপনি যদি অ্যাপ্লিকেশন-নির্দিষ্ট দ্বন্দ্বগুলি সনাক্ত এবং প্রত্যাখ্যান করতে চান, যেমন কোনও ব্যবহারকারী যেমন তারা ইতিমধ্যে অভিনয় করেছেন এমন একটি পোস্ট অভিনয় করেছেন, আপনার সেই ব্যবহারের ক্ষেত্রে কাস্টম সুরক্ষা বিধি লিখতে হবে।
অফলাইনে ডেটা নিয়ে কাজ করুন
যদি কোনও ক্লায়েন্ট তার নেটওয়ার্ক সংযোগটি হারিয়ে ফেলে তবে আপনার অ্যাপ্লিকেশনটি সঠিকভাবে কাজ করা চালিয়ে যাবে।
ফায়ারবেস ডাটাবেসের সাথে সংযুক্ত প্রতিটি ক্লায়েন্ট শ্রোতাদের ব্যবহার করা হচ্ছে এমন কোনও ডেটার নিজস্ব অভ্যন্তরীণ সংস্করণ বজায় রাখে বা যা সার্ভারের সাথে সিঙ্কে রাখার জন্য পতাকাযুক্ত হয়। যখন ডেটা পড়া বা লিখিত হয়, তখন ডেটাটির এই স্থানীয় সংস্করণটি প্রথমে ব্যবহৃত হয়। ফায়ারবেস ক্লায়েন্ট তারপরে সেই ডেটা দূরবর্তী ডাটাবেস সার্ভারগুলির সাথে এবং অন্যান্য ক্লায়েন্টদের সাথে "সেরা-প্রচেষ্টা" ভিত্তিতে সিঙ্ক্রোনাইজ করে।
ফলস্বরূপ, সমস্ত সার্ভারের সাথে কোনও মিথস্ক্রিয়া করার আগে ডাটাবেস স্থানীয় ইভেন্টগুলি ট্রিগার করে। এর অর্থ আপনার অ্যাপ্লিকেশনটি নেটওয়ার্কের বিলম্ব বা সংযোগ নির্বিশেষে প্রতিক্রিয়াশীল থেকে যায়।
সংযোগটি পুনরায় প্রতিষ্ঠিত হয়ে গেলে, আপনার অ্যাপ্লিকেশনটি ইভেন্টগুলির উপযুক্ত সেটটি গ্রহণ করে যাতে ক্লায়েন্ট কোনও কাস্টম কোড না লিখে বর্তমান সার্ভার স্টেটের সাথে সিঙ্ক করে।
আমরা অনলাইন এবং অফলাইন ক্ষমতা সম্পর্কে আরও শিখতে অফলাইন আচরণ সম্পর্কে আরও কথা বলব।
পরবর্তী পদক্ষেপ
- ডেটা তালিকা নিয়ে কাজ করা
- কীভাবে ডেটা কাঠামো করতে হয় তা শিখুন
- অনলাইন এবং অফলাইন ক্ষমতা সম্পর্কে আরও জানুন
এই দস্তাবেজটি ফায়ারবেস ডেটা পড়া এবং লেখার মূল বিষয়গুলি কভার করে।
ফায়ারবেস ডেটা FirebaseDatabase
রেফারেন্সে লেখা হয় এবং রেফারেন্সের সাথে একটি অ্যাসিঙ্ক্রোনাস শ্রোতা সংযুক্ত করে পুনরুদ্ধার করা হয়। শ্রোতা ডেটা প্রাথমিক অবস্থার জন্য একবার ট্রিগার করা হয় এবং আবার যে কোনও সময় ডেটা পরিবর্তিত হয়।
(Al চ্ছিক) প্রোটোটাইপ এবং Firebase Local Emulator Suite সহ পরীক্ষা
আপনার অ্যাপ্লিকেশনটি কীভাবে পড়েছে এবং Realtime Database লেখেন সে সম্পর্কে কথা বলার আগে, আসুন আপনি প্রোটোটাইপ এবং Realtime Database কার্যকারিতা পরীক্ষা করতে ব্যবহার করতে পারেন এমন একটি সরঞ্জাম প্রবর্তন করি: Firebase Local Emulator Suite । আপনি যদি বিভিন্ন ডেটা মডেলগুলি চেষ্টা করে দেখছেন, আপনার সুরক্ষা বিধিগুলি অনুকূল করে তুলছেন বা ব্যাক-এন্ডের সাথে ইন্টারঅ্যাক্ট করার জন্য সবচেয়ে ব্যয়বহুল উপায় খুঁজে পেতে কাজ করছেন, লাইভ পরিষেবাগুলি স্থাপন না করে স্থানীয়ভাবে কাজ করতে সক্ষম হওয়া একটি দুর্দান্ত ধারণা হতে পারে।
একটি Realtime Database এমুলেটর Local Emulator Suite অংশ, যা আপনার অ্যাপ্লিকেশনটিকে আপনার অনুকরণীয় ডাটাবেস সামগ্রী এবং কনফিগারেশনের সাথে যোগাযোগ করতে সক্ষম করে, পাশাপাশি ally চ্ছিকভাবে আপনার অনুকরণীয় প্রকল্প সংস্থানগুলি (ফাংশন, অন্যান্য ডাটাবেস এবং সুরক্ষা বিধি)।
Realtime Database এমুলেটর ব্যবহার করে মাত্র কয়েকটি পদক্ষেপ জড়িত:
- এমুলেটরের সাথে সংযোগ স্থাপনের জন্য আপনার অ্যাপের টেস্ট কনফিগারেশনে কোডের একটি লাইন যুক্ত করা।
- আপনার স্থানীয় প্রকল্প ডিরেক্টরিটির মূল থেকে, চলমান
firebase emulators:start
। - যথারীতি Realtime Database প্ল্যাটফর্ম এসডিকে ব্যবহার করে বা Realtime Database আরইএসটি এপিআই ব্যবহার করে আপনার অ্যাপের প্রোটোটাইপ কোড থেকে কল করা।
Realtime Database এবং Cloud Functions সাথে জড়িত একটি বিশদ ওয়াকথ্রু উপলব্ধ। Local Emulator Suite পরিচিতি আপনারও নজর রাখা উচিত।
একটি ডাটাবেসারফারেন্স পান
ডাটাবেস থেকে ডেটা পড়তে বা লিখতে আপনার DatabaseReference
একটি উদাহরণ প্রয়োজন:
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
ডেটা লিখুন
বেসিক রাইটিং অপারেশন
বেসিক রাইটিং অপারেশনগুলির জন্য, আপনি সেই পথে কোনও বিদ্যমান ডেটা প্রতিস্থাপন করে নির্দিষ্ট রেফারেন্সে ডেটা সংরক্ষণ করতে setValue()
ব্যবহার করতে পারেন। আপনি এই পদ্ধতিটি ব্যবহার করতে পারেন:
- নিম্নলিখিত হিসাবে উপলব্ধ JSON প্রকারের সাথে সম্পর্কিত প্রকারগুলি পাস করুন:
-
String
-
Long
-
Double
-
Boolean
-
Map<String, Object>
-
List<Object>
-
- একটি কাস্টম জাভা অবজেক্টটি পাস করুন, যদি ক্লাসটি সংজ্ঞায়িত করে তবে এটির একটি ডিফল্ট কনস্ট্রাক্টর রয়েছে যা কোনও যুক্তি নেয় না এবং সম্পত্তিগুলি নির্ধারিত করার জন্য পাবলিক গেটার থাকে।
আপনি যদি কোনও জাভা অবজেক্ট ব্যবহার করেন তবে আপনার অবজেক্টের সামগ্রীগুলি স্বয়ংক্রিয়ভাবে নেস্টেড ফ্যাশনে শিশু অবস্থানগুলিতে ম্যাপ করা হয়। জাভা অবজেক্ট ব্যবহার করা সাধারণত আপনার কোডটিকে আরও পঠনযোগ্য এবং বজায় রাখা সহজ করে তোলে। উদাহরণস্বরূপ, আপনার যদি কোনও বেসিক ব্যবহারকারী প্রোফাইল সহ একটি অ্যাপ থাকে তবে আপনার User
অবজেক্টটি নিম্নরূপ দেখতে পারে:
@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. }
@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()
সহ একটি ব্যবহারকারী যুক্ত করতে পারেন:
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
setValue()
ব্যবহার করে এইভাবে কোনও শিশু নোড সহ নির্দিষ্ট স্থানে ডেটা ওভাররাইট করে। তবে আপনি এখনও পুরো অবজেক্টটি পুনরায় লেখা ছাড়াই একটি শিশুকে আপডেট করতে পারেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইলগুলি আপডেট করার অনুমতি দিতে চান তবে আপনি নিম্নলিখিত হিসাবে ব্যবহারকারীর নাম আপডেট করতে পারেন:
database.child("users").child(userId).child("username").setValue(name)
mDatabase.child("users").child(userId).child("username").setValue(name);
ডেটা পড়ুন
অবিচ্ছিন্ন শ্রোতাদের সাথে ডেটা পড়ুন
কোনও পথে ডেটা পড়তে এবং পরিবর্তনগুলি শুনতে, একটি DatabaseReference
একটি ValueEventListener
যুক্ত করতে addValueEventListener()
পদ্ধতিটি ব্যবহার করুন।
শ্রোতা | ইভেন্ট কলব্যাক | সাধারণ ব্যবহার |
---|---|---|
ValueEventListener | onDataChange() | কোনও পথের পুরো বিষয়বস্তুতে পরিবর্তনগুলি পড়ুন এবং শুনুন। |
ইভেন্টের সময় যেমন বিদ্যমান ছিল সেগুলি প্রদত্ত পথে সামগ্রীর একটি স্ট্যাটিক স্ন্যাপশট পড়তে আপনি onDataChange()
পদ্ধতিটি ব্যবহার করতে পারেন। শ্রোতা সংযুক্ত থাকলে এবং যখনই শিশু সহ ডেটা, পরিবর্তনগুলি পরিবর্তন হয় তখন এই পদ্ধতিটি একবার ট্রিগার করা হয়। ইভেন্ট কলব্যাকটি শিশু ডেটা সহ সেই স্থানে সমস্ত ডেটাযুক্ত একটি স্ন্যাপশট পাস করা হয়। যদি কোনও ডেটা না থাকে তবে স্ন্যাপশটটি false
ফিরিয়ে দেবে যখন আপনি কলটি exists()
এবং আপনি যখন getValue()
কল করবেন তখন null
।
নিম্নলিখিত উদাহরণটি ডাটাবেস থেকে কোনও পোস্টের বিশদ পুনরুদ্ধার করে একটি সামাজিক ব্লগিং অ্যাপ্লিকেশন প্রদর্শন করে:
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)
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()
কল করা কল করা ডেটা জাভা অবজেক্টের প্রতিনিধিত্ব করে। যদি লোকেশনটিতে কোনও ডেটা বিদ্যমান না থাকে তবে getValue()
কল করা null
করে দেয়।
এই উদাহরণে, ValueEventListener
onCancelled()
পদ্ধতিটিও সংজ্ঞায়িত করে যা পঠন বাতিল হয়ে গেলে বলা হয়। উদাহরণস্বরূপ, ক্লায়েন্টের ফায়ারবেস ডাটাবেসের অবস্থান থেকে পড়ার অনুমতি না থাকলে একটি পঠন বাতিল করা যেতে পারে। এই পদ্ধতিটি ব্যর্থতা কেন ঘটেছে তা নির্দেশ করে একটি DatabaseError
অবজেক্টটি পাস করা হয়।
একবার ডেটা পড়ুন
একবার জিইটি ব্যবহার করে পড়ুন ()
এসডিকে আপনার অ্যাপ্লিকেশনটি অনলাইন বা অফলাইন কিনা তা ডাটাবেস সার্ভারগুলির সাথে ইন্টারঅ্যাকশনগুলি পরিচালনা করার জন্য ডিজাইন করা হয়েছে।
সাধারণত, আপনার ব্যাকএন্ড থেকে ডেটা আপডেট সম্পর্কে অবহিত করতে ডেটা পড়তে উপরে বর্ণিত ValueEventListener
কৌশলগুলি ব্যবহার করা উচিত। শ্রোতার কৌশলগুলি আপনার ব্যবহার এবং বিলিং হ্রাস করে এবং আপনার ব্যবহারকারীদের অনলাইনে এবং অফলাইনে যাওয়ার সাথে সাথে সর্বোত্তম অভিজ্ঞতা দেওয়ার জন্য অনুকূলিত হয়।
আপনার যদি কেবল একবার ডেটা প্রয়োজন হয় তবে আপনি ডেটাবেস থেকে ডেটা স্ন্যাপশট পেতে get()
ব্যবহার করতে পারেন। যদি কোনও কারণে get()
সার্ভারের মানটি ফেরত দিতে অক্ষম হয় তবে ক্লায়েন্ট স্থানীয় স্টোরেজ ক্যাশে তদন্ত করবে এবং মানটি এখনও না পাওয়া গেলে একটি ত্রুটি ফিরিয়ে দেবে।
get()
এর অপ্রয়োজনীয় ব্যবহার ব্যান্ডউইথের ব্যবহার বাড়িয়ে তুলতে পারে এবং পারফরম্যান্সের ক্ষতি হতে পারে, যা উপরে প্রদর্শিত হিসাবে রিয়েলটাইম শ্রোতা ব্যবহার করে প্রতিরোধ করা যেতে পারে।
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
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
ক্লাস থাকতে পারে:
@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, ) } }
@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; } }
একটি পোস্ট তৈরি করতে এবং একই সাথে এটি সাম্প্রতিক ক্রিয়াকলাপ ফিড এবং পোস্টিং ব্যবহারকারীর ক্রিয়াকলাপ ফিডে আপডেট করতে, ব্লগিং অ্যাপ্লিকেশনটি এর মতো কোড ব্যবহার করে:
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) }
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
-তে সমস্ত ব্যবহারকারীর জন্য পোস্টযুক্ত নোডে একটি পোস্ট তৈরি করতে push()
ব্যবহার করে এবং একই সাথে getKey()
দিয়ে কীটি পুনরুদ্ধার করে। কীটি তখন /user-posts/$userid/$postid
তে ব্যবহারকারীর পোস্টগুলিতে দ্বিতীয় এন্ট্রি তৈরি করতে ব্যবহার করা যেতে পারে।
এই পাথগুলি ব্যবহার করে, আপনি জেএসএন ট্রি -তে একাধিক স্থানে একযোগে আপডেটগুলি সম্পাদন করতে পারেন updateChildren()
এ একক কল সহ, যেমন এই উদাহরণটি কীভাবে উভয় স্থানে নতুন পোস্ট তৈরি করে। যুগপত আপডেটগুলি এইভাবে তৈরি করা পারমাণবিক: হয় সমস্ত আপডেট সফল হয় বা সমস্ত আপডেট ব্যর্থ হয়।
একটি সমাপ্তি কলব্যাক যুক্ত করুন
আপনার ডেটা কখন প্রতিশ্রুতিবদ্ধ হয়েছে তা যদি আপনি জানতে চান তবে আপনি একটি সমাপ্তি শ্রোতা যুক্ত করতে পারেন। setValue()
এবং updateChildren()
উভয়ই একটি al চ্ছিক সমাপ্তি শ্রোতা নেন যা ডাটাবেসে সফলভাবে প্রতিশ্রুতিবদ্ধ হওয়ার সময় বলা হয়। যদি কলটি ব্যর্থ হয় তবে শ্রোতাদের একটি ত্রুটি অবজেক্টটি পাস করা হয় যা ব্যর্থতাটি কেন ঘটেছে তা নির্দেশ করে।
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
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()
সহ এই কৌশলটি ব্যবহার করতে পারেন।
শ্রোতাদের বিচ্ছিন্ন করুন
আপনার ফায়ারবেস ডাটাবেস রেফারেন্সে removeEventListener()
পদ্ধতিটি কল করে কলব্যাকগুলি সরানো হয়।
যদি কোনও শ্রোতাকে কোনও ডেটা স্থানে একাধিকবার যুক্ত করা হয়, তবে এটি প্রতিটি ইভেন্টের জন্য একাধিকবার বলা হয় এবং এটি সম্পূর্ণরূপে অপসারণ করতে আপনাকে অবশ্যই এটি একই সংখ্যক বার আলাদা করতে হবে।
পিতামাতার শ্রোতার কাছে removeEventListener()
কল করা স্বয়ংক্রিয়ভাবে তার শিশু নোডগুলিতে নিবন্ধিত শ্রোতাদের অপসারণ করে না; কলব্যাকটি অপসারণ করতে কোনও শিশু শ্রোতাদের কাছে removeEventListener()
অবশ্যই কল করতে হবে।
লেনদেন হিসাবে ডেটা সংরক্ষণ করুন
ইনক্রিমেন্টাল কাউন্টারগুলির মতো সমবর্তী পরিবর্তনগুলি দ্বারা দূষিত হতে পারে এমন ডেটাগুলির সাথে কাজ করার সময়, আপনি একটি লেনদেন অপারেশন ব্যবহার করতে পারেন। আপনি এই অপারেশন দুটি যুক্তি দিন: একটি আপডেট ফাংশন এবং একটি al চ্ছিক সমাপ্তি কলব্যাক। আপডেট ফাংশনটি ডেটাটির বর্তমান অবস্থাটিকে যুক্তি হিসাবে গ্রহণ করে এবং আপনি লিখতে চান এমন নতুন কাঙ্ক্ষিত রাষ্ট্রকে ফিরিয়ে দেয়। যদি অন্য কোনও ক্লায়েন্ট আপনার নতুন মানটি সফলভাবে লেখার আগে অবস্থানটিতে লেখেন তবে আপনার আপডেট ফাংশনটি নতুন বর্তমান মান দিয়ে আবার ডাকা হবে এবং লেখাটি আবার চেষ্টা করা হয়েছে।
উদাহরণস্বরূপ, উদাহরণস্বরূপ সামাজিক ব্লগিং অ্যাপে, আপনি ব্যবহারকারীদের স্টার এবং আনস্টার পোস্টগুলি করতে এবং কোনও পোস্ট নিম্নলিখিত হিসাবে কতগুলি তারা পেয়েছেন তা ট্র্যাক রাখতে পারবেন:
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!!) } }) }
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); } }); }
একটি লেনদেন ব্যবহার করে স্টার গণনাগুলি ভুল হতে বাধা দেয় যদি একাধিক ব্যবহারকারী একই সময়ে একই পোস্টটি স্টার করে বা ক্লায়েন্টের বাসি ডেটা থাকে। যদি লেনদেনটি প্রত্যাখ্যান করা হয় তবে সার্ভারটি ক্লায়েন্টের কাছে বর্তমান মানটি ফেরত দেয়, যা আপডেট মান সহ আবার লেনদেন চালায়। লেনদেনটি গৃহীত না হওয়া বা অনেকগুলি প্রচেষ্টা না হওয়া পর্যন্ত এটি পুনরাবৃত্তি করে।
পারমাণবিক সার্ভার-সাইড ইনক্রিমেন্টস
উপরোক্ত ব্যবহারের ক্ষেত্রে আমরা ডাটাবেসে দুটি মান লিখছি: ব্যবহারকারী যিনি পোস্টটি স্টার/আনস্টারস এবং ইনক্রিমেন্টেড স্টার কাউন্টের আইডি। যদি আমরা ইতিমধ্যে জানি যে ব্যবহারকারী পোস্টটি অভিনয় করছেন, আমরা লেনদেনের পরিবর্তে একটি পারমাণবিক ইনক্রিমেন্ট অপারেশন ব্যবহার করতে পারি।
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) }
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); }
এই কোডটি কোনও লেনদেন অপারেশন ব্যবহার করে না, সুতরাং কোনও বিরোধী আপডেট থাকলে এটি স্বয়ংক্রিয়ভাবে পুনরায় রান পায় না। তবে, যেহেতু ইনক্রিমেন্ট অপারেশনটি সরাসরি ডাটাবেস সার্ভারে ঘটে, তাই কোনও বিরোধের সম্ভাবনা নেই।
আপনি যদি অ্যাপ্লিকেশন-নির্দিষ্ট দ্বন্দ্বগুলি সনাক্ত এবং প্রত্যাখ্যান করতে চান, যেমন কোনও ব্যবহারকারী যেমন তারা ইতিমধ্যে অভিনয় করেছেন এমন একটি পোস্ট অভিনয় করেছেন, আপনার সেই ব্যবহারের ক্ষেত্রে কাস্টম সুরক্ষা বিধি লিখতে হবে।
অফলাইনে ডেটা নিয়ে কাজ করুন
যদি কোনও ক্লায়েন্ট তার নেটওয়ার্ক সংযোগটি হারিয়ে ফেলে তবে আপনার অ্যাপ্লিকেশনটি সঠিকভাবে কাজ করা চালিয়ে যাবে।
ফায়ারবেস ডাটাবেসের সাথে সংযুক্ত প্রতিটি ক্লায়েন্ট শ্রোতাদের ব্যবহার করা হচ্ছে এমন কোনও ডেটার নিজস্ব অভ্যন্তরীণ সংস্করণ বজায় রাখে বা যা সার্ভারের সাথে সিঙ্কে রাখার জন্য পতাকাযুক্ত হয়। যখন ডেটা পড়া বা লিখিত হয়, তখন ডেটাটির এই স্থানীয় সংস্করণটি প্রথমে ব্যবহৃত হয়। ফায়ারবেস ক্লায়েন্ট তারপরে সেই ডেটা দূরবর্তী ডাটাবেস সার্ভারগুলির সাথে এবং অন্যান্য ক্লায়েন্টদের সাথে "সেরা-প্রচেষ্টা" ভিত্তিতে সিঙ্ক্রোনাইজ করে।
ফলস্বরূপ, সমস্ত সার্ভারের সাথে কোনও মিথস্ক্রিয়া করার আগে ডাটাবেস স্থানীয় ইভেন্টগুলি ট্রিগার করে। এর অর্থ আপনার অ্যাপ্লিকেশনটি নেটওয়ার্কের বিলম্ব বা সংযোগ নির্বিশেষে প্রতিক্রিয়াশীল থেকে যায়।
সংযোগটি পুনরায় প্রতিষ্ঠিত হয়ে গেলে, আপনার অ্যাপ্লিকেশনটি ইভেন্টগুলির উপযুক্ত সেটটি গ্রহণ করে যাতে ক্লায়েন্ট কোনও কাস্টম কোড না লিখে বর্তমান সার্ভার স্টেটের সাথে সিঙ্ক করে।
আমরা অনলাইন এবং অফলাইন ক্ষমতা সম্পর্কে আরও শিখতে অফলাইন আচরণ সম্পর্কে আরও কথা বলব।