Bu belgede, Firebase verilerini okuma ve yazmayla ilgili temel bilgiler yer almaktadır.
Firebase verileri bir FirebaseDatabase referansına yazılır ve referansa eşzamansız bir dinleyici eklenerek alınır. Dinleyici, verilerin ilk durumu için bir kez ve veriler her değiştiğinde tekrar tetiklenir.
(İsteğe bağlı) Firebase Local Emulator Suite ile prototip oluşturma ve test etme
Uygulamanızın Realtime Database üzerinde nasıl okuma ve yazma yaptığından bahsetmeden önce, Realtime Database işlevinin prototipini oluşturmak ve test etmek için kullanabileceğiniz bir dizi araçtan bahsedelim: Firebase Local Emulator Suite. Farklı veri modelleri deniyor, güvenlik kurallarınızı optimize ediyor veya arka uçla etkileşim kurmanın en uygun maliyetli yolunu bulmaya çalışıyorsanız canlı hizmetleri dağıtmadan yerel olarak çalışmak iyi bir fikir olabilir.
Realtime Database emülatörü, Local Emulator Suite'nin bir parçasıdır. Bu emülatör, uygulamanızın emülasyonu yapılan veritabanı içeriğiniz ve yapılandırmanızın yanı sıra isteğe bağlı olarak emülasyonu yapılan proje kaynaklarınızla (işlevler, diğer veritabanları ve güvenlik kuralları) etkileşim kurmasını sağlar.
Realtime Database emülatörünü kullanmak için yalnızca birkaç adım yeterlidir:
- Uygulamanızın test yapılandırmasına emülatöre bağlanmak için bir kod satırı ekleme
- Yerel proje dizininizin kökünden
firebase emulators:startkomutunu çalıştırın. - Her zamanki gibi bir Realtime Database platform SDK'sı kullanarak veya Realtime Database REST API'yi kullanarak uygulamanızın prototip kodundan çağrı yapma.
Realtime Database ve Cloud Functions ile ilgili ayrıntılı bir adım adım açıklamayı inceleyebilirsiniz. Local Emulator Suite girişine de göz atmanız önerilir.
DatabaseReference alma
Veritabanından veri okumak veya veritabanına veri yazmak için DatabaseReference örneğine ihtiyacınız vardır:
Kotlin
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
Verileri yazma
Temel yazma işlemleri
Temel yazma işlemleri için setValue() kullanarak verileri belirtilen bir referansa kaydedebilir ve bu yoldaki mevcut verileri değiştirebilirsiniz. Bu yöntemi kullanarak şunları yapabilirsiniz:
- Mevcut JSON türlerine karşılık gelen kart türleri şunlardır:
StringLongDoubleBooleanMap<String, Object>List<Object>
- Tanımlayan sınıfın bağımsız değişken içermeyen varsayılan bir oluşturucusu ve atanacak özellikler için herkese açık alıcıları varsa özel bir Java nesnesi iletin.
Java nesnesi kullanıyorsanız nesnenizin içeriği, alt konumlara iç içe yerleştirilmiş şekilde otomatik olarak eşlenir. Java nesnesi kullanmak, kodunuzu daha okunabilir ve bakımı daha kolay hale getirir. Örneğin, temel bir kullanıcı profiline sahip bir uygulamanız varsa User nesneniz aşağıdaki gibi görünebilir:
Kotlin
@IgnoreExtraProperties data class User(val username: String? = null, val email: String? = null) { // Null default values create a no-argument default constructor, which is needed // for deserialization from a DataSnapshot. }
Java
@IgnoreExtraProperties public class User { public String username; public String email; public User() { // Default constructor required for calls to DataSnapshot.getValue(User.class) } public User(String username, String email) { this.username = username; this.email = email; } }
setValue() ile kullanıcı eklemek için aşağıdaki adımları uygulayabilirsiniz:
Kotlin
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
Java
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
setValue() bu şekilde kullanıldığında, alt düğümler de dahil olmak üzere belirtilen konumdaki verilerin üzerine yazılır. Ancak nesnenin tamamını yeniden yazmadan alt öğeyi güncelleyebilirsiniz. Kullanıcıların profillerini güncellemelerine izin vermek istiyorsanız
kullanıcı adını aşağıdaki gibi güncelleyebilirsiniz:
Kotlin
database.child("users").child(userId).child("username").setValue(name)
Java
mDatabase.child("users").child(userId).child("username").setValue(name);
Verileri okuma
Kalıcı dinleyicilerle veri okuma
Bir yoldaki verileri okumak ve değişiklikleri dinlemek için addValueEventListener() yöntemini kullanarak DatabaseReference öğesine ValueEventListener ekleyin.
| Dinleyici | Etkinlik geri çağırma | Tipik kullanım |
|---|---|---|
ValueEventListener |
onDataChange() |
Bir yolun tüm içeriğindeki değişiklikleri okuma ve dinleme |
Belirli bir yoldaki içeriklerin etkinlik anındaki statik anlık görüntüsünü okumak için onDataChange() yöntemini kullanabilirsiniz. Bu yöntem, dinleyici eklendiğinde bir kez ve alt öğeler de dahil olmak üzere veriler her değiştiğinde tekrar tetiklenir. Etkinlik geri çağırma işlevine, alt veriler de dahil olmak üzere ilgili konumdaki tüm verileri içeren bir anlık görüntü iletilir. Veri yoksa anlık görüntü, exists() çağrısı yaptığınızda false, getValue() çağrısı yaptığınızda ise null döndürür.
Aşağıdaki örnekte, bir gönderinin ayrıntılarını veritabanından alan bir sosyal blog uygulaması gösterilmektedir:
Kotlin
val postListener = object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { // Get Post object and use the values to update the UI val post = dataSnapshot.getValue<Post>() // ... } override fun onCancelled(databaseError: DatabaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()) } } postReference.addValueEventListener(postListener)
Java
ValueEventListener postListener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Get Post object and use the values to update the UI Post post = dataSnapshot.getValue(Post.class); // .. } @Override public void onCancelled(DatabaseError databaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()); } }; mPostReference.addValueEventListener(postListener);
İşleyici, etkinlik sırasında veritabanında belirtilen konumdaki verileri içeren bir DataSnapshot alır. Bir anlık görüntüde getValue() çağrıldığında verilerin Java nesnesi temsili döndürülür. Konumda veri yoksa getValue() çağrısı null değerini döndürür.
Bu örnekte ValueEventListener, okuma işlemi iptal edilirse çağrılan onCancelled() yöntemini de tanımlar. Örneğin, istemcinin bir Firebase veritabanı konumundan okuma izni yoksa okuma işlemi iptal edilebilir. Bu yönteme, hatanın neden oluştuğunu belirten bir DatabaseError nesnesi iletilir.
Verileri bir kez okuma
get() kullanarak bir kez okuma
SDK, uygulamanızın çevrimiçi veya çevrimdışı olmasına bakılmaksızın veritabanı sunucularıyla etkileşimleri yönetecek şekilde tasarlanmıştır.
Genellikle, arka uçtan gelen veri güncellemeleriyle ilgili bildirim almak için verileri okumak üzere yukarıda açıklanan ValueEventListener tekniklerini kullanmanız gerekir. Dinleyici teknikleri, kullanımınızı ve faturalandırmanızı azaltır ve kullanıcılarınıza çevrimiçi ve çevrimdışı olduklarında en iyi deneyimi sunacak şekilde optimize edilmiştir.
Verilere yalnızca bir kez ihtiyacınız varsa veritabanındaki verilerin anlık görüntüsünü almak için get() kullanabilirsiniz. get() herhangi bir nedenle sunucu değerini döndüremezse istemci, yerel depolama önbelleğini inceler ve değer hâlâ bulunamazsa bir hata döndürür.
get() yönteminin gereksiz kullanımı, bant genişliği kullanımını artırabilir ve performans kaybına yol açabilir. Bu durum, yukarıda gösterildiği gibi gerçek zamanlı bir dinleyici kullanılarak önlenebilir.
Kotlin
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
Java
mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
@Override
public void onComplete(@NonNull Task<DataSnapshot> task) {
if (!task.isSuccessful()) {
Log.e("firebase", "Error getting data", task.getException());
}
else {
Log.d("firebase", String.valueOf(task.getResult().getValue()));
}
}
});
Dinleyici kullanarak bir kez okuma
Bazı durumlarda, sunucuda güncellenmiş bir değer olup olmadığını kontrol etmek yerine yerel önbellekteki değerin hemen döndürülmesini isteyebilirsiniz. Bu gibi durumlarda, verileri yerel disk önbelleğinden hemen almak için addListenerForSingleValueEvent kullanabilirsiniz.
Bu yöntem, yalnızca bir kez yüklenmesi gereken ve sık sık değişmesi veya etkin dinleme gerektirmesi beklenmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, kullanıcı yeni bir yayın oluşturmaya başladığında kullanıcının profilini yüklemek için bu yöntemi kullanır.
Verileri güncelleme veya silme
Belirli alanları güncelleme
Diğer alt düğümlerin üzerine yazmadan bir düğümün belirli alt düğümlerine aynı anda yazmak için updateChildren() yöntemini kullanın.
updateChildren() yöntemini çağırırken anahtar için bir yol belirterek alt düzey alt değerleri güncelleyebilirsiniz. Veriler daha iyi ölçeklendirme için birden fazla konumda depolanıyorsa veri yayma yöntemini kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, bir sosyal blog uygulamasında aşağıdaki gibi bir Post sınıfı olabilir:
Kotlin
@IgnoreExtraProperties data class Post( var uid: String? = "", var author: String? = "", var title: String? = "", var body: String? = "", var starCount: Int = 0, var stars: MutableMap<String, Boolean> = HashMap(), ) { @Exclude fun toMap(): Map<String, Any?> { return mapOf( "uid" to uid, "author" to author, "title" to title, "body" to body, "starCount" to starCount, "stars" to stars, ) } }
Java
@IgnoreExtraProperties public class Post { public String uid; public String author; public String title; public String body; public int starCount = 0; public Map<String, Boolean> stars = new HashMap<>(); public Post() { // Default constructor required for calls to DataSnapshot.getValue(Post.class) } public Post(String uid, String author, String title, String body) { this.uid = uid; this.author = author; this.title = title; this.body = body; } @Exclude public Map<String, Object> toMap() { HashMap<String, Object> result = new HashMap<>(); result.put("uid", uid); result.put("author", author); result.put("title", title); result.put("body", body); result.put("starCount", starCount); result.put("stars", stars); return result; } }
Blog uygulaması, bir yayın oluşturmak ve bu yayını aynı anda son etkinlik feed'ine ve yayını yapan kullanıcının etkinlik feed'ine güncellemek için aşağıdaki gibi bir kod kullanır:
Kotlin
private fun writeNewPost(userId: String, username: String, title: String, body: String) { // Create new post at /user-posts/$userid/$postid and at // /posts/$postid simultaneously val key = database.child("posts").push().key if (key == null) { Log.w(TAG, "Couldn't get push key for posts") return } val post = Post(userId, username, title, body) val postValues = post.toMap() val childUpdates = hashMapOf<String, Any>( "/posts/$key" to postValues, "/user-posts/$userId/$key" to postValues, ) database.updateChildren(childUpdates) }
Java
private void writeNewPost(String userId, String username, String title, String body) { // Create new post at /user-posts/$userid/$postid and at // /posts/$postid simultaneously String key = mDatabase.child("posts").push().getKey(); Post post = new Post(userId, username, title, body); Map<String, Object> postValues = post.toMap(); Map<String, Object> childUpdates = new HashMap<>(); childUpdates.put("/posts/" + key, postValues); childUpdates.put("/user-posts/" + userId + "/" + key, postValues); mDatabase.updateChildren(childUpdates); }
Bu örnekte, /posts/$postid adresindeki tüm kullanıcıların yayınlarını içeren düğümde bir yayın oluşturmak için push() kullanılır ve aynı anda getKey() ile anahtar alınır. Anahtar daha sonra kullanıcının /user-posts/$userid/$postid adresindeki yayınlarında ikinci bir giriş oluşturmak için kullanılabilir.
Bu yolları kullanarak, JSON ağacındaki birden fazla konumda aynı anda güncelleme yapmak için updateChildren() yöntemini tek bir kez çağırabilirsiniz. Bu örnekte, yeni yayının her iki konumda da nasıl oluşturulduğu gösterilmektedir. Bu şekilde yapılan eşzamanlı güncellemeler atomiktir: Güncellemelerin ya hepsi başarılı olur ya da hepsi başarısız olur.
Tamamlama geri çağırma işlevi ekleme
Verilerinizin ne zaman kaydedildiğini öğrenmek istiyorsanız bir tamamlama işleyicisi ekleyebilirsiniz. Hem setValue() hem de updateChildren(), yazma işlemi veritabanına başarıyla uygulandığında çağrılan isteğe bağlı bir tamamlama dinleyicisi alır. Çağrı başarısız olursa dinleyiciye hatanın nedenini belirten bir hata nesnesi iletilir.
Kotlin
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
Java
mDatabase.child("users").child(userId).setValue(user) .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { // Write was successful! // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Write failed // ... } });
Verileri silin
Verileri silmenin en kolay yolu, verilerin konumuna referans vererek removeValue() yöntemini çağırmaktır.
setValue() veya updateChildren() gibi başka bir yazma işlemi için değer olarak null belirterek de silebilirsiniz. Tek bir API çağrısında birden fazla alt öğeyi silmek için bu tekniği updateChildren() ile kullanabilirsiniz.
Dinleyicileri ayırma
Geri çağırmalar, Firebase veritabanı referansınızda removeEventListener() yöntemi çağrılarak kaldırılır.
Bir dinleyici bir veri konumuna birden çok kez eklenmişse her etkinlik için birden çok kez çağrılır ve tamamen kaldırmak için aynı sayıda ayırmanız gerekir.
Üst dinleyicide removeEventListener() yöntemini çağırmak, alt düğümlerinde kayıtlı dinleyicileri otomatik olarak kaldırmaz. Geri çağırmayı kaldırmak için alt dinleyicilerde de removeEventListener() yönteminin çağrılması gerekir.
Verileri işlem olarak kaydetme
Artımlı sayaçlar gibi eşzamanlı değişiklikler nedeniyle bozulabilecek verilerle çalışırken işlem operasyonu kullanabilirsiniz. Bu işlem için iki bağımsız değişken belirtirsiniz: güncelleme işlevi ve isteğe bağlı tamamlama geri çağırma işlevi. Güncelleme işlevi, verilerin mevcut durumunu bağımsız değişken olarak alır ve yazmak istediğiniz yeni durumu döndürür. Yeni değeriniz başarıyla yazılmadan önce başka bir istemci konuma yazarsa güncelleme işleviniz yeni geçerli değerle tekrar çağrılır ve yazma işlemi yeniden denenir.
Örneğin, sosyal blog uygulaması örneğinde kullanıcıların yayınlara yıldız ekleyip kaldırmasına ve bir yayının kaç yıldız aldığını aşağıdaki gibi takip etmesine izin verebilirsiniz:
Kotlin
private fun onStarClicked(postRef: DatabaseReference) { // ... postRef.runTransaction(object : Transaction.Handler { override fun doTransaction(mutableData: MutableData): Transaction.Result { val p = mutableData.getValue(Post::class.java) ?: return Transaction.success(mutableData) if (p.stars.containsKey(uid)) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1 p.stars.remove(uid) } else { // Star the post and add self to stars p.starCount = p.starCount + 1 p.stars[uid] = true } // Set value and report transaction success mutableData.value = p return Transaction.success(mutableData) } override fun onComplete( databaseError: DatabaseError?, committed: Boolean, currentData: DataSnapshot?, ) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError!!) } }) }
Java
private void onStarClicked(DatabaseReference postRef) { postRef.runTransaction(new Transaction.Handler() { @NonNull @Override public Transaction.Result doTransaction(@NonNull MutableData mutableData) { Post p = mutableData.getValue(Post.class); if (p == null) { return Transaction.success(mutableData); } if (p.stars.containsKey(getUid())) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1; p.stars.remove(getUid()); } else { // Star the post and add self to stars p.starCount = p.starCount + 1; p.stars.put(getUid(), true); } // Set value and report transaction success mutableData.setValue(p); return Transaction.success(mutableData); } @Override public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot currentData) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError); } }); }
İşlem kullanmak, birden fazla kullanıcının aynı yayına aynı anda yıldız vermesi veya istemcinin eski verilere sahip olması durumunda yıldız sayılarının yanlış olmasını önler. İşlem reddedilirse sunucu, mevcut değeri istemciye döndürür ve istemci, işlemi güncellenmiş değerle tekrar çalıştırır. Bu işlem, işlem kabul edilene veya çok fazla deneme yapılana kadar tekrarlanır.
Atomik sunucu tarafı artışları
Yukarıdaki kullanım alanında veritabanına iki değer yazıyoruz: yayına yıldız ekleyen/yıldızı kaldıran kullanıcının kimliği ve artırılan yıldız sayısı. Kullanıcının yayına yıldız eklediğini zaten biliyorsak işlem yerine atomik artırma işlemi kullanabiliriz.
Kotlin
private fun onStarClicked(uid: String, key: String) { val updates: MutableMap<String, Any> = hashMapOf( "posts/$key/stars/$uid" to true, "posts/$key/starCount" to ServerValue.increment(1), "user-posts/$uid/$key/stars/$uid" to true, "user-posts/$uid/$key/starCount" to ServerValue.increment(1), ) database.updateChildren(updates) }
Java
private void onStarClicked(String uid, String key) { Map<String, Object> updates = new HashMap<>(); updates.put("posts/"+key+"/stars/"+uid, true); updates.put("posts/"+key+"/starCount", ServerValue.increment(1)); updates.put("user-posts/"+uid+"/"+key+"/stars/"+uid, true); updates.put("user-posts/"+uid+"/"+key+"/starCount", ServerValue.increment(1)); mDatabase.updateChildren(updates); }
Bu kodda işlem operasyonu kullanılmadığından, çakışan bir güncelleme olduğunda otomatik olarak yeniden çalıştırılmaz. Ancak artırma işlemi doğrudan veritabanı sunucusunda gerçekleştiği için çakışma olasılığı yoktur.
Kullanıcının daha önce yıldız eklediği bir yayına tekrar yıldız eklemesi gibi uygulamaya özel çakışmaları tespit edip reddetmek istiyorsanız bu kullanım alanı için özel güvenlik kuralları yazmanız gerekir.
Verilerle çevrimdışı çalışma
İstemcinin ağ bağlantısı kesilirse uygulamanız düzgün şekilde çalışmaya devam eder.
Firebase veritabanına bağlı her istemci, dinleyicilerin kullanıldığı veya sunucuyla senkronize tutulmak üzere işaretlenen tüm verilerin kendi dahili sürümünü saklar. Veriler okunurken veya yazılırken öncelikle verilerin bu yerel sürümü kullanılır. Firebase istemcisi daha sonra bu verileri uzak veritabanı sunucuları ve diğer istemcilerle "en iyi çaba" temelinde senkronize eder.
Sonuç olarak, veritabanına yapılan tüm yazma işlemleri, sunucuyla herhangi bir etkileşimden önce yerel etkinlikleri hemen tetikler. Bu sayede uygulamanız, ağ gecikmesi veya bağlantı durumundan bağımsız olarak yanıt vermeye devam eder.
Bağlantı yeniden kurulduktan sonra uygulamanız, istemcinin mevcut sunucu durumuyla senkronize olması için uygun etkinlik grubunu alır. Bu işlem için özel kod yazmanız gerekmez.
Çevrimdışı davranış hakkında daha fazla bilgiyi Online ve çevrimdışı özellikler hakkında daha fazla bilgi bölümünde bulabilirsiniz.