Buka konsol
Coba Cloud Firestore: Lihat database yang skalabel dan fleksibel dari Firebase dan Google Cloud Platform. Pelajari Cloud Firestore lebih lanjut.

Menangani Daftar Data di Android

Dokumen ini menjelaskan cara menangani daftar data di Firebase. Untuk mempelajari dasar-dasar pembacaan dan penulisan data di Firebase, lihat Membaca dan Menulis Data di Android.

Mendapatkan DatabaseReference

Untuk membaca dan menulis data dari database, Anda memerlukan instance DatabaseReference:

Java
Android

private DatabaseReference mDatabase;// ...
mDatabase = FirebaseDatabase.getInstance().getReference();

Kotlin
Android

private lateinit var database: DatabaseReference// ...
database = FirebaseDatabase.getInstance().reference

Membaca dan menulis daftar

Menambahkan ke daftar data

Gunakan metode push() untuk menambahkan data ke daftar pada aplikasi multipengguna. Metode push() menghasilkan kunci unik setiap kali turunan baru ditambahkan ke referensi Firebase yang ditentukan. Dengan kunci yang dihasilkan secara otomatis untuk setiap elemen baru pada daftar, beberapa klien dapat menambahkan turunan ke lokasi yang sama secara bersamaan tanpa mengalami konflik penulisan. Kunci unik yang dihasilkan oleh push() didasarkan pada stempel waktu, sehingga daftar item otomatis diurutkan secara kronologis.

Anda dapat menggunakan referensi ke data baru yang ditampilkan oleh metode push() untuk memperoleh nilai kunci yang dihasilkan secara otomatis untuk turunan atau menetapkan data untuk turunan tersebut. Memanggil getKey() pada referensi push() akan menampilkan nilai untuk kunci yang dihasilkan secara otomatis.

Anda dapat menggunakan kunci yang dihasilkan secara otomatis ini untuk mempermudah perataan struktur data. Untuk mengetahui informasi lebih lanjut, lihat contoh data fan-out.

Mendeteksi peristiwa turunan

Saat menangani daftar, aplikasi Anda harus memantau peristiwa turunan, bukan peristiwa nilai yang digunakan untuk objek tunggal.

Peristiwa turunan dipicu sebagai respons terhadap operasi tertentu yang terjadi pada turunan node dari suatu operasi, seperti turunan baru yang ditambahkan melalui metode push() atau turunan yang diupdate melalui metode updateChildren(). Secara bersama-sama, setiap metode ini berguna untuk mendeteksi perubahan pada node tertentu pada database.

Untuk mendeteksi peristiwa turunan pada DatabaseReference, tambahkan ChildEventListener:

Listener Callback peristiwa Penggunaan standar
ChildEventListener onChildAdded() Mengambil daftar item atau mendeteksi penambahan daftar item. Callback ini dipicu 1 kali untuk setiap turunan yang ada, dan dipicu lagi setiap kali ada turunan baru yang ditambahkan ke lokasi yang ditetapkan. DataSnapshot yang diteruskan ke listener memuat data dari turunan baru.
onChildChanged() Mendeteksi perubahan pada item dalam daftar. Peristiwa ini dipicu setiap kali node turunan diubah, termasuk setiap perubahan pada turunan dari node turunan. DataSnapshot yang diteruskan ke listener peristiwa memuat data yang telah diupdate untuk turunan tersebut.
onChildRemoved() Mendeteksi item yang sedang dihapus dari daftar. DataSnapshot yang diteruskan ke callback peristiwa memuat data untuk turunan yang dihapus.
onChildMoved() Mendeteksi perubahan urutan item dalam daftar terurut. Peristiwa ini dipicu setiap kali callback onChildChanged() dijalankan melalui update yang menyebabkan pengurutan ulang turunan. Peristiwa ini digunakan dengan data yang diurutkan dengan orderByChild atau orderByValue.

Misalnya, aplikasi blogging sosial mungkin menggunakan metode ini secara bersama-sama untuk memantau aktivitas dalam komentar suatu postingan, seperti terlihat di bawah ini:

Java
Android

ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

        // A new comment has been added, add it to the displayed list
        Comment comment = dataSnapshot.getValue(Comment.class);

        // ...
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        Comment newComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        Comment movedComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException());
        Toast.makeText(mContext, "Failed to load comments.",
                Toast.LENGTH_SHORT).show();
    }
};
ref.addChildEventListener(childEventListener);

Kotlin
Android

val childEventListener = object : ChildEventListener {
    override fun onChildAdded(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.key!!)

        // A new comment has been added, add it to the displayed list
        val comment = dataSnapshot.getValue(Comment::class.java)

        // ...
    }

    override fun onChildChanged(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildChanged: ${dataSnapshot.key}")

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        val newComment = dataSnapshot.getValue(Comment::class.java)
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildRemoved(dataSnapshot: DataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.key!!)

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildMoved(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.key!!)

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        val movedComment = dataSnapshot.getValue(Comment::class.java)
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onCancelled(databaseError: DatabaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException())
        Toast.makeText(context, "Failed to load comments.",
                Toast.LENGTH_SHORT).show()
    }
}
databaseReference.addChildEventListener(childEventListener)

Mendeteksi peristiwa nilai

Meskipun penggunaan ChildEventListener direkomendasikan untuk membaca daftar data, ada situasi ketika menambahkan ValueEventListener ke daftar referensi akan berguna.

Penambahan ValueEventListener ke daftar data akan menampilkan daftar data secara keseluruhan sebagai DataSnapshot tunggal, yang nantinya dapat di-loop untuk mengakses masing-masing turunan.

Sekalipun hanya ada 1 kueri yang cocok, snapshot tetaplah sebuah daftar yang hanya memuat 1 item. Untuk mengakses item tersebut, Anda harus melakukan loop pada hasil:

Java
Android

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

Kotlin
Android

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

Pola ini akan berguna saat Anda ingin mengambil semua turunan dari daftar dalam satu operasi, bukan mendeteksi peristiwa onChildAdded tambahan.

Melepas listener

Callback dihapus dengan memanggil metode removeEventListener() di referensi database Firebase Anda.

Jika listener telah ditambahkan beberapa kali ke lokasi data, listener tersebut akan dipanggil beberapa kali untuk setiap peristiwa, dan Anda harus melepasnya dalam jumlah yang sama seperti saat menambahkannya agar terhapus semuanya.

Memanggil removeEventListener() pada listener induk tidak akan otomatis menghapus listener yang terdaftar pada node turunannya; removeEventListener() juga harus dipanggil pada setiap listener turunan untuk menghapus callback.

Mengurutkan dan memfilter data

Anda bisa menggunakan class Query Realtime Database untuk memperoleh data yang diurutkan berdasarkan kunci, nilai, atau nilai turunan. Anda juga dapat memfilter hasil pengurutan ke jumlah hasil tertentu atau ke rentang kunci atau nilai.

Mengurutkan data

Untuk mengambil data yang diurutkan, mulai dengan menentukan salah satu metode 'urutkan berdasarkan' untuk menentukan cara pengurutan hasil.

Metode Penggunaan
orderByChild() Mengurutkan hasil berdasarkan nilai kunci turunan yang ditentukan atau lokasi turunan bertingkat.
orderByKey() Mengurutkan hasil berdasarkan kunci turunan.
orderByValue() Mengurutkan hasil berdasarkan nilai turunan.

Anda hanya bisa menggunakan 1 metode 'urutkan berdasarkan' pada satu waktu. Memanggil metode 'urutkan berdasarkan' beberapa kali dalam kueri yang sama akan menyebabkan error.

Contoh berikut menunjukkan cara mengambil daftar postingan teratas milik pengguna yang diurutkan berdasarkan jumlah bintangnya:

Java
Android

// My top posts by number of stars
String myUserId = getUid();
Query myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
        .orderByChild("starCount");
myTopPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Kotlin
Android

// My top posts by number of stars
val myUserId = uid
val myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
    .orderByChild("starCount")

myTopPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

Ini menentukan kueri yang, jika dikombinasikan dengan listener turunan, akan menyinkronkan klien dengan postingan pengguna dari lokasi dalam database berdasarkan ID penggunanya, yang diurutkan berdasarkan jumlah bintang yang diterima setiap postingan. Teknik penggunaan ID sebagai kunci indeks ini disebut fan-out data. Anda dapat membaca lebih lanjut tentang hal ini di bagian Membuat Struktur Database Anda.

Panggilan ke metode orderByChild() menetapkan kunci turunan sebagai dasar untuk mengurutkan hasil. Dalam kasus ini, postingan diurutkan berdasarkan nilai turunan "starCount" masing-masing. Kueri juga dapat diurutkan berdasarkan turunan bertingkat, jika Anda memiliki data yang tampak seperti ini:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

Dalam contoh ini, kita dapat mengurutkan elemen daftar berdasarkan nilai yang bersarang dalam kunci metrics dengan menentukan lokasi relatif ke turunan bertingkat pada panggilan orderByChild().

Java
Android

// Most viewed posts
Query myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views");
myMostViewedPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Kotlin
Android

// Most viewed posts
val myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views")
myMostViewedPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

Untuk informasi lebih lanjut mengenai metode pengurutan jenis data lainnya, lihat Metode pengurutan data kueri.

Memfilter data

Untuk memfilter data, Anda dapat menggabungkan salah satu metode batas atau rentang dengan metode 'urutkan berdasarkan' ketika menyusun kueri.

Metode Penggunaan
limitToFirst() Menetapkan jumlah item maksimum untuk ditampilkan dari awal daftar hasil yang diurutkan.
limitToLast() Menetapkan jumlah item maksimum untuk ditampilkan dari akhir daftar hasil yang diurutkan.
startAt() Menampilkan item yang lebih besar atau sama dengan kunci atau nilai yang ditentukan, tergantung metode 'urutkan berdasarkan' yang dipilih.
endAt() Menampilkan item yang lebih kecil atau sama dengan kunci atau nilai yang ditentukan, tergantung metode 'urutkan berdasarkan' yang dipilih.
equalTo() Menampilkan item yang sama dengan kunci atau nilai yang ditentukan, tergantung metode 'urutkan berdasarkan' yang dipilih.

Berbeda dengan metode 'urutkan berdasarkan', Anda dapat menggabungkan beberapa fungsi batas atau rentang. Misalnya, Anda dapat menggabungkan metode startAt() dan endAt() untuk membatasi hasil ke rentang nilai tertentu.

Sekalipun hanya ada 1 kueri yang cocok, snapshot tetaplah sebuah daftar yang hanya memuat 1 item. Untuk mengakses item tersebut, Anda harus melakukan loop pada hasil:

Java
Android

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

Kotlin
Android

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

Membatasi jumlah hasil

Anda dapat menggunakan metode limitToFirst() dan limitToLast() untuk menetapkan jumlah maksimum turunan yang akan disinkronkan untuk callback tertentu. Misalnya, jika Anda menggunakan limitToFirst() untuk menetapkan batas 100, pada awalnya Anda hanya akan menerima hingga 100 callback onChildAdded(). Jika Anda memiliki kurang dari 100 item yang tersimpan di database Firebase, callback onChildAdded() akan dijalankan untuk setiap item.

Saat item berubah, Anda akan menerima callback onChildAdded() untuk item yang masuk ke kueri dan callback onChildRemoved() untuk item yang keluar, sehingga jumlah totalnya tetap 100.

Contoh berikut menunjukkan cara aplikasi blogging menentukan kueri untuk mengambil daftar 100 postingan terbaru oleh semua pengguna:

Java
Android

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
Query recentPostsQuery = databaseReference.child("posts")
        .limitToFirst(100);

Kotlin
Android

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys.
return databaseReference.child("posts")
        .limitToFirst(100)

Contoh ini hanya menetapkan kueri, yang harus memiliki listener untuk benar-benar menyinkronkan data.

Filter berdasarkan kunci atau nilai

Anda dapat menggunakan startAt(), endAt(), dan equalTo() untuk memilih titik awal, akhir, dan ekuivalen yang arbitrer untuk kueri. Hal ini dapat bermanfaat untuk pemberian nomor data atau menemukan item dengan turunan yang memiliki nilai tertentu.

Cara data kueri diurutkan

Bagian ini menjelaskan cara data diurutkan menggunakan setiap metode 'urutkan berdasarkan' di class Query.

orderByChild

Ketika menggunakan orderByChild(), data yang berisi kunci turunan yang ditentukan diurutkan sebagai berikut:

  1. Turunan yang memiliki nilai null untuk kunci turunan yang ditentukan akan muncul pertama.
  2. Turunan yang memiliki nilai false untuk kunci turunan yang ditentukan akan muncul berikutnya. Jika beberapa turunan memiliki nilai false, turunan tersebut akan diurutkan secara leksikografis berdasarkan kunci.
  3. Turunan yang memiliki nilai true untuk kunci turunan yang ditentukan akan muncul berikutnya. Jika beberapa turunan memiliki nilai true, turunan tersebut akan diurutkan secara leksikografis berdasarkan kunci.
  4. Turunan dengan nilai numerik akan muncul berikutnya, dan diurutkan dalam urutan menaik. Jika beberapa turunan memiliki nilai numerik yang sama untuk node turunan yang ditentukan, turunan tersebut akan diurutkan berdasarkan kunci.
  5. String muncul setelah angka, dan diurutkan secara leksikografis dalam urutan menaik. Jika beberapa turunan memiliki nilai yang sama untuk node turunan yang ditentukan, turunan tersebut akan diurutkan secara leksikografis berdasarkan kunci.
  6. Objek muncul terakhir, dan diurutkan secara leksikografis berdasarkan kunci dalam urutan menaik.

orderByKey

Ketika menggunakan orderByKey() untuk mengurutkan data, data akan ditampilkan dalam urutan menaik berdasarkan kunci.

  1. Turunan dengan kunci yang bisa diurai sebagai bilangan bulat 32-bit berada di urutan atas, diurutkan dalam urutan menaik.
  2. Turunan dengan nilai string sebagai kuncinya berada di urutan berikutnya, diurutkan secara leksikografis dalam urutan menaik.

orderByValue

Ketika menggunakan orderByValue(), turunan akan diurutkan berdasarkan nilainya. Kriteria urutan sama seperti di orderByChild(), perbedaannya yaitu yang digunakan adalah nilai node, bukan nilai kunci turunan yang ditentukan.

Langkah berikutnya