Conceptos

Antes de comenzar

En esta guía, se asume que ya conoces los conceptos inherentes a la programación nativa y al desarrollo de Android.

Introducción

En esta sección, se proporciona una explicación detallada del funcionamiento del NDK. Android NDK es un conjunto de herramientas que te permiten incorporar código C o C++ ("código nativo") a tus apps de Android. La capacidad para usar código nativo en apps de Android puede ser particularmente útil para los desarrolladores que deseen realizar una o varias de las siguientes acciones:

  • Conectar sus apps entre plataformas.
  • Reutilizar bibliotecas existentes o proporcionar sus propias bibliotecas para reutilizarlas.
  • Aumentar el rendimiento en ciertos casos, en especial los de mayor exigencia computacional, como el de los juegos.

Cómo funciona

En esta sección, se presentan los componentes principales que se usan en la compilación de una app nativa para Android, y se describen los procesos de compilación y empaquetado.

Componentes principales

Debes comprender los siguientes componentes a medida que compilas tu app:

  • Bibliotecas compartidas nativas: El NDK compila estas bibliotecas, o archivos .so, a partir de tu código fuente C/C++.
  • Bibliotecas estáticas nativas: El NDK también puede compilar bibliotecas estáticas, o archivos .a, que puedes vincular con otras bibliotecas.
  • Interfaz nativa de Java (JNI): Es la interfaz mediante la cual los componentes Java y C++ se comunican entre sí. En esta guía, se asume que ya conoces la JNI. Para obtener información sobre ella, consulta la Especificación de la interfaz nativa Java.
  • Interfaz binaria de aplicaciones (ABI): La ABI define exactamente cómo se espera que el código máquina de tu app interactúe con el sistema en el tiempo de ejecución. El NDK compila archivos .so con estas definiciones. Las diferentes ABI corresponden a las distintas arquitecturas: El NDK incluye compatibilidad con ABI para ARM de 32 bits, AArch64, x86 y x86-64. Para obtener más información, consulta Administración de ABI.
  • Manifiesto: Si escribes una app sin componente Java, debes declarar la clase NativeActivity en el manifiesto. En la sección "Cómo usar la interfaz native_activity.h" de Apps y actividades nativas se proporciona información detallada sobre cómo hacerlo.

Fluir

El flujo general para desarrollar una app nativa para Android es el siguiente:

  1. Diseña tu app y decide las partes que implementarás en Java, así como las que implementarás como código nativo.

    Nota: Si bien es posible evitar el uso de Java completamente, quizá te resulte útil el marco de trabajo de Java para Android para tareas que incluyen el control de la pantalla y la interfaz de usuario.

  2. Crea un proyecto de aplicación para Android como lo harías en cualquier otro proyecto de Android.
  3. Si escribes una app sólo con código nativo, declara la clase NativeActivity en AndroidManifest.xml. Para obtener más información, consulta Apps y actividades nativas.
  4. Crea un archivo Android.mk que describa la biblioteca nativa y, también, incluya el nombre, los indicadores, las bibliotecas vinculadas y los archivos de origen que se compilarán en el directorio "JNI".
  5. De manera opcional, puedes crear un archivo Application.mk si configuras las ABI de destino, la cadena de herramientas, el modo de lanzamiento y depuración, y la STL.
    • ABI: todas las ABI que no están obsoletas
    • Cadena de herramientas: Clang
    • Modo: Lanzamiento
    • STL: system
  6. Coloca tu archivo de origen nativo en el directorio jni del proyecto.
  7. Usa ndk-build para compilar las bibliotecas (.so, .a) nativas.
  8. Compila el componente de Java, que produce el archivo .dex ejecutable.
  9. Integra todo en un archivo APK que contenga .so, .dex y otros archivos necesarios para que tu app funcione.

Aplicaciones y actividades nativas

El SDK de Android proporciona una clase de ayuda, NativeActivity, que te permite escribir una actividad completamente nativa. NativeActivity maneja la comunicación entre el marco de trabajo de Android y tu código nativo, de manera que no tengas que crear una subclase ni llamar a sus métodos Lo único que debes hacer es declarar que tu app sea nativa en el archivo AndroidManifest.xml y comenzar a crearla.

Una app de Android que usa NativeActivity aún se ejecuta en su propia máquina virtual, en una zona de pruebas separada de las demás apps. Por lo tanto, aún puedes acceder a las API del marco de trabajo de Android por medio de JNI. Sin embargo, en algunos casos (como sucede con sensores, eventos de entrada y elementos), el NDK proporciona interfaces nativas que puedes usar en lugar de tener que realizar llamadas en toda la JNI. Para obtener más información, consulta API nativas del NDK de Android.

Independientemente de que desarrolles una actividad nativa o no, te recomendamos crear tus proyectos con las herramientas de compilación de Android tradicionales. De esta manera, podrás garantizar que las apps de Android se compilen y empaqueten con la estructura correcta.

Android NDK te ofrece dos opciones para implementar tu actividad nativa:

  • El encabezado native_activity.h define la versión nativa de la clase NativeActivity. Contiene la interfaz de devolución de llamada y las estructuras de datos que necesitas para crear tu actividad nativa. Debido a que el subproceso principal de tu app controla las devoluciones de llamada, las implementaciones de estas no deben generar bloqueos. Si lo hacen, podrías recibir errores ANR (la aplicación no responde), ya que el subproceso principal no responderá hasta que se muestre la devolución de llamada.
  • El archivo android_native_app_glue.h define una biblioteca auxiliar estática compilada encima de la interfaz de native_activity.h. Genera otro subproceso que controla, por ejemplo, las devoluciones de llamadas o los eventos de entrada en un bucle de eventos. Si mueves estos eventos a un subproceso independiente evitarás que las devoluciones de llamadas bloqueen tu subproceso principal.

El origen <ndk_root>/sources/android/native_app_glue/android_native_app_glue.c también está disponible, lo que te permite modificar la implementación.

Para obtener más información sobre cómo usar esta biblioteca estática, explora la app de muestra native-activity y su documentación. Podrás acceder a más contenido en los comentarios del archivo <ndk_root>/sources/android/native_app_glue/android_native_app_glue.h.

Uso de la interfaz native_activity.h

Para implementar una actividad nativa con la interfaz native_activity.h, haz lo siguiente:

  1. Crea un directorio jni/ en el directorio raíz de tu proyecto. Este directorio almacena todo tu código nativo.
  2. Declara tu actividad nativa en el archivo AndroidManifest.xml.
  3. Como tu app no tiene código Java, establece android:hasCode a false.

        <application android:label="@string/app_name" android:hasCode="false">
        

    Debes establecer el atributo android:name de la etiqueta de actividad a NativeActivity.

        <activity android:name="android.app.NativeActivity"
                    android:label="@string/app_name">
        

    Nota: Puedes generar la subclase NativeActivity. Si lo haces, usa el nombre de la subclase en lugar de NativeActivity.

    El atributo android:value de la etiqueta meta-data especifica el nombre de la biblioteca compartida que contiene el punto de entrada para la app (como C/C++ main), y omite el prefijo lib y el sufijo .so del nombre de la biblioteca.

                  <meta-data android:name="android.app.lib_name"
                    android:value="native-activity" />
                    <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
                      <category android:name="android.intent.category.LAUNCHER" />
                    </intent-filter>
                  </activity>
                </application>
              </manifest>
        
  4. Crea un archivo para tu actividad nativa y, luego, implementa esta función nombrada en la variable ANativeActivity_onCreate. La aplicación llama esta función cuando se inicia la actividad nativa. Esta función, que es análoga a main en C/C++, recibe un puntero a una estructura ANativeActivity, que contiene punteros de función a distintas implementaciones de devoluciones de llamadas que debes escribir. Configura los punteros de la función de devolución de llamada correspondientes en ANativeActivity->callbacks para las implementaciones de las devoluciones de llamadas.
  5. Establece el campo ANativeActivity->instance con la dirección de cualquier instancia de datos específicos que quieras usar.
  6. Implementa todas las demás acciones que desees que tu actividad realice al iniciarse.
  7. Implementa el resto de las devoluciones de llamada que estableciste en ANativeActivity->callbacks. Para obtener más información sobre cuándo se debe llamar a las devoluciones de llamada, consulta Cómo administrar el ciclo de vida de la actividad.
  8. Desarrolla el resto de tu aplicación.
  9. Crea un Android.mk file en el directorio jni/ de tu proyecto para describir tu módulo nativo al sistema de compilación. Para obtener más información, consulta Android.mk.
  10. Una vez que tengas un archivo Android.mk, compila tu código nativo con el comando ndk-build.
  11.     $ cd <path>/<to>/<project>
        $ <ndk>/ndk-build
        
  12. Compila e instala tu proyecto para Android como lo haces habitualmente. Si tu código nativo se encuentra en el directorio jni/, la secuencia de comandos de compilación empaqueta automáticamente los archivos .so compilados a partir de ella en el APK.

Código de ejemplo adicional

Para descargar muestras del NDK, consulta Muestras de NDK.