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

Mengaktifkan Kapabilitas Offline di Android

Aplikasi Firebase tetap berfungsi bahkan ketika aplikasi Anda kehilangan koneksi jaringan untuk sementara. Selain itu, Firebase menyediakan fitur untuk menampung data secara lokal, mengelola kehadiran, dan menangani latensi.

Persistensi Disk

Aplikasi Firebase menangani gangguan sementara pada jaringan secara otomatis. Data yang tersimpan di cache akan tersedia saat offline dan Firebase akan mengirim ulang setiap operasi tulis saat konektivitas jaringan pulih.

Jika Anda mengaktifkan persistensi disk, aplikasi Anda akan menulis data secara lokal ke perangkat sehingga aplikasi dapat mempertahankan status saat offline, bahkan saat aplikasi dimulai ulang oleh pengguna atau sistem operasi.

Anda dapat mengaktifkan persistensi disk hanya dengan satu baris kode.

Java
Android

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

Kotlin
Android

FirebaseDatabase.getInstance().setPersistenceEnabled(true)

Perilaku Persistensi

Dengan mengaktifkan persistensi, semua data yang disinkronkan oleh klien Firebase Realtime Database saat online akan tersimpan ke disk dan tersedia secara offline, bahkan saat aplikasi dimulai ulang oleh pengguna atau sistem operasi. Artinya, aplikasi berfungsi seperti saat online dengan menggunakan data lokal yang tersimpan di cache. Callback listener akan terus aktif untuk update lokal.

Klien Firebase Realtime Database secara otomatis menyimpan antrean semua operasi tulis yang dijalankan selagi aplikasi offline. Saat persistensi diaktifkan, antrean ini juga akan disimpan ke disk sehingga semua operasi tulis tersedia saat aplikasi dimulai ulang oleh pengguna atau sistem operasi. Setelah konektivitas aplikasi pulih, semua operasi tersebut akan dikirim ke server Firebase Realtime Database.

Jika aplikasi Anda menggunakan Firebase Authentication, klien Firebase Realtime Database akan menyimpan token autentikasi pengguna setiap kali aplikasi dimulai ulang. Jika masa pakai token autentikasi habis selagi aplikasi offline, klien akan menjeda operasi tulis hingga autentikasi ulang dilakukan. Jika tidak, operasi tulis mungkin akan gagal karena adanya aturan keamanan.

Menjaga Data Tetap Baru

Firebase Realtime Database menyinkronkan dan menyimpan salinan lokal data untuk listener aktif. Selain itu, Anda dapat terus menyinkronkan lokasi tertentu.

Java
Android

DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.keepSynced(true);

Kotlin
Android

val scoresRef = FirebaseDatabase.getInstance().getReference("scores")
scoresRef.keepSynced(true)

Klien Firebase Realtime Database akan otomatis mendownload data di lokasi tersebut dan terus menyinkronkannya meskipun referensi itu tidak memiliki listener aktif. Anda dapat menonaktifkan sinkronisasi dengan baris kode berikut.

Java
Android

scoresRef.keepSynced(false);

Kotlin
Android

scoresRef.keepSynced(false)

Secara default, data sebesar 10 MB yang telah disinkronkan sebelumnya akan disimpan ke dalam cache. Jumlah ini tentunya cukup untuk sebagian besar aplikasi. Jika ukuran cache melebihi ukuran yang dikonfigurasi, Firebase Realtime Database akan menghapus data yang paling lama tidak digunakan. Data yang terus disinkronkan tidak akan dihapus dari cache.

Memproses Kueri Data Secara Offline

Firebase Realtime Database menyimpan data yang ditampilkan dari kueri untuk digunakan saat offline. Untuk kueri yang dibuat saat offline, Firebase Realtime Database akan terus berfungsi untuk data yang dimuat sebelumnya. Jika data yang diminta belum dimuat, Firebase Realtime Database akan memuat data dari cache lokal. Setelah konektivitas jaringan kembali tersedia, data akan dimuat dan mencerminkan kueri tersebut.

Misalnya, kode berikut memproses kueri untuk empat item terakhir dalam Firebase Realtime Database untuk skor

Java
Android

DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.orderByValue().limitToLast(4).addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(@NonNull DataSnapshot snapshot, String previousChild) {
        Log.d(TAG, "The " + snapshot.getKey() + " dinosaur's score is " + snapshot.getValue());
    }

    // ...
});

Kotlin
Android

val scoresRef = FirebaseDatabase.getInstance().getReference("scores")
scoresRef.orderByValue().limitToLast(4).addChildEventListener(object : ChildEventListener {
    override fun onChildAdded(snapshot: DataSnapshot, previousChild: String?) {
        Log.d(TAG, "The ${snapshot.key} dinosaur's score is ${snapshot.value}")
    }

    // ...
})

Asumsikan pengguna kehilangan koneksi, statusnya menjadi offline, dan memulai ulang aplikasi. Selagi masih offline, aplikasi akan memproses kueri untuk 2 item terakhir dari lokasi yang sama. Kueri ini akan berhasil menampilkan dua item terakhir karena keempat item telah dimuat dalam kueri di atas.

Java
Android

scoresRef.orderByValue().limitToLast(2).addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(@NonNull DataSnapshot snapshot, String previousChild) {
        Log.d(TAG, "The " + snapshot.getKey() + " dinosaur's score is " + snapshot.getValue());
    }

    // ...
});

Kotlin
Android

scoresRef.orderByValue().limitToLast(2).addChildEventListener(object : ChildEventListener {
    override fun onChildAdded(snapshot: DataSnapshot, previousChild: String?) {
        Log.d(TAG, "The ${snapshot.key} dinosaur's score is ${snapshot.value}")
    }

    // ...
})

Pada contoh sebelumnya, klien Firebase Realtime Database memunculkan peristiwa 'turunan ditambahkan' untuk dua dinosaurus dengan skor tertinggi lewat cache yang disimpan. Namun itu tidak akan memunculkan peristiwa 'nilai', karena aplikasi tidak pernah mengeksekusi kueri tersebut selagi online.

Jika aplikasi ingin meminta keenam item terakhir itu selagi offline, peristiwa 'turunan ditambahkan' untuk keempat item yang disimpan dalam cache akan langsung dimunculkan. Saat perangkat kembali online, klien Firebase Realtime Database akan melakukan sinkronisasi dengan server dan memunculkan kedua peristiwa terakhir 'turunan ditambahkan' dan 'nilai' untuk aplikasi.

Menangani Transaksi Saat Offline

Setiap transaksi yang dijalankan sewaktu aplikasi offline akan dimasukkan ke dalam antrean. Setelah aplikasi kembali online, transaksi akan dikirim ke server Realtime Database.

Mengelola Kehadiran

Pada aplikasi realtime, mendeteksi kapan klien membuat dan memutus koneksi sering kali bermanfaat. Misalnya, Anda mungkin ingin menandai pengguna sebagai 'offline' saat kliennya memutus koneksi.

Klien Firebase Database menyediakan primitif sederhana yang dapat Anda gunakan untuk menulis ke database saat klien terputus dari server Firebase Database. Update ini akan terjadi, terlepas dari apakah klien terputus secara rapi atau tidak, sehingga Anda dapat mengandalkannya untuk membersihkan data bahkan saat koneksi terputus atau klien mengalami error. Semua operasi tulis, termasuk menyetel, mengupdate, dan menghapus, dapat dilakukan setelah koneksi terputus.

Berikut adalah contoh sederhana penulisan data setelah koneksi terputus menggunakan primitif onDisconnect:

Java
Android

DatabaseReference presenceRef = FirebaseDatabase.getInstance().getReference("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!");

Kotlin
Android

val presenceRef = FirebaseDatabase.getInstance().getReference("disconnectmessage")
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!")

Cara Kerja onDisconnect

Ketika Anda membuat operasi onDisconnect(), operasi tersebut berlangsung di server Firebase Realtime Database. Server akan memeriksa keamanan untuk memastikan bahwa pengguna dapat menjalankan peristiwa tulis yang diminta, dan memberi tahu aplikasi Anda jika peristiwa tersebut tidak valid. Selanjutnya, server akan memantau koneksinya. Jika sewaktu-waktu koneksi terputus atau ditutup secara aktif oleh klien Realtime Database, server akan memeriksa keamanan untuk kedua kalinya (untuk memastikan operasi tetap valid) dan kemudian memanggil peristiwa tersebut.

Aplikasi Anda dapat menggunakan callback pada operasi tulis untuk memastikan onDisconnect terpasang dengan benar:

Java
Android

presenceRef.onDisconnect().removeValue(new DatabaseReference.CompletionListener() {
    @Override
    public void onComplete(DatabaseError error, @NonNull DatabaseReference reference) {
        if (error != null) {
            Log.d(TAG, "could not establish onDisconnect event:" + error.getMessage());
        }
    }
});

Kotlin
Android

presenceRef.onDisconnect().removeValue { error, reference ->
    error?.let {
        Log.d(TAG, "could not establish onDisconnect event: ${error.message}")
    }
}

Peristiwa onDisconnect juga dapat dibatalkan dengan memanggil .cancel():

Java
Android

OnDisconnect onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.setValue("I disconnected");
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel();

Kotlin
Android

val onDisconnectRef = presenceRef.onDisconnect()
onDisconnectRef.setValue("I disconnected")
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel()

Mendeteksi Status Koneksi

Untuk banyak fitur yang terkait dengan kehadiran, aplikasi perlu mengetahui apakah statusnya online atau offline. Firebase Realtime Database menyediakan lokasi khusus di /.info/connected yang diperbarui setiap kali status koneksi klien Firebase Realtime Database berubah. Berikut ini contohnya:

Java
Android

DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        boolean connected = snapshot.getValue(Boolean.class);
        if (connected) {
            Log.d(TAG, "connected");
        } else {
            Log.d(TAG, "not connected");
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        Log.w(TAG, "Listener was cancelled");
    }
});

Kotlin
Android

val connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected")
connectedRef.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        val connected = snapshot.getValue(Boolean::class.java) ?: false
        if (connected) {
            Log.d(TAG, "connected")
        } else {
            Log.d(TAG, "not connected")
        }
    }

    override fun onCancelled(error: DatabaseError) {
        Log.w(TAG, "Listener was cancelled")
    }
})

/.info/connected adalah nilai boolean yang tidak disinkronkan antara klien Realtime Database karena nilainya bergantung pada status klien. Dengan kata lain, jika 1 klien membaca /.info/connected sebagai 'false', tidak ada jaminan bahwa klien lain juga akan membacanya sebagai 'false'.

Pada Android, Firebase mengelola status koneksi secara otomatis untuk mengurangi bandwidth dan penggunaan daya baterai. Jika klien tidak memiliki listener aktif, tidak memiliki operasi tulis atau onDisconnect yang tertunda, dan tidak secara eksplisit diputus dengan metode goOffline, Firebase akan menutup koneksi setelah 60 detik tanpa aktivitas.

Menangani Latensi

Stempel Waktu Server

Server Firebase Realtime Database memiliki mekanisme untuk menyisipkan stempel waktu yang dibuat di server sebagai data. Fitur ini, digabungkan dengan onDisconnect, memberikan cara mudah dan andal untuk mencatat kapan koneksi klien Realtime Database terputus:

Java
Android

DatabaseReference userLastOnlineRef = FirebaseDatabase.getInstance().getReference("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);

Kotlin
Android

val userLastOnlineRef = FirebaseDatabase.getInstance().getReference("users/joe/lastOnline")
userLastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP)

Clock Skew

Meskipun firebase.database.ServerValue.TIMESTAMP jauh lebih akurat dan lebih cocok untuk sebagian besar operasi baca/tulis, terkadang ada baiknya memperkirakan clock skew klien yang terkait dengan server Firebase Realtime Database. Anda dapat menambahkan callback ke lokasi /.info/serverTimeOffset untuk mendapatkan nilai, dalam milidetik, yang ditambahkan oleh klien Firebase Realtime Database ke waktu lokal yang dilaporkan (periode waktu dalam milidetik) untuk memperkirakan waktu server. Perlu diperhatikan bahwa akurasi selisih ini dapat dipengaruhi oleh latensi jaringan, sehingga hanya bermanfaat untuk menemukan selisih waktu jam yang besar (> satu detik).

Java
Android

DatabaseReference offsetRef = FirebaseDatabase.getInstance().getReference(".info/serverTimeOffset");
offsetRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        double offset = snapshot.getValue(Double.class);
        double estimatedServerTimeMs = System.currentTimeMillis() + offset;
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        Log.w(TAG, "Listener was cancelled");
    }
});

Kotlin
Android

val offsetRef = FirebaseDatabase.getInstance().getReference(".info/serverTimeOffset")
offsetRef.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        val offset = snapshot.getValue(Double::class.java) ?: 0.0
        val estimatedServerTimeMs = System.currentTimeMillis() + offset
    }

    override fun onCancelled(error: DatabaseError) {
        Log.w(TAG, "Listener was cancelled")
    }
})

Contoh Aplikasi Kehadiran

Dengan menggabungkan operasi pemutusan koneksi dengan pemantauan status koneksi dan stempel waktu server, Anda dapat membangun sistem kehadiran pengguna. Dalam sistem ini, setiap pengguna menyimpan data di lokasi database untuk menunjukkan apakah klien Realtime Database sedang online atau tidak. Klien akan menetapkan lokasi ini ke 'true' jika mereka online dan ke stempel waktu jika mereka memutuskan koneksi. Stempel waktu ini menunjukkan kapan terakhir kali pengguna tertentu online.

Perlu diperhatikan bahwa aplikasi Anda harus mengantrekan operasi pemutusan koneksi sebelum pengguna ditandai online, untuk menghindari kondisi race jika sewaktu-waktu koneksi jaringan klien terputus sebelum kedua perintah dapat dikirimkan ke server.

Berikut adalah contoh sederhana dari sistem kehadiran pengguna:

Java
Android

// Since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
final FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference myConnectionsRef = database.getReference("users/joe/connections");

// Stores the timestamp of my last disconnect (the last time I was seen online)
final DatabaseReference lastOnlineRef = database.getReference("/users/joe/lastOnline");

final DatabaseReference connectedRef = database.getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot snapshot) {
        boolean connected = snapshot.getValue(Boolean.class);
        if (connected) {
            DatabaseReference con = myConnectionsRef.push();

            // When this device disconnects, remove it
            con.onDisconnect().removeValue();

            // When I disconnect, update the last time I was seen online
            lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);

            // Add this device to my connections list
            // this value could contain info about the device or a timestamp too
            con.setValue(Boolean.TRUE);
        }
    }

    @Override
    public void onCancelled(DatabaseError error) {
        Log.w(TAG, "Listener was cancelled at .info/connected");
    }
});

Kotlin
Android

// Since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
val database = FirebaseDatabase.getInstance()
val myConnectionsRef = database.getReference("users/joe/connections")

// Stores the timestamp of my last disconnect (the last time I was seen online)
val lastOnlineRef = database.getReference("/users/joe/lastOnline")

val connectedRef = database.getReference(".info/connected")
connectedRef.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        val connected = snapshot.getValue(Boolean::class.java) ?: false
        if (connected) {
            val con = myConnectionsRef.push()

            // When this device disconnects, remove it
            con.onDisconnect().removeValue()

            // When I disconnect, update the last time I was seen online
            lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP)

            // Add this device to my connections list
            // this value could contain info about the device or a timestamp too
            con.setValue(java.lang.Boolean.TRUE)
        }
    }

    override fun onCancelled(error: DatabaseError) {
        Log.w(TAG, "Listener was cancelled at .info/connected")
    }
})