Bu belgede Firebase verilerini okuma ve yazmayla ilgili temel bilgiler verilmektedir.
Firebase verileri, FirebaseDatabase
referansına yazılır ve referansa eşzamansız bir işleyici eklenerek alınır. İşleyici bir kez verilerin ilk durumu için tetiklenir ve veriler her değiştiğinde bir kez daha tetiklenir.
(İsteğe bağlı) Firebase Local Emulator Suite ile prototip oluşturun ve test edin
Uygulamanızın Realtime Database'den nasıl okuduğu ve Realtime Database'e nasıl yazıldığı hakkında konuşmadan önce, Realtime Database işlevinin prototipini oluşturmak ve test etmek için kullanabileceğiniz Firebase Local Emulator Suite adlı araçlardan bahsedelim. Farklı veri modelleri 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 çok iyi bir fikir olabilir.
Realtime Database emülatörü, Local Emulator Suite'in bir parçasıdır. Bu paket, uygulamanızın emüle edilmiş veritabanı içeriğiniz ve yapılandırmanızın yanı sıra isteğe bağlı olarak emüle edilen proje kaynaklarınızla (işlevler, diğer veritabanları ve güvenlik kuralları) etkileşimde bulunmasını sağlar.
Realtime Database emülatörünün kullanımı yalnızca birkaç adımdan oluşur:
- Emülatöre bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleme.
- Yerel proje dizininizin kök dizininden
firebase emulators:start
komutunu çalıştırın. - Normalde olduğu gibi bir Realtime Database platform SDK'sı veya Realtime Database REST API kullanarak uygulamanızın prototip kodundan çağrı yapma.
Realtime Database ve Cloud Functions'ı içeren adım adım açıklamalı ayrıntılı bir kılavuz mevcuttur. Ayrıca Local Emulator Suite tanıtımı'na da göz atmalısınız.
Veritabanı Referansı Alma
Veritabanından veri okumak veya yazmak için DatabaseReference
örneğinin olması gerekir:
Kotlin+KTX
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şlemlerinde, verileri belirtilen bir referansa kaydetmek ve söz konusu yoldaki mevcut tüm verileri değiştirmek için setValue()
kullanabilirsiniz. Bu yöntemi şu amaçlarla kullanabilirsiniz:
- Kullanılabilir JSON türlerine karşılık gelen aşağıdaki gibi geçiş türleri:
String
Long
Double
Boolean
Map<String, Object>
List<Object>
- Tanımlayan sınıfın, hiçbir bağımsız değişken almayan ve atanması için genel alıcılar içeren bir varsayılan oluşturucusu varsa özel bir Java nesnesi iletin.
Java nesnesi kullanıyorsanız nesnenizin içeriği iç içe yerleştirilmiş bir biçimde otomatik olarak alt konumlarla eşlenir. Java nesnesi kullanmak, genellikle kodunuzun daha
okunabilir ve bakımını kolaylaştırır. Örneğin, temel kullanıcı profiline sahip bir uygulamanız varsa User
nesneniz aşağıdaki gibi görünebilir:
Kotlin+KTX
@IgnoreExtraProperties data class User(val username: String? = null, val email: String? = null) { // Null default values create a no-argument default constructor, which is needed // for deserialization from a DataSnapshot. }
Java
@IgnoreExtraProperties public class User { public String username; public String email; public User() { // Default constructor required for calls to DataSnapshot.getValue(User.class) } public User(String username, String email) { this.username = username; this.email = email; } }
setValue()
uygulamasına sahip bir kullanıcıyı aşağıdaki şekilde ekleyebilirsiniz:
Kotlin+KTX
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
Java
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
setValue()
bu şekilde kullanıldığında, tüm alt düğümler de dahil olmak üzere belirtilen konumdaki verilerin üzerine yazılır. Ancak, nesnenin tamamını yeniden yazmadan da alt öğeleri güncelleyebilirsiniz. Kullanıcıların profillerini güncellemelerine izin vermek
isterseniz kullanıcı adını aşağıdaki şekilde güncelleyebilirsiniz:
Kotlin+KTX
database.child("users").child(userId).child("username").setValue(name)
Java
mDatabase.child("users").child(userId).child("username").setValue(name);
Verileri okuma
Verileri kalıcı işleyicilerle okuma
Bir yoldaki verileri okumak ve değişiklikleri dinlemek için addValueEventListener()
yöntemini kullanarak DatabaseReference
öğesine ValueEventListener
ekleyin.
Dinleyici | Etkinlik geri çağırması | Tipik kullanım |
---|---|---|
ValueEventListener |
onDataChange() |
Bir yolun tüm içeriğindeki değişiklikleri okuma ve dinleme. |
Etkinlik sırasında mevcut oldukları için belirli bir yoldaki içeriklerin statik anlık görüntüsünü okumak için onDataChange()
yöntemini kullanabilirsiniz. Bu yöntem, işleyici eklendiğinde bir kez ve alt öğeler dahil olmak üzere veriler her değiştiğinde tetiklenir. Etkinlik geri çağırmasına, alt veriler de dahil olmak üzere söz konusu 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
değerini döndürür.
Aşağıdaki örnekte, veritabanından bir yayının ayrıntılarını alan sosyal blog uygulaması gösterilmektedir:
Kotlin+KTX
val postListener = object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { // Get Post object and use the values to update the UI val post = dataSnapshot.getValue<Post>() // ... } override fun onCancelled(databaseError: DatabaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()) } } postReference.addValueEventListener(postListener)
Java
ValueEventListener postListener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Get Post object and use the values to update the UI Post post = dataSnapshot.getValue(Post.class); // .. } @Override public void onCancelled(DatabaseError databaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()); } }; mPostReference.addValueEventListener(postListener);
İşleyici, etkinlik sırasında veritabanında belirtilen konumda verileri içeren bir DataSnapshot
alır. Anlık görüntüde getValue()
çağrısı yapmak, verilerin Java nesne gösterimini döndürür. Konumda veri yoksa getValue()
işlevinin çağrılması null
sonucunu döndürür.
Bu örnekte ValueEventListener
, okuma işlemi iptal edildiğinde çağrılacak 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 oku
get() kullanarak bir kez okuma
SDK, uygulamanızın online veya çevrimdışı olması fark etmeksizin veritabanı sunucularıyla etkileşimleri yönetecek şekilde tasarlanmıştır.
Genel olarak, verilerde yapılan güncellemelerle ilgili olarak arka uçtan bildirim almak amacıyla verileri okumak için yukarıda açıklanan ValueEventListener
tekniklerini kullanmanız gerekir. Dinleme teknikleri, kullanımınızı ve faturalandırmanızı azaltır, kullanıcılarınıza dijital ve çevrimdışı ortamda en iyi deneyimi sunmak için optimize edilmiştir.
Verilere yalnızca bir kez ihtiyaç duyarsanız 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 kontrol eder ve değer hâlâ bulunamazsa hata döndürür.
Gereksiz get()
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ı dinleyici kullanılarak önlenebilir.
Kotlin+KTX
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
Java
mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
@Override
public void onComplete(@NonNull Task<DataSnapshot> task) {
if (!task.isSuccessful()) {
Log.e("firebase", "Error getting data", task.getException());
}
else {
Log.d("firebase", String.valueOf(task.getResult().getValue()));
}
}
});
Dinleyici kullanarak bir kez okuma
Bazı durumlarda, sunucuda güncellenmiş değeri kontrol etmek yerine yerel önbellekteki değerin hemen döndürülmesini isteyebilirsiniz. Bu 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 beklenmeyen veya etkin dinleme gerektirmesi beklenen veriler için faydalıdır. Örneğin, önceki örneklerde bahsedilen blog uygulaması, yeni bir yayın yazmaya başlayan kullanıcının profilini yüklemek için bu yöntemi kullanır.
Verileri güncelleme veya silme
Belirli alanları güncelle
Diğer alt düğümlerin üzerine yazmadan aynı anda bir düğümün belirli alt öğelerine yazmak için updateChildren()
yöntemini kullanın.
updateChildren()
çağrısı yapılırken, anahtarın yolunu belirterek alt düzey alt değerleri güncelleyebilirsiniz. Veriler daha iyi ölçeklendirmek 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ının şuna benzer bir Post
sınıfı olabilir:
Kotlin+KTX
@IgnoreExtraProperties data class Post( var uid: String? = "", var author: String? = "", var title: String? = "", var body: String? = "", var starCount: Int = 0, var stars: MutableMap<String, Boolean> = HashMap(), ) { @Exclude fun toMap(): Map<String, Any?> { return mapOf( "uid" to uid, "author" to author, "title" to title, "body" to body, "starCount" to starCount, "stars" to stars, ) } }
Java
@IgnoreExtraProperties public class Post { public String uid; public String author; public String title; public String body; public int starCount = 0; public Map<String, Boolean> stars = new HashMap<>(); public Post() { // Default constructor required for calls to DataSnapshot.getValue(Post.class) } public Post(String uid, String author, String title, String body) { this.uid = uid; this.author = author; this.title = title; this.body = body; } @Exclude public Map<String, Object> toMap() { HashMap<String, Object> result = new HashMap<>(); result.put("uid", uid); result.put("author", author); result.put("title", title); result.put("body", body); result.put("starCount", starCount); result.put("stars", stars); return result; } }
Bir yayın oluşturmak ve aynı anda bunu en son etkinlik feed'i ve yayınlayan kullanıcının etkinlik feed'iyle güncellemek için blog uygulaması aşağıdaki gibi bir kod kullanır:
Kotlin+KTX
private fun writeNewPost(userId: String, username: String, title: String, body: String) { // Create new post at /user-posts/$userid/$postid and at // /posts/$postid simultaneously val key = database.child("posts").push().key if (key == null) { Log.w(TAG, "Couldn't get push key for posts") return } val post = Post(userId, username, title, body) val postValues = post.toMap() val childUpdates = hashMapOf<String, Any>( "/posts/$key" to postValues, "/user-posts/$userId/$key" to postValues, ) database.updateChildren(childUpdates) }
Java
private void writeNewPost(String userId, String username, String title, String body) { // Create new post at /user-posts/$userid/$postid and at // /posts/$postid simultaneously String key = mDatabase.child("posts").push().getKey(); Post post = new Post(userId, username, title, body); Map<String, Object> postValues = post.toMap(); Map<String, Object> childUpdates = new HashMap<>(); childUpdates.put("/posts/" + key, postValues); childUpdates.put("/user-posts/" + userId + "/" + key, postValues); mDatabase.updateChildren(childUpdates); }
Bu örnekte, /posts/$postid
alanındaki tüm kullanıcılar için yayınları içeren düğümde bir yayın oluşturmak ve getKey()
ile aynı anda anahtarı almak üzere push()
kullanılmaktadır. Bu 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, tek bir updateChildren()
çağrısı yaparak JSON ağacındaki birden fazla konumda eş zamanlı güncellemeler yapabilirsiniz (örneğin, bu örneğin yeni yayını her iki konumda da nasıl oluşturduğu). Bu şekilde yapılan eş zamanlı güncellemeler son derece önemlidir: ya tüm güncellemeler başarılı olur ya da başarısız olur.
Tamamlama Geri Çağırması Ekle
Verilerinizin ne zaman kaydedildiğini öğrenmek istiyorsanız tamamlama dinleyici 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 işleyiciyi kullanır. Çağrı başarısız olursa dinleyiciye hatanın neden oluştuğunu belirten bir hata nesnesi iletilir.
Kotlin+KTX
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
Java
mDatabase.child("users").child(userId).setValue(user) .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { // Write was successful! // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Write failed // ... } });
Verileri silin
Verileri silmenin en basit yolu, söz konusu verilerin konumuna referans vererek removeValue()
öğesini çağırmaktır.
setValue()
veya updateChildren()
gibi başka bir yazma işlemi için null
değerini belirterek de silme işlemini yapabilirsiniz. Bu tekniği, tek bir API çağrısında birden çok alt öğeyi silmek için updateChildren()
ile kullanabilirsiniz.
Dinleyicileri ayır
Geri çağırma işlevleri, Firebase veritabanı referansınızda removeEventListener()
yöntemi çağrılarak kaldırılır.
İşleyici 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 çıkarmanız gerekir.
Bir üst işleyicide removeEventListener()
çağrısı yapıldığında, alt düğümlerde kayıtlı işleyiciler otomatik olarak kaldırılmaz. Geri çağırmanın kaldırılması için tüm alt işleyicilerde de removeEventListener()
çağrılmalıdır.
Verileri işlem olarak kaydet
Artımlı sayaçlar gibi eşzamanlı değişikliklere göre bozulabilecek verilerle çalışırken işlem işlemi kullanabilirsiniz. Bu işleme bir güncelleme işlevi ve bir isteğe bağlı tamamlama geri çağırması olmak üzere iki bağımsız değişken verirsiniz. 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, örnek sosyal blog uygulaması uygulamasında, kullanıcıların yayınlara yıldız eklemesine ve yıldızlarını kaldırmasına ve bir yayının kaç yıldız aldığını aşağıdaki şekilde takip etmesine olanak tanıyabilirsiniz:
Kotlin+KTX
private fun onStarClicked(postRef: DatabaseReference) { // ... postRef.runTransaction(object : Transaction.Handler { override fun doTransaction(mutableData: MutableData): Transaction.Result { val p = mutableData.getValue(Post::class.java) ?: return Transaction.success(mutableData) if (p.stars.containsKey(uid)) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1 p.stars.remove(uid) } else { // Star the post and add self to stars p.starCount = p.starCount + 1 p.stars[uid] = true } // Set value and report transaction success mutableData.value = p return Transaction.success(mutableData) } override fun onComplete( databaseError: DatabaseError?, committed: Boolean, currentData: DataSnapshot?, ) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError!!) } }) }
Java
private void onStarClicked(DatabaseReference postRef) { postRef.runTransaction(new Transaction.Handler() { @NonNull @Override public Transaction.Result doTransaction(@NonNull MutableData mutableData) { Post p = mutableData.getValue(Post.class); if (p == null) { return Transaction.success(mutableData); } if (p.stars.containsKey(getUid())) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1; p.stars.remove(getUid()); } else { // Star the post and add self to stars p.starCount = p.starCount + 1; p.stars.put(getUid(), true); } // Set value and report transaction success mutableData.setValue(p); return Transaction.success(mutableData); } @Override public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot currentData) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError); } }); }
Bir işlem kullanmak, birden çok kullanıcının aynı yayına aynı anda yıldız göstermesi veya istemcinin eski verileri varsa yıldız sayılarının yanlış belirlenmesini önler. İşlem reddedilirse sunucu geçerli değeri istemciye döndürür. İstemci de 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 tekrar eder.
Atomik sunucu tarafı artışları
Yukarıdaki kullanım örneğinde veritabanına iki değer yazıyoruz: yayına yıldız veren/yıldızı kaldıran kullanıcının kimliği ve artan 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+KTX
private fun onStarClicked(uid: String, key: String) { val updates: MutableMap<String, Any> = hashMapOf( "posts/$key/stars/$uid" to true, "posts/$key/starCount" to ServerValue.increment(1), "user-posts/$uid/$key/stars/$uid" to true, "user-posts/$uid/$key/starCount" to ServerValue.increment(1), ) database.updateChildren(updates) }
Java
private void onStarClicked(String uid, String key) { Map<String, Object> updates = new HashMap<>(); updates.put("posts/"+key+"/stars/"+uid, true); updates.put("posts/"+key+"/starCount", ServerValue.increment(1)); updates.put("user-posts/"+uid+"/"+key+"/stars/"+uid, true); updates.put("user-posts/"+uid+"/"+key+"/starCount", ServerValue.increment(1)); mDatabase.updateChildren(updates); }
Bu kod bir işlem işlemini kullanmaz. Bu nedenle, çakışan bir güncelleme olduğunda otomatik olarak yeniden çalıştırılmaz. Bununla birlikte, 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 yıldız eklemesi gibi uygulamaya özgü çakışmaları tespit etmek ve 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, işleyicilerin kullanılmakta olduğu veya sunucuyla senkronize edilmek üzere işaretlenen tüm verilerin kendi dahili sürümlerini korur. 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 yerel etkinlikleri sunucuyla herhangi bir etkileşimden hemen önce tetikler. Bu, uygulamanızın ağ gecikmesi veya bağlantıdan bağımsız olarak yanıt vermeye devam edeceği anlamına gelir.
Bağlantı yeniden kurulduktan sonra uygulamanız, uygun etkinlik grubunu alır. Böylece, istemci herhangi bir özel kod yazmak zorunda kalmadan mevcut sunucu durumuyla senkronize edilir.
Online ve çevrimdışı özellikler hakkında daha fazla bilgi bölümünde çevrimdışı davranış hakkında daha fazla bilgi vereceğiz.