Ikuti semua informasi yang diumumkan di Firebase Summit, dan pelajari bagaimana Firebase dapat membantu Anda mempercepat pengembangan aplikasi dan menjalankan aplikasi dengan percaya diri. Pelajari Lebih Lanjut

Mengaktifkan Kemampuan Offline di Android

Aplikasi Firebase berfungsi meskipun aplikasi Anda kehilangan koneksi jaringannya untuk sementara. Selain itu, Firebase menyediakan alat untuk menyimpan data secara lokal, mengelola keberadaan, dan menangani latensi.

Kegigihan Disk

Aplikasi Firebase secara otomatis menangani gangguan jaringan sementara. Data yang di-cache tersedia saat offline dan Firebase mengirim ulang setiap penulisan saat konektivitas jaringan dipulihkan.

Saat Anda mengaktifkan persistensi disk, aplikasi Anda menulis data secara lokal ke perangkat sehingga aplikasi Anda dapat mempertahankan status saat offline, meskipun pengguna atau sistem operasi memulai ulang aplikasi.

Anda dapat mengaktifkan persistensi disk hanya dengan satu baris kode.

Kotlin+KTX

Firebase.database.setPersistenceEnabled(true)

Java

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

Perilaku Kegigihan

Dengan mengaktifkan persistensi, data apa pun yang akan disinkronkan oleh klien Firebase Realtime Database saat online akan disimpan ke disk dan tersedia secara offline, bahkan saat pengguna atau sistem operasi memulai ulang aplikasi. Ini berarti aplikasi Anda berfungsi seperti online dengan menggunakan data lokal yang disimpan di cache. Callback pendengar akan terus diaktifkan untuk pembaruan lokal.

Klien Firebase Realtime Database secara otomatis menyimpan antrean semua operasi tulis yang dijalankan saat aplikasi Anda offline. Saat persistensi diaktifkan, antrean ini juga dipertahankan ke disk sehingga semua tulisan Anda tersedia saat pengguna atau sistem operasi memulai ulang aplikasi. Saat aplikasi mendapatkan kembali konektivitas, semua operasi dikirim ke server Firebase Realtime Database.

Jika aplikasi Anda menggunakan Firebase Authentication , klien Firebase Realtime Database akan mempertahankan token autentikasi pengguna saat aplikasi dimulai ulang. Jika token autentikasi kedaluwarsa saat aplikasi Anda offline, klien akan menjeda operasi tulis hingga aplikasi Anda mengautentikasi ulang pengguna, jika tidak, operasi tulis mungkin gagal karena aturan keamanan.

Menjaga Data Tetap Segar

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

Kotlin+KTX

val scoresRef = Firebase.database.getReference("scores")
scoresRef.keepSynced(true)

Java

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

Klien Firebase Realtime Database secara otomatis mengunduh data di lokasi ini dan menyinkronkannya meskipun referensi tidak memiliki pemroses aktif. Anda dapat mematikan sinkronisasi kembali dengan baris kode berikut.

Kotlin+KTX

scoresRef.keepSynced(false)

Java

scoresRef.keepSynced(false);

Secara default, 10 MB data yang sebelumnya disinkronkan akan di-cache. Ini seharusnya cukup untuk sebagian besar aplikasi. Jika ukuran cache melebihi ukuran yang dikonfigurasi, Firebase Realtime Database akan menghapus data yang terakhir digunakan. Data yang disimpan dalam sinkronisasi tidak dibersihkan dari cache.

Meminta Data Offline

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

Misalnya, kode ini menanyakan empat item terakhir dalam skor Firebase Realtime Database

Kotlin+KTX

val scoresRef = Firebase.database.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}")
    }

    // ...
})

Java

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());
    }

    // ...
});

Asumsikan bahwa pengguna kehilangan koneksi, menjadi offline, dan memulai ulang aplikasi. Saat masih offline, aplikasi meminta dua item terakhir dari lokasi yang sama. Kueri ini akan berhasil mengembalikan dua item terakhir karena aplikasi telah memuat keempat item dalam kueri di atas.

Kotlin+KTX

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}")
    }

    // ...
})

Java

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());
    }

    // ...
});

Dalam contoh sebelumnya, klien Firebase Realtime Database memunculkan peristiwa 'penambahan turunan' untuk dua dinosaurus dengan skor tertinggi, dengan menggunakan cache yang bertahan. Tapi itu tidak akan menaikkan acara 'nilai', karena aplikasi tidak pernah mengeksekusi kueri itu saat online.

Jika aplikasi meminta enam item terakhir saat offline, itu akan langsung mendapatkan acara 'tambahan anak' untuk empat item yang di-cache. Saat perangkat kembali online, klien Firebase Realtime Database melakukan sinkronisasi dengan server dan mendapatkan dua peristiwa 'penambahan turunan' dan 'nilai' terakhir untuk aplikasi.

Menangani Transaksi Offline

Transaksi apa pun yang dilakukan saat aplikasi sedang offline, akan masuk antrean. Setelah aplikasi mendapatkan kembali konektivitas jaringan, transaksi dikirim ke server Realtime Database.

Mengelola Kehadiran

Dalam aplikasi realtime seringkali berguna untuk mendeteksi kapan klien terhubung dan terputus. Misalnya, Anda mungkin ingin menandai pengguna sebagai 'offline' saat klien mereka terputus.

Klien Firebase Database menyediakan primitif sederhana yang dapat Anda gunakan untuk menulis ke database saat klien terputus dari server Firebase Database. Pembaruan ini terjadi apakah klien terputus dengan bersih atau tidak, sehingga Anda dapat mengandalkan mereka untuk membersihkan data bahkan jika koneksi terputus atau klien lumpuh. Semua operasi tulis, termasuk menyetel, memperbarui, dan menghapus, dapat dilakukan setelah pemutusan.

Berikut adalah contoh sederhana penulisan data setelah pemutusan dengan menggunakan primitif onDisconnect :

Kotlin+KTX

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

Java

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

Bagaimana onDisconnect Bekerja

Saat Anda membuat operasi onDisconnect() , operasi tersebut berada di server Firebase Realtime Database. Server memeriksa keamanan untuk memastikan pengguna dapat melakukan peristiwa tulis yang diminta, dan memberi tahu aplikasi Anda jika tidak valid. Server kemudian memonitor koneksi. Jika sewaktu-waktu koneksi habis, atau ditutup secara aktif oleh klien Realtime Database, server akan memeriksa keamanan untuk kedua kalinya (untuk memastikan operasi masih valid) dan kemudian memanggil peristiwa tersebut.

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

Kotlin+KTX

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

Java

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());
        }
    }
});

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

Kotlin+KTX

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

Java

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

Mendeteksi Status Koneksi

Untuk banyak fitur terkait kehadiran, aplikasi Anda perlu mengetahui kapan sedang online atau offline. Firebase Realtime Database menyediakan lokasi khusus di /.info/connected yang diperbarui setiap kali status koneksi klien Firebase Realtime Database berubah. Ini contohnya:

Kotlin+KTX

val connectedRef = Firebase.database.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")
    }
})

Java

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");
    }
});

/.info/connected adalah nilai boolean yang tidak disinkronkan antara klien Realtime Database karena nilainya bergantung pada status klien. Dengan kata lain, jika satu klien membaca /.info/connected sebagai salah, ini bukan jaminan bahwa klien terpisah juga akan membaca salah.

Di Android, Firebase secara otomatis mengelola status koneksi untuk mengurangi bandwidth dan penggunaan baterai. Saat klien tidak memiliki pendengar aktif, tidak ada operasi tulis atau onDisconnect yang tertunda, dan tidak terputus secara eksplisit dengan metode goOffline , Firebase akan menutup koneksi setelah 60 detik tidak aktif.

Menangani Latensi

Stempel Waktu Server

Server Firebase Realtime Database menyediakan mekanisme untuk memasukkan stempel waktu yang dihasilkan di server sebagai data. Fitur ini, digabungkan dengan onDisconnect , menyediakan cara mudah untuk mencatat waktu ketika klien Realtime Database terputus:

Kotlin+KTX

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

Java

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

Kemiringan Jam

Meskipun firebase.database.ServerValue.TIMESTAMP jauh lebih akurat, dan lebih disukai untuk sebagian besar operasi baca/tulis, terkadang berguna untuk memperkirakan kemiringan jam klien sehubungan dengan server Firebase Realtime Database. Anda dapat melampirkan callback ke lokasi /.info/serverTimeOffset untuk mendapatkan nilai, dalam milidetik, yang ditambahkan oleh klien Firebase Realtime Database ke waktu lokal yang dilaporkan (waktu zaman dalam milidetik) untuk memperkirakan waktu server. Perhatikan bahwa akurasi offset ini dapat dipengaruhi oleh latensi jaringan, sehingga berguna terutama untuk menemukan perbedaan besar (> 1 detik) dalam waktu jam.

Kotlin+KTX

val offsetRef = Firebase.database.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")
    }
})

Java

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");
    }
});

Contoh Aplikasi Kehadiran

Dengan menggabungkan operasi pemutusan sambungan 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 menyetel lokasi ini ke true saat mereka online dan stempel waktu saat mereka memutuskan hubungan. Stempel waktu ini menunjukkan kapan terakhir kali pengguna tersebut online.

Perhatikan bahwa aplikasi Anda harus mengantrikan operasi pemutusan sambungan sebelum pengguna ditandai online, untuk menghindari kondisi balapan jika koneksi jaringan klien terputus sebelum kedua perintah dapat dikirim ke server.

Berikut adalah sistem kehadiran pengguna yang sederhana:

Kotlin+KTX

// 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 = Firebase.database
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>() ?: 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")
    }
})

Java

// 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(@NonNull 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(@NonNull DatabaseError error) {
        Log.w(TAG, "Listener was cancelled at .info/connected");
    }
});