Bu belgede, Firebase verilerini okuma ve yazma ile ilgili temel bilgiler verilmektedir.
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, veriler her değiştiğinde ise tekrar tetiklenir.
(İsteğe bağlı) Firebase Local Emulator Suite ile prototip oluşturma ve test etme
Uygulamanızın Realtime Database ile nasıl okuma ve yazma işlemi yaptığından bahsetmeden önce, Realtime Database işlevselliğini prototip oluşturmak ve test etmek için kullanabileceğiniz bir dizi aracı tanıtalım: Firebase Local Emulator Suite. Farklı veri modellerini deniyorsanız, güvenlik kurallarınızı optimize ediyorsanız veya arka uçla etkileşim kurmanın en uygun maliyetli yolunu bulmaya çalışıyorsanız canlı hizmetleri dağıtmadan yerel olarak çalışabilmek harika bir fikir olabilir.
Realtime Database emülatörü, Local Emulator Suite'nin bir parçasıdır. Bu araç, uygulamanızın emüle edilmiş veritabanı içeriğiniz ve yapılandırmanızla, ayrıca isteğe bağlı olarak emüle edilmiş 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 gerekir:
- Emülatöre bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleyin.
- Yerel proje dizininizin kökünden
firebase emulators:start
komutunu çalıştırın. - Uygulamanızın prototip kodundan Realtime Database platform SDK'sını her zamanki gibi kullanarak veya Realtime Database REST API'yi kullanarak çağrı yapma.
Realtime Database ve Cloud Functions ile ilgili ayrıntılı birrehber mevcuttur. Ayrıca Local Emulator Suite giriş bölümüne de göz atmanız gerekir.
DatabaseReference alma
Veri tabanından veri okumak veya veri tabanı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();
Veri yazma
Temel yazma işlemleri
Temel yazma işlemleri için setValue()
kullanarak verileri belirtilen bir referansa kaydedebilir ve bu yoldaki mevcut verilerin yerini alabilirsiniz. Bu yöntemi kullanarak şunları yapabilirsiniz:
- Kullanılabilir JSON türlerine karşılık gelen kart türleri aşağıdaki gibidir:
String
Long
Double
Boolean
Map<String, Object>
List<Object>
- Tanımlayan sınıfın, bağımsız değişken almayan ve atanacak özellikler için herkese açık alıcıları olan varsayılan bir oluşturucusu varsa özel bir Java nesnesi iletin.
Bir Java nesnesi kullanırsanız nesnenizin içeriği, iç içe yerleştirilmiş şekilde alt konumlara otomatik olarak eşlenir. Java nesnesi kullanmak da genellikle kodunuzu daha okunabilir ve bakımı daha kolay hale getirir. Örneğin, temel kullanıcı profili olan 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:
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()
öğesini bu şekilde kullanmak, belirtilen konumdaki verilerin (alt düğümler dahil) üzerine yazar. Ancak yine de tüm nesneyi yeniden yazmadan bir çocuğu güncelleyebilirsiniz. Kullanıcıların profillerini güncellemesine 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 araması | Tipik kullanım |
---|---|---|
ValueEventListener |
onDataChange() |
Bir yolun tüm içeriğindeki değişiklikleri okuma ve dinleme |
Belirli bir yoldaki içeriklerin, etkinlik sırasında olduğu şekliyle statik bir anlık görüntüsünü okumak için onDataChange()
yöntemini kullanabilirsiniz. Bu yöntem, dinleyici eklendiğinde bir kez, veriler (çocuklar dahil) her değiştiğinde ise tekrar tetiklenir. Etkinlik geri çağırmasına, alt veriler de dahil olmak üzere o konumdaki tüm verileri içeren bir anlık görüntü iletilir. Veri yoksa exists()
'ı aradığınızda anlık görüntü false
, getValue()
'ı aradığınızda ise null
değerini döndürür.
Aşağıdaki örnekte, bir gönderinin ayrıntılarını veritabanından alan 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);
Dinleyici, 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 nesne 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 nedenini 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önetmek için tasarlanmıştır.
Genel olarak, arka uçtaki verilerde yapılan güncellemelerden haberdar olmak için verileri okumak üzere ValueEventListener
yukarıda açıklanan teknikleri
kullanmanız gerekir. Dinleyici teknikleri, kullanımınızı ve faturalandırmanızı azaltır. Ayrıca, kullanıcılarınıza çevrimiçi ve çevrimdışı oldukları sırada en iyi deneyimi sunmak için 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üremiyorsa istemci, yerel depolama önbelleğini yoklar ve değer hâlâ bulunamazsa bir hata döndürür.
get()
öğesinin 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, yalnızca bir kez yüklenmesi gereken ve sık sık değişmesi ya da aktif dinleme gerektirmesi beklenmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, kullanıcı yeni bir gönderi yazmaya başladığında profilini yüklemek için bu yöntemi kullanır.
Verileri güncelleme veya silme
Belirli alanları güncelleme
Bir düğümün belirli alt öğelerine diğer alt düğümleri üzerine yazmadan aynı anda yazmak için updateChildren()
yöntemini kullanın.
updateChildren()
çağrısı yaparken anahtar için bir yol belirterek alt düzeydeki alt değerleri güncelleyebilirsiniz. Veriler daha iyi ölçeklendirme için birden fazla konumda depolanıyorsa veri dağıtımı kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, bir sosyal blog uygulaması aşağıdaki gibi bir Post
sınıfına sahip 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; } }
Bir yayın oluşturmak ve aynı anda hem son etkinlikler feed'inde hem de yayını yapan kullanıcının etkinlik feed'inde güncellemek için blog uygulaması şu 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, push()
kullanılarak /posts/$postid
adresindeki tüm kullanıcıların yayınlarını içeren düğümde bir yayın oluşturuluyor ve aynı anda getKey()
ile anahtar alınıyor. Ardından anahtar, kullanıcının /user-posts/$userid/$postid
adresindeki gönderilerinde ikinci bir giriş oluşturmak için kullanılabilir.
Bu yolları kullanarak, updateChildren()
için tek bir çağrıyla JSON ağacındaki birden fazla konumda eşzamanlı güncellemeler yapabilirsiniz. Örneğin, bu örnekte yeni gönderi her iki konumda da oluşturulur. Bu şekilde yapılan eşzamanlı güncellemeler atomiktir: Tüm güncellemeler başarılı olur veya tüm güncellemeler başarısız olur.
Tamamlama geri çağırması ekleme
Verilerinizin ne zaman işlendiğini öğrenmek istiyorsanız tamamlanma işleyicisi ekleyebilirsiniz. Hem setValue()
hem de updateChildren()
, yazma işlemi veritabanına başarıyla kaydedildiğinde çağrılan isteğe bağlı bir tamamlama işleyicisi alır. Çağrı başarısız olursa dinleyiciye, başarısızlığı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 basit yolu, verilerin konumuna yapılan bir referansta removeValue()
işlevini çağırmaktır.
Ayrıca null
veya updateChildren()
gibi başka bir yazma işlemi için değer olarak setValue()
belirterek de silebilirsiniz. Bu tekniği updateChildren()
ile birlikte kullanarak tek bir API çağrısında birden fazla alt öğeyi silebilirsiniz.
Dinleyicileri ayırma
Geri aramalar, Firebase veritabanı referansınızda removeEventListener()
yöntemi çağrılarak kaldırılır.
Bir dinleyici bir veri konumuna birden fazla kez eklenmişse her etkinlik için birden fazla kez çağrılır ve tamamen kaldırmak için aynı sayıda ayırmanız gerekir.
Bir ebeveyn dinleyicide removeEventListener()
çağrısı yapılması, alt düğümlerinde kayıtlı dinleyicileri otomatik olarak kaldırmaz. Geri çağırmayı kaldırmak için tüm alt dinleyicilerde de removeEventListener()
çağrısı yapılmalıdır.
Verileri işlem olarak kaydetme
Eşzamanlı değişiklikler nedeniyle bozulabilecek verilerle (ör. artımlı sayaçlar) çalışırken işlem işlemi kullanabilirsiniz. Bu işleme iki bağımsız değişken iletirsiniz: bir güncelleme işlevi ve isteğe bağlı bir tamamlanma geri çağırması. 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. Başka bir istemci, yeni değeriniz başarıyla yazılmadan önce konuma yazarsa güncelleme işleviniz yeni geçerli değerle tekrar çağrılır ve yazma işlemi yeniden denenir.
Örneğin, örnek sosyal blog uygulamasında kullanıcıların gönderilere yıldız eklemesine ve yıldızları kaldırmasına izin verebilir, ayrıca bir gönderinin kaç yıldız aldığını aşağıdaki gibi takip edebilirsiniz:
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ı aynı anda aynı gönderiye yıldız verirse veya istemcide eski veriler varsa yıldız sayılarının yanlış olmasını önler. İşlem reddedilirse sunucu, mevcut değeri istemciye döndürür. İstemci, 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: gönderiye yıldız ekleyen/kaldıran kullanıcının kimliği ve artırılmış yıldız sayısı. Kullanıcının yayını yıldızladığını 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 işlemi kullanılmadığı için ç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.
Uygulamaya özgü çakışmaları (ör. kullanıcının daha önce yıldızladığı bir gönderiyi tekrar yıldızlaması) tespit edip reddetmek istiyorsanız bu kullanım alanı için özel güvenlik kuralları yazmanız gerekir.
Verilerle çevrimdışı çalışma
Bir istemcinin ağ bağlantısı kesilirse uygulamanız doğru şekilde çalışmaya devam eder.
Bir Firebase veritabanına bağlı her istemci, dinleyicilerin kullanıldığı veya sunucuyla senkronize tutulması için işaretlenen tüm verilerin kendi dahili sürümünü korur. Veriler okunduğunda veya yazıldığında önce verilerin bu yerel sürümü kullanılır. Ardından Firebase istemcisi, bu verileri uzaktaki veritabanı sunucularıyla ve diğer istemcilerle "en iyi çaba" prensibine göre senkronize eder.
Sonuç olarak, veritabanına yapılan tüm yazma işlemleri, sunucuyla herhangi bir etkileşim olmadan hemen yerel etkinlikleri tetikler. Bu sayede uygulamanız, ağ gecikmesinden veya bağlantıdan bağımsız olarak yanıt vermeye devam eder.
Bağlantı yeniden kurulduğunda uygulamanız, istemcinin mevcut sunucu durumuyla senkronize olması için uygun etkinlik kümesini 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 başlıklı makalede bulabilirsiniz.