Skip to content

Instantly share code, notes, and snippets.

@rogergcc
Last active January 23, 2026 16:33
Show Gist options
  • Select an option

  • Save rogergcc/a7d87ddc295b1312c4772481d7394725 to your computer and use it in GitHub Desktop.

Select an option

Save rogergcc/a7d87ddc295b1312c4772481d7394725 to your computer and use it in GitHub Desktop.
A collection of Kotlin classes designed for auditing and backing up a Firebase Realtime Database structure into a local JSON file using modern Coroutines and the KTX libraries.
**Filename 2:** `FirebaseStructureFetcher.kt`
class FirebaseStructureFetcher(
private val fileHelper: FileHelper,
private val firebaseService: FirebaseService,
private val jsonBuilder: JsonBuilder,
// SupervisorJob evita que un error de Firebase mate el Scope
private val scope: CoroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
) {
fun fetch() {
Log.d("TEST_LOGGER", "Iniciando descarga...")
scope.launch { // Empezamos en el scope por defecto
try {
firebaseService.checkRealServerStatus().let { status ->
Log.d("TEST_LOGGER", "Estado del servidor: $status")
}
Log.d("TEST_LOGGER", "Consultando Firebase...")
// 1. Llamada asíncrona a Firebase (hilo secundario por await)
val snapshot = firebaseService.fetchDatabaseStructure()
Log.d("TEST_LOGGER", "Construyendo JSON...")
// 2. Procesamiento pesado en hilo Default (CPU)
val json = withContext(Dispatchers.Default) {
jsonBuilder.buildJson(snapshot)
}
// 3. Guardado en disco en hilo IO
withContext(Dispatchers.IO) {
fileHelper.saveJsonToFile(json, "structure.json")
.onSuccess { Log.d("TEST_LOGGER", "OK: Guardado en ${it.absolutePath}") }
.onFailure { Log.e("TEST_LOGGER", "ERROR: No se pudo guardar", it) }
}
} catch (e: Exception) {
Log.e("TEST_LOGGER", "Fallo en Firebase/Proceso", e)
}
}
}
}
**Filename 3:** `FirebaseService.kt`
class FirebaseService {
suspend fun fetchDatabaseStructure(): DataSnapshot {
val rootRef = FirebaseDatabase.getInstance().reference
// .get().await() es la forma moderna (2026) de obtener datos sin callbacks
return rootRef.get().await()
}
suspend fun checkRealServerStatus(): String {
return try {
// En 2026, withTimeout es vital para no dejar la app colgada
withTimeout(5000) {
val db = FirebaseDatabase.getInstance()
// Intentamos obtener el Snapshot de la raíz
db.reference.get().await()
"✅ ONLINE_AND_ACTIVE"
}
} catch (e: Exception) {
// Si pasan los 5 segundos sin respuesta de Google:
if (e is kotlinx.coroutines.TimeoutCancellationException) {
"❌ TIMEOUT: El proyecto existe en el JSON pero el servidor NO responde (Proyecto borrado)."
} else {
"❌ ERROR: ${e.message}"
}
}
}
}
**Filename 4:** `JsonBuilder.kt`
class JsonBuilder {
@Throws(JSONException::class)
fun buildJson(dataSnapshot: DataSnapshot): JSONObject {
val json = JSONObject()
for (child in dataSnapshot.children) {
if (child.hasChildren()) {
json.put(child.key, buildJson(child))
} else {
json.put(child.key, child.value)
}
}
return json
}
}
**Filename 5:** `FileHelper.kt`
class FileHelper(private val context: Context) {
fun saveJsonToFile(json: JSONObject, fileName: String): Result<File> {
return runCatching {
val file = File(context.getExternalFilesDir(null), fileName)
file.writeText(json.toString(4))
file // Si esto se ejecuta, runCatching devuelve Success(file)
}
}
}
---

Android Firebase Realtime Database Utility Suite

A collection of Kotlin classes designed for auditing and backing up a Firebase Realtime Database structure into a local JSON file using modern Coroutines and the KTX libraries.

Features:

  • Asynchronous data fetching via Coroutines (await()).
  • JSON building from DataSnapshot (handling nested structures and types).
  • File saving to private external storage.
  • Project health check to verify connectivity.

Usage:

Inject these classes into your ViewModel or Activity and call fetch():

val fetcher = FirebaseStructureFetcher(fileHelper, firebaseService, jsonBuilder)
fetcher.fetch()
@rogergcc
Copy link
Author

rogergcc commented Jan 23, 2026

if works if result is
ONLINE_AND_ACTIVE

se ejecuta en un projecto Android Kotlin en Android studio

no importa si es google-services.json es de otro projecto

solo se cambia el

"android_client_info": {
          "package_name": "com.tuproject.demo"
        }
        
  "android_info": {
          "package_name": "com.tuproject.demo",
        
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment