Bu belge, Firebase verilerini okuma ve yazmayla ilgili temel bilgileri kapsar.
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 değiştiğinde yeniden tetiklenir.
(İsteğe bağlı) Firebase Local Emulator Suite ile prototip oluşturun ve test edin
Uygulamanızın Realtime Database'den nasıl okuyup yazdığından bahsetmeden önce, Realtime Database işlevselliğini prototiplemek ve test etmek için kullanabileceğiniz bir dizi araç tanıtalım: 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ışabilmek harika bir fikir olabilir.
Gerçek Zamanlı Veritabanı öykünücüsü, uygulamanızın öykünülmüş veritabanı içeriğiniz ve yapılandırmanızın yanı sıra isteğe bağlı olarak öykünülmüş proje kaynaklarınızla (işlevler, diğer veritabanları ve güvenlik kuralları) etkileşim kurmasını sağlayan Yerel Öykünücü Paketi'nin bir parçasıdır.
Gerçek Zamanlı Veritabanı öykünücüsünü kullanmak yalnızca birkaç adımı içerir:
- Öykünücüye bağlanmak için uygulamanızın test yapılandırmasına bir kod satırı ekleme.
- Yerel proje dizininizin kökünden,
firebase emulators:start
. - Her zamanki gibi bir Realtime Database platformu SDK'sını veya Realtime Database REST API'sini kullanarak uygulamanızın prototip kodundan çağrı yapma.
Gerçek Zamanlı Veritabanı ve Bulut İşlevlerini içeren ayrıntılı bir kılavuz mevcuttur. Ayrıca Local Emulator Suite tanıtımına da göz atmalısınız.
Bir Veritabanı Referansı Alın
Veritabanından veri okumak veya yazmak için bir DatabaseReference
örneğine ihtiyacınız vardır:
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
Kotlin+KTX
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Veri yaz
Temel yazma işlemleri
Temel yazma işlemleri için, verileri belirtilen bir referansa kaydetmek için setValue()
'yi kullanabilir ve bu yoldaki mevcut tüm verileri değiştirebilirsiniz. Bu yöntemi aşağıdakiler için kullanabilirsiniz:
- Kullanılabilir JSON türlerine karşılık gelen türleri aşağıdaki gibi geçirin:
-
String
-
Long
-
Double
-
Boolean
-
Map<String, Object>
-
List<Object>
-
- Özel bir Java nesnesini iletin, onu tanımlayan sınıf, hiçbir argüman almayan ve atanacak özellikler için genel alıcılara sahip varsayılan bir kurucuya sahipse.
Bir Java nesnesi kullanıyorsanız, nesnenizin içeriği otomatik olarak alt konumlara iç içe geçmiş bir şekilde eşlenir. Bir Java nesnesi kullanmak da genellikle kodunuzu daha okunabilir ve bakımı daha kolay hale getirir. Örneğin, temel kullanıcı profiline sahip bir uygulamanız varsa, User
nesneniz aşağıdaki gibi görünebilir:
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; } }
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. }
setValue()
ile bir kullanıcıyı aşağıdaki gibi ekleyebilirsiniz:
Java
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
Kotlin+KTX
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
setValue()
'nin bu şekilde kullanılması, herhangi bir alt düğüm de dahil olmak üzere, belirtilen konumdaki verilerin üzerine yazar. Ancak, tüm nesneyi yeniden yazmadan bir 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:
Java
mDatabase.child("users").child(userId).child("username").setValue(name);
Kotlin+KTX
database.child("users").child(userId).child("username").setValue(name)
Verileri oku
Kalıcı dinleyicilerle verileri okuyun
Bir yoldaki verileri okumak ve değişiklikleri dinlemek için, bir DatabaseReference
ValueEventListener
eklemek için addValueEventListener()
yöntemini kullanın.
dinleyici | Olay geri arama | Tipik kullanım |
---|---|---|
ValueEventListener | onDataChange() | Bir yolun tüm içeriğindeki değişiklikleri okuyun ve dinleyin. |
İçeriğin belirli bir yoldaki statik anlık görüntüsünü, olay zamanında var oldukları şekliyle okumak için onDataChange()
yöntemini kullanabilirsiniz. Bu yöntem, dinleyici eklendiğinde bir kez ve çocuklar dahil veriler her değiştiğinde tekrar tetiklenir. Olay geri araması, alt veriler de dahil olmak üzere o konumdaki tüm verileri içeren bir anlık görüntüye iletilir. Herhangi bir veri yoksa, exists()
öğesini çağırdığınızda anlık görüntü false
, üzerinde getValue()
çağırdığınızda ise null
değerini döndürür.
Aşağıdaki örnek, veri tabanından bir gönderinin ayrıntılarını alan bir sosyal blog uygulamasını göstermektedir:
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);
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)
Dinleyici, olay anında veritabanında belirtilen konumdaki verileri içeren bir DataSnapshot
alır. Bir anlık görüntüde getValue()
çağrılması, verilerin Java nesne temsilini döndürür. Konumda veri yoksa, getValue()
öğesinin çağrılması null
değerini döndürür.
Bu örnekte ValueEventListener
, okuma iptal edildiğinde çağrılan onCancelled()
yöntemini de tanımlar. Örneğin, istemcinin bir Firebase veritabanı konumundan okuma izni yoksa bir okuma iptal edilebilir. Bu yönteme, hatanın neden oluştuğunu belirten bir DatabaseError
nesnesi iletilir.
Verileri bir kez oku
get() kullanarak bir kez okuyun
SDK, uygulamanız ister çevrimiçi ister çevrimdışı olsun, veritabanı sunucularıyla etkileşimleri yönetmek için tasarlanmıştır.
Genel olarak, arka uçtan verilerde yapılan güncellemelerden haberdar olmak için verileri okumak için yukarıda açıklanan ValueEventListener
tekniklerini kullanmalısınız. 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 sunmak için optimize edilmiştir.
Verilere yalnızca bir kez ihtiyacınız varsa, veri tabanından verilerin anlık görüntüsünü almak için get()
öğesini kullanabilirsiniz. Herhangi bir nedenle get()
sunucu değerini döndüremezse, istemci yerel depolama önbelleğini araştırır ve değer hala bulunamazsa bir hata döndürür.
Get get()
'in gereksiz kullanımı bant genişliği kullanımını artırabilir ve yukarıda gösterildiği gibi gerçek zamanlı bir dinleyici kullanılarak önlenebilecek performans kaybına yol açabilir.
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()));
}
}
});
Kotlin+KTX
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
Dinleyici kullanarak bir kez okuyun
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 durumlarda, verileri yerel disk önbelleğinden hemen almak için addListenerForSingleValueEvent
kullanabilirsiniz.
Bu, yalnızca bir kez yüklenmesi gereken ve sık değişmesi beklenmeyen veya etkin dinleme gerektirmeyen veriler için kullanışlıdır. Örneğin, önceki örneklerdeki blog uygulaması, bir 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üncelle
Diğer alt düğümlerin üzerine yazmadan bir düğümün belirli çocuklarına aynı anda yazmak için updateChildren()
yöntemini kullanın.
updateChildren()
çağırırken, anahtar için bir yol belirterek alt düzey alt değerleri güncelleyebilirsiniz. Daha iyi ölçeklendirmek için veriler birden fazla konumda depolanıyorsa, veri yayma özelliğini kullanarak bu verilerin tüm örneklerini güncelleyebilirsiniz. Örneğin, bir sosyal blog uygulamasının aşağıdaki gibi bir Post
sınıfı olabilir:
Java
@IgnoreExtraProperties public class Post { public String uid; public String author; public String title; public String body; public int starCount = 0; public Map<String, Boolean> stars = new HashMap<>(); public Post() { // Default constructor required for calls to DataSnapshot.getValue(Post.class) } public Post(String uid, String author, String title, String body) { this.uid = uid; this.author = author; this.title = title; this.body = body; } @Exclude public Map<String, Object> toMap() { HashMap<String, Object> result = new HashMap<>(); result.put("uid", uid); result.put("author", author); result.put("title", title); result.put("body", body); result.put("starCount", starCount); result.put("stars", stars); return result; } }
Kotlin+KTX
@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 ) } }
Bir gönderi oluşturmak ve aynı anda onu en son etkinlik akışına ve gönderiyi gönderen kullanıcının etkinlik akışına güncellemek için blog uygulaması şuna benzer bir kod kullanır:
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); }
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) }
Bu örnek, /posts/$postid
postid konumundaki tüm kullanıcılar için gönderileri içeren düğümde bir gönderi oluşturmak ve aynı anda anahtarı getKey()
ile almak için push()
() kullanır. Anahtar daha sonra kullanıcının /user-posts/$userid/$postid
adresindeki gönderilerinde ikinci bir giriş oluşturmak için kullanılabilir.
Bu yolları kullanarak, bu örneğin her iki konumda yeni gönderiyi nasıl oluşturduğu gibi, updateChildren()
tek bir çağrıyla JSON ağacındaki birden çok konumda eşzamanlı güncellemeler gerçekleştirebilirsiniz. Bu şekilde yapılan eşzamanlı güncellemeler atomiktir: ya tüm güncellemeler başarılı olur ya da tüm güncellemeler başarısız olur.
Tamamlama Geri Araması Ekle
Verilerinizin ne zaman kaydedildiğini bilmek istiyorsanız, bir tamamlama dinleyicisi ekleyebilirsiniz. Hem setValue()
hem de updateChildren()
, yazma işlemi veritabanına başarıyla tamamlandığında çağrılan isteğe bağlı bir tamamlama dinleyicisi alır. Çağrı başarısız olursa, dinleyiciye hatanın neden oluştuğunu belirten bir hata nesnesi iletilir.
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 // ... } });
Kotlin+KTX
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
Verileri sil
Verileri silmenin en basit yolu, o verinin konumuna bir başvuruda removeValue()
ç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 çok çocuğu silmek için bu tekniği updateChildren()
ile kullanabilirsiniz.
Dinleyicileri ayır
Firebase veritabanı referansınızdaki removeEventListener()
yöntemi çağrılarak geri aramalar kaldırılır.
Bir dinleyici bir veri konumuna birden çok kez eklendiyse, her olay için birden çok kez çağrılır ve onu tamamen kaldırmak için aynı sayıda ayırmanız gerekir.
Bir üst dinleyicide removeEventListener()
öğesinin çağrılması, alt düğümlerinde kayıtlı dinleyicileri otomatik olarak kaldırmaz; removeEventListener()
, geri aramayı kaldırmak için tüm alt dinleyicilerde de çağrılmalıdır.
Verileri işlem olarak kaydedin
Artımlı sayaçlar gibi eşzamanlı değişikliklerle bozulabilecek verilerle çalışırken, bir işlem işlemi kullanabilirsiniz. Bu işleme iki argüman verirsiniz: bir güncelleme işlevi ve isteğe bağlı bir tamamlama geri çağrısı. Güncelleme işlevi, verilerin mevcut durumunu bir argüman olarak alır ve yazmak istediğiniz yeni istenen 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ında, kullanıcıların gönderilere yıldız eklemesine ve yıldızları kaldırmasına ve bir gönderinin kaç yıldız aldığını aşağıdaki gibi takip etmesine izin verebilirsiniz:
Java
private void onStarClicked(DatabaseReference postRef) { postRef.runTransaction(new Transaction.Handler() { @Override public Transaction.Result doTransaction(MutableData mutableData) { Post p = mutableData.getValue(Post.class); if (p == null) { return Transaction.success(mutableData); } if (p.stars.containsKey(getUid())) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1; p.stars.remove(getUid()); } else { // Star the post and add self to stars p.starCount = p.starCount + 1; p.stars.put(getUid(), true); } // Set value and report transaction success mutableData.setValue(p); return Transaction.success(mutableData); } @Override public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot currentData) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError); } }); }
Kotlin+KTX
private fun onStarClicked(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!!) } }) }
Bir işlemin kullanılması, birden fazla kullanıcının aynı gönderiye aynı anda yıldız vermesi veya istemcinin eski verileri olması durumunda yıldız sayılarının yanlış olmasını önler. İşlem reddedilirse, sunucu, işlemi güncellenen değerle yeniden çalıştıran istemciye mevcut değeri döndürür. Bu, işlem kabul edilene veya çok fazla deneme yapılana kadar tekrarlanır.
Atomik sunucu tarafı artışları
Yukarıdaki kullanım durumunda, veritabanına iki değer yazıyoruz: gönderiye yıldız atan/yıldızı kaldıran kullanıcının kimliği ve artan yıldız sayısı. Kullanıcının gönderiye yıldız eklediğini zaten biliyorsak, işlem yerine atomik bir artış işlemi kullanabiliriz.
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); }
Kotlin+KTX
private fun onStarClicked(uid: String, key: String) { val updates: MutableMap<String, Any> = HashMap() updates["posts/$key/stars/$uid"] = true updates["posts/$key/starCount"] = ServerValue.increment(1) updates["user-posts/$uid/$key/stars/$uid"] = true updates["user-posts/$uid/$key/starCount"] = ServerValue.increment(1) database.updateChildren(updates) }
Bu kod bir işlem işlemi kullanmaz, bu nedenle çakışan bir güncelleme varsa 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.
Bir kullanıcının daha önce yıldız eklemiş olduğu bir gönderiye yıldız eklemesi gibi uygulamaya özel çakışmaları algılamak ve reddetmek istiyorsanız, bu kullanım durumu için özel güvenlik kuralları yazmalısınız.
Verilerle çevrimdışı çalışın
Bir istemci ağ bağlantısını kaybederse uygulamanız düzgün şekilde çalışmaya devam eder.
Bir Firebase veritabanına bağlı her istemci, üzerinde dinleyicilerin kullanıldığı veya sunucuyla senkronize tutulmak üzere işaretlenen herhangi bir verinin kendi dahili sürümünü tutar. Veri okunurken veya yazılırken, önce verinin bu yerel versiyonu 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 hemen önce yerel olayları tetikler. Bu, uygulamanızın ağ gecikmesinden veya bağlantıdan bağımsız olarak yanıt vermeye devam edeceği anlamına gelir.
Bağlantı yeniden kurulduğunda, uygulamanız uygun olay kümesini alır, böylece istemci herhangi bir özel kod yazmak zorunda kalmadan mevcut sunucu durumuyla eşitlenir.
Çevrimiçi ve çevrimdışı yetenekler hakkında daha fazla bilgi edinin bölümünde çevrimdışı davranış hakkında daha fazla konuşacağız.
Sonraki adımlar
- Veri listeleriyle çalışma
- Verileri nasıl yapılandıracağınızı öğrenin
- Çevrimiçi ve çevrimdışı yetenekler hakkında daha fazla bilgi edinin