Obtén informes de fallas del NDK de Android

Si tu app para Android contiene bibliotecas nativas, puedes habilitar los seguimientos de pila completa y los informes de fallas detallados para tu código nativo desde Firebase Crashlytics con solo hacer unas pequeñas actualizaciones en la configuración de compilación de tu app.

En esta guía, se describe cómo configurar los informes de fallas con el SDK de Firebase Crashlytics para el NDK.

Si quieres saber cómo comenzar a usar Crashlytics en tus proyectos de Unity, consulta la Guía de introducción de Unity.

Antes de comenzar

  1. Si aún no lo has hecho, agrega Firebase a tu proyecto de Android. Si no tienes ninguna app para Android, puedes descargar una app de ejemplo.

  2. Recomendación: Para obtener automáticamente registros de rutas de navegación y comprender las acciones del usuario que conducen a una falla, un evento recuperable o de ANR, debes habilitar Google Analytics en tu proyecto de Firebase.

    • Si tu proyecto de Firebase existente no tiene habilitado Google Analytics, puedes habilitar Google Analytics en la pestaña Integraciones de > Configuración del proyecto en Firebase console.

    • Si quieres crear un nuevo proyecto de Firebase, habilita Google Analytics durante el flujo de trabajo de creación del proyecto.

  3. Asegúrate de que tu app tenga las siguientes versiones mínimas requeridas:

    • Gradle 8.0
    • Complemento de Android para Gradle 8.1.0
    • Complemento de Gradle para los servicios de Google 4.4.1

Paso 1: Agrega el SDK de Crashlytics para el NDK a tu app

En el archivo Gradle del módulo (nivel de la app) (generalmente <project>/<app-module>/build.gradle.kts o <project>/<app-module>/build.gradle), agrega la dependencia de la biblioteca del NDK de Crashlytics para Android. Te recomendamos usar Firebase Android BoM para controlar las versiones de las bibliotecas.

Para obtener una experiencia óptima con Crashlytics, te recomendamos habilitar la Google Analytics en tu proyecto de Firebase y agregar el SDK de Firebase para Google Analytics a tu app.

dependencies {
    // Import the BoM for the Firebase platform
    implementation(platform("com.google.firebase:firebase-bom:33.9.0"))

    // Add the dependencies for the Crashlytics NDK and Analytics libraries
    // When using the BoM, you don't specify versions in Firebase library dependencies
    implementation("com.google.firebase:firebase-crashlytics-ndk")
    implementation("com.google.firebase:firebase-analytics")
}

Cuando usas la Firebase Android BoM, tu app siempre usará versiones compatibles de las bibliotecas de Firebase para Android.

Si eliges no usar la Firebase BoM, debes especificar cada versión de la biblioteca de Firebase en su línea de dependencia.

Ten en cuenta que, si usas múltiples bibliotecas de Firebase en tu app, es muy recomendable que uses la BoM para administrar las versiones de las bibliotecas para garantizar que todas las versiones sean compatibles.

dependencies {
    // Add the dependencies for the Crashlytics NDK and Analytics libraries
    // When NOT using the BoM, you must specify versions in Firebase library dependencies
    implementation("com.google.firebase:firebase-crashlytics-ndk:19.4.0")
    implementation("com.google.firebase:firebase-analytics:22.2.0")
}
¿Buscas un módulo de biblioteca específico de Kotlin? A partir de octubre de 2023 (Firebase BoM 32.5.0), tanto los desarrolladores de Kotlin como los de Java pueden depender del módulo de la biblioteca principal (para obtener más información, consulta las Preguntas frecuentes sobre esta iniciativa).

Paso 2: Agrega el complemento de Gradle de Crashlytics a tu app

  1. En tu archivo de Gradle de nivel raíz (a nivel de proyecto) (<project>/build.gradle.kts o <project>/build.gradle), agrega el complemento de Gradle de Crashlytics al bloque plugins:

    plugins {
        // Make sure that you have the AGP plugin 8.1+ dependency
        id("com.android.application") version "8.1.4" apply false
        // ...
    
        // Make sure that you have the Google services Gradle plugin 4.4.1+ dependency
        id("com.google.gms.google-services") version "4.4.2" apply false
    
        // Add the dependency for the Crashlytics Gradle plugin
        id("com.google.firebase.crashlytics") version "3.0.3" apply false
    }
    plugins {
        // Make sure that you have the AGP plugin 8.1+ dependency
        id 'com.android.application' version '8.1.4' apply false
        // ...
    
        // Make sure that you have the Google services Gradle plugin 4.4.1+ dependency
        id 'com.google.gms.google-services' version '4.4.2' apply false
    
        // Add the dependency for the Crashlytics Gradle plugin
        id 'com.google.firebase.crashlytics' version '3.0.3' apply false
    }
  2. En el archivo de Gradle del módulo (a nivel de app) (generalmente <project>/<app-module>/build.gradle.kts o <project>/<app-module>/build.gradle), agrega el complemento de Gradle de Crashlytics:

    plugins {
      id("com.android.application")
      // ...
    
      // Make sure that you have the Google services Gradle plugin
      id("com.google.gms.google-services")
    
      // Add the Crashlytics Gradle plugin
      id("com.google.firebase.crashlytics")
    }
    plugins {
      id 'com.android.application'
      // ...
    
      // Make sure that you have the Google services Gradle plugin
      id 'com.google.gms.google-services'
    
      // Add the Crashlytics Gradle plugin
      id 'com.google.firebase.crashlytics'
    }

Paso 3: Agrega la extensión Crashlytics a tu compilación

En el archivo de Gradle (generalmente <project>/<app-module>/build.gradle.kts o <project>/<app-module>/build.gradle) del módulo (a nivel de app), configura la extensión de Crashlytics.

import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension

// ...

android {
  // ...
  buildTypes {
      getByName("release") {
          // Add this extension
          configure<CrashlyticsExtension> {
              // Enable processing and uploading of native symbols to Firebase servers.
              // By default, this is disabled to improve build speeds.
              // This flag must be enabled to see properly-symbolicated native
              // stack traces in the Crashlytics dashboard.
              nativeSymbolUploadEnabled = true
          }
      }
  }
}
// ...

android {
  // ...
  buildTypes {
      release {
          // Add this extension
          firebaseCrashlytics {
              // Enable processing and uploading of native symbols to Firebase servers.
              // By default, this is disabled to improve build speeds.
              // This flag must be enabled to see properly-symbolicated native
              // stack traces in the Crashlytics dashboard.
              nativeSymbolUploadEnabled true
          }
      }
  }
}

Paso 4: Configura la carga automática de símbolos nativos

Para producir seguimientos de pila legibles de fallas del NDK, Crashlytics debe conocer los símbolos de tus objetos binarios nativos. El complemento de Gradle de Crashlytics incluye la tarea uploadCrashlyticsSymbolFileBUILD_VARIANT para automatizar el proceso.

  1. A fin de que puedas acceder a la tarea para subir símbolos automáticamente, asegúrate de que nativeSymbolUploadEnabled esté configurado como true en el archivo Gradle de tu módulo (nivel de app).

  2. Para que los nombres de los métodos aparezcan en los seguimientos de pila, debes invocar de manera explícita la tarea uploadCrashlyticsSymbolFileBUILD_VARIANT después de cada compilación de la biblioteca del NDK. Por ejemplo:

    >./gradlew app:assembleBUILD_VARIANT\
               app:uploadCrashlyticsSymbolFileBUILD_VARIANT
  3. El SDK de Crashlytics para el NDK y el complemento de Gradle de Crashlytics dependen de la presencia del ID de compilación de GNU dentro de los objetos compartidos nativos.

    Puedes verificar la presencia de este ID ejecutando readelf -n en cada objeto binario. Si no aparece el ID de compilación, agrega -Wl,--build-id a las marcas del sistema de compilación para solucionar el problema.

Paso 5: Fuerza una falla de prueba para finalizar la configuración

Para finalizar la configuración de Crashlytics y ver los datos iniciales en el panel de Crashlytics de Firebase console, debes forzar una falla de prueba.

  1. Agrega código a tu app para forzar una falla de prueba.

    Puedes usar el siguiente código en la MainActivity de tu app para agregar un botón que, cuando se presione, genere una falla. El botón tiene la etiqueta “Test Crash”.

    val crashButton = Button(this)
    crashButton.text = "Test Crash"
    crashButton.setOnClickListener {
       throw RuntimeException("Test Crash") // Force a crash
    }
    
    addContentView(crashButton, ViewGroup.LayoutParams(
           ViewGroup.LayoutParams.MATCH_PARENT,
           ViewGroup.LayoutParams.WRAP_CONTENT))
    Button crashButton = new Button(this);
    crashButton.setText("Test Crash");
    crashButton.setOnClickListener(new View.OnClickListener() {
       public void onClick(View view) {
           throw new RuntimeException("Test Crash"); // Force a crash
       }
    });
    
    addContentView(crashButton, new ViewGroup.LayoutParams(
           ViewGroup.LayoutParams.MATCH_PARENT,
           ViewGroup.LayoutParams.WRAP_CONTENT));
  2. Compila y ejecuta tu app.

  3. Fuerza la falla de prueba para enviar el primer informe de fallas de la app, de la siguiente manera:

    1. Abre la app desde tu emulador o dispositivo de prueba.

    2. En tu app, presiona el botón “Test Crash” que agregaste con el código anterior.

    3. Una vez que tu app falle, reiníciala para que pueda enviar el informe de fallas a Firebase.

  4. Ve al panel de Crashlytics de Firebase console para ver la falla de prueba.

    Si actualizaste la consola y sigues sin poder ver la falla de prueba después de cinco minutos, habilita el registro de depuración para confirmar si tu app está enviando informes de fallas.


Eso es todo. Crashlytics ahora supervisa la app para detectar fallas, y puedes ver y analizar informes y estadísticas de fallas en el panel de Crashlytics.

Próximos pasos

  • (Recomendado) Obtén ayuda para recopilar informes de GWP-ASan con el propósito de depurar las fallas causadas por errores de la memoria nativa. Estos errores relacionados con la memoria se pueden asociar con la corrupción de la memoria dentro de tu app, lo que es la causa principal de las vulnerabilidades de seguridad de las apps. Para aprovechar esta función de depuración, asegúrate de que tu app tenga GWP-ASan habilitado de forma explícita y que use el SDK de Crashlytics para NDK más reciente (v18.3.6 y versiones posteriores o Firebase BoM v31.3.0 y versiones posteriores).

  • Agrega informes, registros y claves opcionales, así como seguimiento de errores recuperables para personalizar la configuración del informe de fallas.

  • Realiza integraciones en Google Play para que puedas filtrar los informes de fallas de tu app para Android por segmento de Google Play directamente en el panel de Crashlytics. Esto te permite enfocar mejor el panel en compilaciones específicas.

Soluciona problemas

Si ves diferentes seguimientos de pila en Firebase console y en Logcat, consulta la guía para solucionar problemas.



Alternativas para subir símbolos

El flujo de trabajo principal de esta página aplica a compilaciones estándar de Gradle. Sin embargo, algunas apps usan una configuración o herramientas diferentes (por ejemplo, un proceso de compilación distinto de Gradle). En estas situaciones, las siguientes opciones pueden ser útiles para subir símbolos correctamente.

Opción: Sube símbolos para módulos de biblioteca y dependencias externas

Esta opción puede ser útil en las siguientes situaciones:

  • Si usas un proceso de compilación personalizado del NDK en Gradle.
  • Si tus bibliotecas nativas se compilan en un módulo de biblioteca o de funciones o las proporciona un tercero.
  • Si la tarea de carga de símbolos automática falla o ves fallas no simbolizadas en el panel.

En la tarea estándar de carga de símbolos de Crashlytics, se supone que compilas tus bibliotecas nativas como parte de la compilación de Gradle del módulo de tu app con herramientas estándar de compilación del NDK, como CMake.

Sin embargo, si usas un proceso de compilación personalizado del NDK en Gradle, si tus bibliotecas nativas se compilan en módulos de biblioteca o de funciones, o si las proporciona un tercero, es posible que debas especificar de manera explícita la ruta de acceso a tus bibliotecas no eliminadas. Para lograrlo, puedes agregar la propiedad unstrippedNativeLibsDir dentro de la extensión de Crashlytics en el archivo de compilación de Gradle.

  1. Asegúrate de que completaste las siguientes tareas iniciales del flujo de trabajo principal mencionadas antes en esta página:

    1. Habilitaste Crashlytics en Firebase console.

    2. Se agregó el SDK de Crashlytics para el NDK y el complemento de Gradle de Crashlytics.

    3. Agregar la extensión de Crashlytics a tu compilación

    4. Configurar la carga automática de símbolos nativos

  2. Para que la tarea de carga automática de símbolos pueda encontrar la información de tu símbolo, agrega lo siguiente al archivo de Gradle (generalmente<project>/<app-module>/build.gradle.kts o<project>/<app-module>/build.gradle) del módulo (a nivel de la app):

    import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension
    
    // ...
    
    android {
        // ...
        buildTypes {
            release {
                configure<CrashlyticsExtension> {
                    nativeSymbolUploadEnabled = true
                    unstrippedNativeLibsDir = file("PATH/TO/UNSTRIPPED/DIRECTORY")
                }
            }
        }
    }
    // ...
    
    android {
        // ...
        buildTypes {
            release {
                firebaseCrashlytics {
                    nativeSymbolUploadEnabled true
                    unstrippedNativeLibsDir file("PATH/TO/UNSTRIPPED/DIRECTORY")
                }
            }
        }
    }

    El complemento de Crashlytics buscará de manera recurrente en el directorio especificado bibliotecas nativas con una extensión .so. Luego, Crashlytics extrae símbolos de depuración de todas esas bibliotecas y los sube a los servidores de Firebase.

    Esto es lo que puedes especificar en la propiedad unstrippedNativeLibsDir:

    • Cualquier argumento permitido para org.gradle.api.Project#files(Object...), incluidos: java.lang.String, java.io.File o org.gradle.api.file.FileCollection

    • Varios directorios para una sola variante de compilación proporcionando una lista o una instancia de FileCollection

    • (A partir del complemento de Gradle de Crashlytics v3.0.0) Acumular varios directorios en productos individuales y variantes de compilación.

      buildTypes {
        release {
          configure<CrashlyticsExtension> {
            nativeSymbolUploadEnabled = true
            unstrippedNativeLibsDir = file("MY/NATIVE/LIBS")
          }
        }
        productFlavors {
          flavorDimensions += "feature"
          create("basic") {
            dimension = "feature"
            // ...
          }
          create("featureX") {
            dimension = "feature"
            configure<CrashlyticsExtension> {
              unstrippedNativeLibsDir = file("MY/FEATURE_X/LIBS")
            }
          }
        }
      }
      

    La tarea uploadCrashlyticsSymbolFilesBasicRelease solo subirá los símbolos en MY/NATIVE/LIBS, pero uploadCrashlyticsSymbolFilesFeatureXRelease subirá símbolos en ambos MY/NATIVE/LIBS y MY/FEATURE_X/LIBS.

  3. Por último, fuerza una falla de prueba para terminar de configurar Crashlytics y ver los datos iniciales en el panel de Crashlytics de Firebase console.

Opción: Sube símbolos para compilaciones que no sean de Gradle o bibliotecas nativas inaccesibles no eliminadas

Esta opción puede ser útil en las siguientes situaciones:

  • Si usas un proceso de compilación diferente de Gradle.

  • Si se proporcionan tus bibliotecas nativas no eliminadas de alguna manera en la que no sean accesibles durante las compilaciones de Gradle.

Esta opción requiere que ejecutes un comando de Firebase CLI cuando creas una compilación de lanzamiento o cualquier compilación para la que quieras ver seguimientos de pila simbolizados en Firebase console.

  1. Asegúrate de que completaste las siguientes tareas iniciales del flujo de trabajo principal mencionadas antes en esta página:

    1. Habilitaste Crashlytics en Firebase console.

    2. Se agregó el SDK de Crashlytics para el NDK y el complemento de Gradle de Crashlytics.

    Ten en cuenta que, con esta opción, no necesitas agregar la extensión firebaseCrashlytics ni configurar la carga automática de símbolos porque, en su lugar, usarás Firebase CLI (los pasos que se indican a continuación) para generar y subir tus archivos de símbolos.

  2. Configura tu entorno y proyecto para subir símbolos:

    1. Sigue las instrucciones para instalar Firebase CLI.

      Si ya instalaste la CLI, asegúrate de actualizarla a su versión más reciente.

    2. Solo para apps que usan el nivel de API de Android 30 o superior: Actualiza la plantilla AndroidManifest.xml de tu app para inhabilitar el etiquetado de punteros:

      1. Marca la casilla de Android Player Settings > Publishing Settings > Build > Custom Main Manifest.

      2. Abre la plantilla de manifiesto ubicada en Assets/Plugins/Android/AndroidManifest.xml.

      3. Agrega el siguiente atributo a la etiqueta de aplicación: <application android:allowNativeHeapPointerTagging="false" ... />.

  3. Compila tu proyecto.

  4. Sube la información sobre tus símbolos.

    Una vez que finalice la compilación, genera un archivo de símbolos compatible con Crashlytics y súbelo a los servidores de Firebase; para ello, ejecuta el siguiente comando de Firebase CLI:

    firebase crashlytics:symbols:upload --app=FIREBASE_APP_ID PATH/TO/SYMBOLS
    • FIREBASE_APP_ID: El ID de la app para Android de Firebase (no el nombre del paquete)
      ID de ejemplo de la app para Android de Firebase: 1:567383003300:android:17104a2ced0c9b9b

      Puedes hacerlo de las siguientes dos maneras:

      • En el archivo google-services.json, el ID de la app es el valor mobilesdk_app_id.

      • En Firebase console, ve a Configuración del proyecto. Desplázate hacia abajo hasta la tarjeta Tus apps y, luego, haz clic en la app de Firebase que quieres para buscar su ID.

    • PATH/TO/SYMBOLS es la ruta de acceso al archivo de símbolos que genera la CLI

      • Se exporta a un proyecto de Android Studio: PATH/TO/SYMBOLS puede ser cualquier directorio. Firebase CLI buscará de manera recurrente las bibliotecas nativas en el directorio especificado con una extensión .so.

      • Si se compiló el APK directamente en Unity: PATH/TO/SYMBOLS es la ruta de acceso del archivo de símbolos comprimido que se generó en el directorio raíz del proyecto cuando finalizó la compilación (por ejemplo: myproject/myapp-1.0-v100.symbols.zip).

    Marca Descripción
    --generator=csym

    Usa el generador de archivos de símbolos cSYM heredado en lugar del generador de Breakpad predeterminado.

    No se recomienda utilizarlo. Recomendamos usar el generador de archivos de símbolos de Breakpad predeterminado.

    --generator=breakpad

    Usa el generador de archivos de símbolos de Breakpad.

    Ten en cuenta que la configuración predeterminada para generar archivos de símbolos es Breakpad. Solo debes usar esta marca si agregaste symbolGenerator { csym() } a la configuración de tu compilación y quieres anularla para usar Breakpad.

    --dry-run

    Genera los archivos de símbolos, pero no los sube.

    Esta marca es útil si quieres inspeccionar el contenido de los archivos que se envían.

    --debug Proporciona información de depuración adicional.
  5. Por último, fuerza una falla de prueba para terminar de configurar Crashlytics y ver los datos iniciales en el panel de Crashlytics de Firebase console.

    Después de compilar la app como parte de forzar una falla, asegúrate de ejecutar el comando crashlytics:symbols:upload de Firebase CLI para subir el archivo de símbolos.