Skip to main content
Module: 07 of 13Duration: 3 weeksTopics: 3 · 6 subtopicsPrerequisites: Modules 01–06

Firebase & Cloud Services

Firebase is Google's backend-as-a-service platform — you get auth, a real-time database, file storage, push notifications, analytics, and crash reporting without spinning up servers. For solo developers and small teams shipping production apps, Firebase often replaces an entire backend.

Topic 1 · Authentication

Setup & configuration

  1. Create a Firebase project at console.firebase.google.com.
  2. Add your Android app — supply the package name and SHA-1.
  3. Download google-services.json into app/ (never commit it to public repos).
  4. Apply plugins:
// settings.gradle.kts
plugins {
id("com.google.gms.google-services") version "4.4.2" apply false
id("com.google.firebase.crashlytics") version "3.0.2" apply false
}

// app/build.gradle.kts
plugins {
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
dependencies {
implementation(platform("com.google.firebase:firebase-bom:33.5.0")) // BoM = consistent versions
implementation("com.google.firebase:firebase-auth-ktx")
implementation("com.google.firebase:firebase-firestore-ktx")
implementation("com.google.firebase:firebase-storage-ktx")
implementation("com.google.firebase:firebase-messaging-ktx")
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-crashlytics-ktx")
implementation("com.google.firebase:firebase-config-ktx")
}

Email/password, Google Sign-In, Phone OTP

class AuthRepository @Inject constructor(
private val auth: FirebaseAuth
) {
val currentUser: Flow<FirebaseUser?> = callbackFlow {
val listener = FirebaseAuth.AuthStateListener { trySend(it.currentUser) }
auth.addAuthStateListener(listener)
awaitClose { auth.removeAuthStateListener(listener) }
}

suspend fun signUp(email: String, password: String): Result<FirebaseUser> = runCatching {
auth.createUserWithEmailAndPassword(email, password).await().user!!
}

suspend fun signIn(email: String, password: String): Result<FirebaseUser> = runCatching {
auth.signInWithEmailAndPassword(email, password).await().user!!
}

suspend fun signInWithGoogle(idToken: String): Result<FirebaseUser> = runCatching {
val credential = GoogleAuthProvider.getCredential(idToken, null)
auth.signInWithCredential(credential).await().user!!
}

fun signOut() = auth.signOut()
}

For Google Sign-In on Android, use the Credential Manager API (not the deprecated GoogleSignInClient) — it integrates with passkeys and Sign-In with Google in one flow.

For Phone OTP, Firebase handles SMS delivery, code verification, and auto-retrieval. Always require App Check in production to prevent abuse.


Topic 2 · Database & Storage

Cloud Firestore — CRUD, queries, real-time listeners

Firestore is a document-oriented NoSQL database with real-time sync, offline support, and granular security rules. Data lives in collections of documents:

users (collection)
└── userId123 (document)
├── name: "Aarav"
├── email: "a@x.com"
└── posts (subcollection)
└── postId456 (document)
├── title: "Hello"
└── createdAt: Timestamp
class PostRepository @Inject constructor(
private val db: FirebaseFirestore
) {
private val posts = db.collection("posts")

suspend fun create(post: Post): String {
val ref = posts.add(post.toMap()).await()
return ref.id
}

fun observeFeed(userId: String): Flow<List<Post>> = callbackFlow {
val sub = posts
.whereEqualTo("authorId", userId)
.orderBy("createdAt", Query.Direction.DESCENDING)
.limit(50)
.addSnapshotListener { snap, err ->
if (err != null) { close(err); return@addSnapshotListener }
trySend(snap?.documents.orEmpty().mapNotNull { it.toPost() })
}
awaitClose { sub.remove() }
}
}

Security rules are non-negotiable — they're your real backend authorization layer:

// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{db}/documents {
match /posts/{postId} {
allow read: if true; // public posts
allow create: if request.auth != null
&& request.resource.data.authorId == request.auth.uid;
allow update, delete: if resource.data.authorId == request.auth.uid;
}
}
}

Cloud Storage — file upload & download

class MediaRepository @Inject constructor(
private val storage: FirebaseStorage
) {
suspend fun uploadAvatar(userId: String, bytes: ByteArray): String {
val ref = storage.reference.child("avatars/$userId.jpg")
ref.putBytes(bytes).await()
return ref.downloadUrl.await().toString()
}
}

Apply Storage Security Rules just like Firestore — restrict who can read, write, and to which paths.


Topic 3 · Services

Push notifications with FCM

Firebase Cloud Messaging delivers messages to your app — both when the app is running and when it's not.

class AppMessagingService : FirebaseMessagingService() {

override fun onNewToken(token: String) {
// Persist token in your backend so you can target this device
ServiceLocator.tokenSync.uploadToken(token)
}

override fun onMessageReceived(message: RemoteMessage) {
// Data-only payloads always reach this method.
// Notification payloads bypass it when the app is in background.
showNotification(
title = message.notification?.title ?: message.data["title"].orEmpty(),
body = message.notification?.body ?: message.data["body"].orEmpty(),
deepLink = message.data["deepLink"]
)
}
}
<!-- AndroidManifest.xml -->
<service
android:name=".messaging.AppMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

Analytics, Crashlytics, Remote Config

// Analytics — log custom events
Firebase.analytics.logEvent("checkout_started") {
param("cart_value", cart.totalCents.toLong())
param("item_count", cart.items.size.toLong())
}

// Crashlytics — capture non-fatal errors with context
Firebase.crashlytics.setUserId(currentUser.id)
Firebase.crashlytics.setCustomKey("subscription_tier", "pro")
runCatching { riskyOperation() }
.onFailure { Firebase.crashlytics.recordException(it) }

// Remote Config — change values without shipping a new APK
val remoteConfig = Firebase.remoteConfig.apply {
setConfigSettingsAsync(remoteConfigSettings { minimumFetchIntervalInSeconds = 3600 })
setDefaultsAsync(R.xml.remote_defaults)
}
remoteConfig.fetchAndActivate().await()
val showNewCheckout = remoteConfig.getBoolean("show_new_checkout")

Companion services

Key takeaways

Practice exercises

  1. 01

    Email + Google sign-in

    Build an auth screen with Firebase Auth + Credential Manager. Persist the FirebaseUser as a Flow in your AuthRepository.

  2. 02

    Firestore feed

    Build a real-time chat collection with security rules: anyone can read, only authenticated users can post their own messages.

  3. 03

    Push deep link

    Send an FCM message with a deepLink data field; tap the notification to open the matching screen.

Next module

Continue to Module 08 — Advanced Android Components to learn services, WorkManager, sensors, biometrics, and Glance widgets.