Kotlin Firebase Realtime Database – Read/Write Data example | Android

Firebase Realtime Database is a cloud-hosted database that helps us to store and sync data with NoSQL cloud database in realtime to every connected client. In this tutorial, we’re gonna look at way to read/write data in an Android App with Kotlin.

More Practice:
Kotlin Firebase Realtime Database – Get List of Data example | Android
Kotlin Firebase Realtime Database – Display List of Data with FirebaseRecyclerAdapter | Android

I. Firebase Realtime Database Overview

1. How data is structured

All Firebase Realtime Database data is stored as JSON objects without tables or records. When adding data, it becomes a node in JSON structure. For each node, we can:
– provide our own key (IDs/semantic names), or
– let it do automatically using push() method.


{
  "users": {
    "EAuSKQmToJgs8g28Hdlor4MT3HM2": {
      "name": "JavaSampleApproach",
      "email": "contact@grokonez.com",
    },
    "CgumIKCBG6eUKFEG1DTlrgxajR52": {
      "name": "Kotlination",
      "email": "contact@kotlination.com",
    }
  }

  "message": {
    "author": "JavaSampleApproach",
    "body": "WOW! Java Technology, Spring Framework!"
  }

  "chats": {
    "one": {
      "author": "JavaSampleApproach",
      "body": "Hello, Java please!",
    },
    "two": {
      "author": "Kotlination",
      "body": "Kotlin is good!",
    }
  }
}

2. Offline Capabilities on Android

Firebase work even if our app temporarily loses internet connection. It provides tools for persisting data locally, managing presence, and handling latency.

2.1 Disk Persistence

When enabling disk persistence that automatically stores the data offline in case of no internet connection. When internet is on, offline data will be pushed to realtime database, even if the user or operating system restarts the app.

We can enable disk persistence with just one line of code:


FirebaseDatabase.getInstance().setPersistenceEnabled(true)

2.2 Managing Presence

– Firebase provides simple primitives that we can use to write to the database at the time client disconnects from the Firebase Database servers. All write operations, including setting, updating, and removing, can be performed upon a disconnection.


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

– With Firebase, we also can detect Connection State. It is useful to know when our app is online or offline. Firebase Realtime Database provides a special location at /.info/connected which is updated every time the client’s connection state changes.

2.3 Handling Latency

The Firebase Realtime Database servers provide a mechanism to insert timestamps generated on the server as data with firebase.database.ServerValue.TIMESTAMP.

3. CRUD Operations

We can insert, read, update, delete data from Firebase Realtime Database. By default, these operations access to database is restricted so only authenticated users can read or write data.

II. Way to read/write data

0. Add Firebase to Android App

0.1 Add Firebase Realtime Database

Steps to import and enable Firebase Realtime Database is just like steps for Firebase Auth.
Just follow: Add_Firebase_to_Android_Project

kotlin-firebase-db-set-up-0

0.2 Add Firebase Auth

By default, only authenticated users can read or write data, so we need Firebase Authentication.
Go to Your Firebase Project Console -> Authentication -> SIGN-IN METHOD -> Enable Email/Password.

To do without setting up Authentication, we can change the rules in the Firebase Console -> choose Project -> Database section on the left -> Rules tab:


// change the code below
{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

0.3 Check

After adding Firebase Auth & Realtime DB, we can see:

build.gradle (project-level):


buildscript {
    // ...
    dependencies {
        // ...
        classpath 'com.google.gms:google-services:3.1.0'
    }
}

build.gradle (App-level):


dependencies {
    // ...
    implementation 'com.google.firebase:firebase-auth:11.0.4'
    implementation 'com.google.firebase:firebase-database:11.0.4'
}

apply plugin: 'com.google.gms.google-services'

google-services.json file:
kotlin-firebase-db-json-service

1. Get a DatabaseReference

In order to perform any read or write operation to database, we need to get the reference to database first, so Instance of DatabaseReference gives us reference to database JSON node. From here we can to use the child node names to go further:


private var mDatabase: DatabaseReference? = null
private var mMessageReference: DatabaseReference? = null
// ...
mDatabase = FirebaseDatabase.getInstance().reference
mMessageReference = FirebaseDatabase.getInstance().getReference("message")

2. Write Data

We can use setValue() to:
– save data to a specified reference
– replace any existing data at that reference

The data can be:
String, Long, Double, Boolean, Map<String, Object>, List<Object>
– custom Java object (has a default constructor with no arguments and public getters for the properties to be assigned)


mDatabase!!.child("title").setValue("JavaSampleApproach")

val user = User(username, email)
mDatabase!!.child("users").child(userId).setValue(user)

val message = Message(author, body, time)
mMessageReference!!.setValue(message)

3. Read Data

3.1 At a path

Use the addValueEventListener() method to add a ValueEventListener to a DatabaseReference. It has onDataChange() event callback that is called every time data is changed at the specified database reference, including changes to children:


val messageListener = object : ValueEventListener {

    override fun onDataChange(dataSnapshot: DataSnapshot) {
        if (dataSnapshot.exists()) {
            val message = dataSnapshot.getValue(Message::class.java)
            // ...
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Failed to read value
    }
}

mMessageReference!!.addValueEventListener(messageListener)

3.2 Once

We can use the addListenerForSingleValueEvent() method to make a callback to be called once and then immediately removed.

For instance, load a user’s profile when he begin send a new message:


// User data change listener
mDatabase!!.child("users").child(user!!.uid).addListenerForSingleValueEvent(object : ValueEventListener {

    override fun onDataChange(dataSnapshot: DataSnapshot) {
        val user = dataSnapshot.getValue(User::class.java)
        //...
        writeNewMessage(...)
    }

    override fun onCancelled(error: DatabaseError) {
        // Failed to read value
    }
})

3.3 Detach listeners

Callbacks are removed by calling the removeEventListener() method:


mMessageReference!!.removeEventListener(mMessageListener)

Calling removeEventListener() on a parent listener does not automatically remove listeners registered on its child nodes. So we must also call it on any child listeners to remove the callback.

4. Delete Data

– The simplest way is to call removeValue() on a reference to the location of that data.


mMessageReference!!.removeValue();

– We can also delete by specifying null as the value for write operation setValue() method:


mMessageReference!!.setValue(null)

III. Practice

1. Goal

We will build an Android App that can:
– create Account, sign in/sign out for Firebase Authentication.
– read/write user and message data to Firebase Realtime Database.

kotlin-firebase-db-goal

kotlin-firebase-db-console-result

2. Technology

– Gradle 3.0.1
– Android Studio 3.x
– Firebase Android SDK 11.x

3. Project Structure

kotlin-firebase-db-structure

LoginActivity is for Authentication, then user can change to MessageActivity to send Message to Firebase Realtime Database.

4. Step by step

4.1 Create Android Project

– Generate new Android Project with package com.javasampleapproach.kotlin.firebase.realtimedb.
– Follow steps above to add Firebase Auth and Realtime DB.

4.2 Enable Firebase Auth

Go to Your Firebase Project Console -> Authentication -> SIGN-IN METHOD -> Enable Email/Password.

4.3 Model


package com.javasampleapproach.kotlin.firebase.realtimedb.model

import com.google.firebase.database.IgnoreExtraProperties

@IgnoreExtraProperties
class User {
    var name: String? = null
    var email: String? = null

    constructor() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }

    constructor(username: String?, email: String?) {
        this.name = username
        this.email = email
    }
}

package com.javasampleapproach.kotlin.firebase.realtimedb.model

import com.google.firebase.database.IgnoreExtraProperties

@IgnoreExtraProperties
class Message {

    var author: String? = null
    var body: String? = null
    var time: String? = null

    constructor() {
        // Default constructor required for calls to DataSnapshot.getValue(Post.class)
    }

    constructor(author: String, body: String, time: String) {
        this.author = author
        this.body = body
        this.time = time
    }
}

4.4 LoginActivity

In this tutorial, we don’t explain way to authenticate an user again. To know how to implement Firebase Authentication App Client, please visit:
Kotlin Firebase Authentication – How to Sign Up, Sign In, Sign Out, Verify Email | Android

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="3">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tvTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:gravity="center"
            android:text="grokonez.com"
            android:textSize="28sp" />

        <TextView
            android:id="@+id/tvStatus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="4dp"
            android:text="Signed Out"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tvDetail"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="4dp"
            android:textSize="14sp"
            tools:text="Firebase User ID: 123456789abc" />

    </LinearLayout>


    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#E0E0E0"
        android:gravity="center_vertical">

        <LinearLayout
            android:id="@+id/email_password_fields"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:paddingLeft="16dp"
            android:paddingRight="16dp">

            <EditText
                android:id="@+id/edtEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Email"
                android:inputType="textEmailAddress" />

            <EditText
                android:id="@+id/edtPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Password"
                android:inputType="textPassword" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/email_password_buttons"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/email_password_fields"
            android:orientation="horizontal"
            android:paddingLeft="16dp"
            android:paddingRight="16dp">

            <Button
                android:id="@+id/btn_email_sign_in"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1"
                android:text="Sign In" />

            <Button
                android:id="@+id/btn_email_create_account"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1"
                android:text="Create Account" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/layout_signed_in_buttons"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:orientation="horizontal"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:visibility="gone"
            android:weightSum="2.0">

            <Button
                android:id="@+id/btn_sign_out"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1.0"
                android:text="Sign Out" />

            <Button
                android:id="@+id/btn_test_message"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1.0"
                android:text="Test Message" />

        </LinearLayout>

    </RelativeLayout>

</LinearLayout>

package com.javasampleapproach.kotlin.firebase.realtimedb

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_login.*
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import android.widget.Toast
import android.util.Log
import android.text.TextUtils
import android.content.Intent
import com.google.firebase.database.FirebaseDatabase
import com.javasampleapproach.kotlin.firebase.realtimedb.model.User


class LoginActivity : AppCompatActivity(), View.OnClickListener {

    private val TAG = "LoginActivity"

    private var mAuth: FirebaseAuth? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        btn_email_sign_in.setOnClickListener(this)
        btn_email_create_account.setOnClickListener(this)
        btn_sign_out.setOnClickListener(this)
        btn_test_message.setOnClickListener(this)

        mAuth = FirebaseAuth.getInstance()
    }

    override fun onStart() {
        super.onStart()

        val currentUser = mAuth!!.currentUser
        updateUI(currentUser)
    }

    override fun onClick(view: View?) {
        val i = view!!.id

        when (i) {
            R.id.btn_email_create_account -> createAccount(edtEmail.text.toString(), edtPassword.text.toString())
            R.id.btn_email_sign_in -> signIn(edtEmail.text.toString(), edtPassword.text.toString())
            R.id.btn_sign_out -> signOut()
            R.id.btn_test_message -> testMessage()
        }
    }

    private fun createAccount(email: String, password: String) {
        Log.e(TAG, "createAccount:" + email)
        if (!validateForm(email, password)) {
            return
        }

        mAuth!!.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        Log.e(TAG, "createAccount: Success!")

                        // update UI with the signed-in user's information
                        val user = mAuth!!.currentUser
                        updateUI(user)
                        writeNewUser(user!!.uid, getUsernameFromEmail(user.email), user.email)
                    } else {
                        Log.e(TAG, "createAccount: Fail!", task.exception)
                        Toast.makeText(applicationContext, "Authentication failed!", Toast.LENGTH_SHORT).show()
                        updateUI(null)
                    }
                }
    }

    private fun signIn(email: String, password: String) {
        Log.e(TAG, "signIn:" + email)
        if (!validateForm(email, password)) {
            return
        }

        mAuth!!.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        Log.e(TAG, "signIn: Success!")

                        // update UI with the signed-in user's information
                        val user = mAuth!!.currentUser
                        updateUI(user)
                    } else {
                        Log.e(TAG, "signIn: Fail!", task.exception)
                        Toast.makeText(applicationContext, "Authentication failed!", Toast.LENGTH_SHORT).show()
                        updateUI(null)
                    }

                    if (!task.isSuccessful) {
                        tvStatus.text = "Authentication failed!"
                    }
                }
    }

    private fun signOut() {
        mAuth!!.signOut()
        updateUI(null)
    }

    private fun validateForm(email: String, password: String): Boolean {

        if (TextUtils.isEmpty(email)) {
            Toast.makeText(applicationContext, "Enter email address!", Toast.LENGTH_SHORT).show()
            return false
        }

        if (TextUtils.isEmpty(password)) {
            Toast.makeText(applicationContext, "Enter password!", Toast.LENGTH_SHORT).show()
            return false
        }

        if (password.length < 6) {
            Toast.makeText(applicationContext, "Password too short, enter minimum 6 characters!", Toast.LENGTH_SHORT).show()
            return false
        }

        return true
    }

    private fun updateUI(user: FirebaseUser?) {

        if (user != null) {
            tvStatus.text = "User Email: " + user.email
            tvDetail.text = "Firebase User ID: " + user.uid

            email_password_buttons.visibility = View.GONE
            email_password_fields.visibility = View.GONE
            layout_signed_in_buttons.visibility = View.VISIBLE
        } else {
            tvStatus.text = "Signed Out"
            tvDetail.text = null

            email_password_buttons.visibility = View.VISIBLE
            email_password_fields.visibility = View.VISIBLE
            layout_signed_in_buttons.visibility = View.GONE
        }
    }

    private fun writeNewUser(userId: String, username: String?, email: String?) {
        val user = User(username, email)

        FirebaseDatabase.getInstance().reference.child("users").child(userId).setValue(user)
    }

    private fun getUsernameFromEmail(email: String?): String {
        return if (email!!.contains("@")) {
            email.split("@".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
        } else {
            email
        }
    }

    private fun testMessage() {
        startActivity(Intent(this, MessageActivity::class.java))
    }
}

4.5 MessageActivity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="4">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="grokonez.com"
            android:textSize="28sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Messages"
            android:textSize="18sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:orientation="vertical"
        android:padding="16dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:weightSum="2">

            <TextView
                android:id="@+id/tvAuthor"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/tvTime"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />

        </LinearLayout>

        <TextView
            android:id="@+id/tvBody"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/holo_blue_dark"
            android:textSize="20sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#E0E0E0"
        android:gravity="center"
        android:orientation="vertical"
        android:paddingLeft="16dp"
        android:paddingRight="16dp">

        <EditText
            android:id="@+id/edtSentText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="text" />

        <LinearLayout
            android:id="@+id/layout_signed_in_buttons"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="right"
            android:orientation="horizontal"
            android:weightSum="2">

            <Button
                android:id="@+id/btnBack"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.5"
                android:text="Back" />

            <Button
                android:id="@+id/btnSend"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.5"
                android:text="Send" />
        </LinearLayout>

    </LinearLayout>

</LinearLayout>

package com.javasampleapproach.kotlin.firebase.realtimedb

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_message.*

import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import com.javasampleapproach.kotlin.firebase.realtimedb.model.Message

import java.text.SimpleDateFormat
import java.util.Calendar
import com.javasampleapproach.kotlin.firebase.realtimedb.model.User


class MessageActivity : AppCompatActivity() {

    private val TAG = "MessageActivity"
    private val REQUIRED = "Required"

    private var user: FirebaseUser? = null

    private var mDatabase: DatabaseReference? = null
    private var mMessageReference: DatabaseReference? = null
    private var mMessageListener: ValueEventListener? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_message)

        mDatabase = FirebaseDatabase.getInstance().reference
        mMessageReference = FirebaseDatabase.getInstance().getReference("message")
        user = FirebaseAuth.getInstance().currentUser

        btnSend.setOnClickListener {
            submitMessage()
            edtSentText.setText("")
        }

        btnBack.setOnClickListener {
            finish()
        }
    }

    override fun onStart() {
        super.onStart()

        val messageListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                if (dataSnapshot.exists()) {
                    val message = dataSnapshot.getValue(Message::class.java)

                    Log.e(TAG, "onDataChange: Message data is updated: " + message!!.author + ", " + message.time + ", " + message.body)

                    tvAuthor.text = message.author
                    tvTime.text = message.time
                    tvBody.text = message.body
                }
            }

            override fun onCancelled(databaseError: DatabaseError) {
                // Failed to read value
                Log.e(TAG, "onCancelled: Failed to read message")

                tvAuthor.text = ""
                tvTime.text = ""
                tvBody.text = "onCancelled: Failed to read message!"
            }
        }

        mMessageReference!!.addValueEventListener(messageListener)

        // copy for removing at onStop()
        mMessageListener = messageListener
    }

    override fun onStop() {
        super.onStop()

        if (mMessageListener != null) {
            mMessageReference!!.removeEventListener(mMessageListener)
        }
    }

    private fun submitMessage() {
        val body = edtSentText.text.toString()

        if (TextUtils.isEmpty(body)) {
            edtSentText.error = REQUIRED
            return
        }

        // User data change listener
        mDatabase!!.child("users").child(user!!.uid).addListenerForSingleValueEvent(object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                val user = dataSnapshot.getValue(User::class.java)

                if (user == null) {
                    Log.e(TAG, "onDataChange: User data is null!")
                    Toast.makeText(this@MessageActivity, "onDataChange: User data is null!", Toast.LENGTH_SHORT).show()
                    return
                }

                writeNewMessage(body)
            }

            override fun onCancelled(error: DatabaseError) {
                // Failed to read value
                Log.e(TAG, "onCancelled: Failed to read user!")
            }
        })
    }

    private fun writeNewMessage(body: String) {
        val time = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().time)
        val message = Message(getUsernameFromEmail(user!!.email), body, time)

        mMessageReference!!.setValue(message)
    }

    private fun getUsernameFromEmail(email: String?): String {
        return if (email!!.contains("@")) {
            email.split("@".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
        } else {
            email
        }
    }
}

4.6 Android Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.javasampleapproach.kotlin.firebase.realtimedb">

    <application ...>
        <activity android:name=".LoginActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MessageActivity"></activity>
    </application>

</manifest>
4.7 Run & Check result

- Use Android Studio, build and Run your Android App:

kotlin-firebase-db-goal

- Open Firebase Project Console -> Database:
kotlin-firebase-db-console-result

III. Source code

Kotlin-FirebaseRealtimeDB-read-write



By grokonez | December 18, 2017.

Last updated on April 24, 2021.



Related Posts


1 thought on “Kotlin Firebase Realtime Database – Read/Write Data example | Android”

Got Something To Say:

Your email address will not be published. Required fields are marked *

*