Conceptos

Antes de comenzar

En esta guía, se considera 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 de 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:

  • Migrar las 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 aplicación 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 de 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 de Java y C++ se comunican entre sí. En esta guía, se da por sentado que ya conoces la JNI. Para obtener información sobre ella, consulta la Especificación de la interfaz nativa de Java.

  • Interfaz binaria de la aplicación (ABI): Define exactamente cómo se espera que el código máquina de tu app interactúe con el sistema durante el tiempo de ejecución. El NDK compila archivos .so según 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 ABI de Android.

  • Manifiesto: Si escribes una app sin componentes de Java, debes declarar la clase NativeActivity en el manifiesto. Consulta Cómo usar la interfaz native_activity.h para obtener más información al respecto.

Flujo

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.

  2. Crea un proyecto de app para Android como lo harías en cualquier otro proyecto de Android.

  3. Si escribes una app nativa, declara la clase NativeActivity en AndroidManifest.xml. Para obtener más información, consulta Aplicaciones y actividades nativas.

  4. Crea un archivo Android.mk que describa la biblioteca nativa y que incluya el nombre, las marcas, las bibliotecas vinculadas y los archivos de origen que se van a compilar en el directorio "JNI".

  5. De manera opcional, puedes crear un archivo Application.mk configurando las ABI de destino, la cadena de herramientas, el modo de lanzamiento y depuración, y la STL. Si no especificas alguno de estos, se usan los siguientes valores predeterminados, respectivamente:

    • ABI: todas las ABI que no están obsoletas
    • 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 nativas (.so, .a).

  8. Compila el componente Java y crea el archivo .dex ejecutable.

  9. Empaqueta todo en un archivo APK que contenga .so, .dex y todos los demás archivos necesarios para que se ejecute la app.

Aplicaciones y actividades nativas

El SDK de Android proporciona una clase de ayuda, NativeActivity, que te permite escribir una actividad completamente nativa. NativeActivity controla la comunicación entre el framework 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 la aplicación es nativa en el archivo AndroidManifest.xml y comenzar a crearla.

Una aplicación para Android que usa NativeActivity aún se ejecuta en su propia máquina virtual, en una zona de pruebas separada de las demás aplicaciones. Por lo tanto, aún puedes acceder a las API del framework de Android mediante la JNI. Sin embargo, en algunos casos (p. ej., 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 sobre ese tipo de compatibilidad, consulta API nativas.

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

El NDK de Android 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 necesarias para crear tu actividad nativa. Debido a que el subproceso principal de la aplicación controla las devoluciones de llamada, implementarlas no debe generar bloqueos. Si lo hace, podrías recibir errores ANR (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 estática de ayuda compilada por encima de la interfaz 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.

También está disponible la fuente <ndk_root>/sources/android/native_app_glue/android_native_app_glue.c, que te permite modificar la implementación.

A fin de obtener más información para usar esta biblioteca estática, explora la aplicación de muestra de native-activity y su documentación. Puedes leer más acerca de este tema en los comentarios del archivo <ndk_root>/sources/android/native_app_glue/android_native_app_glue.h.

Cómo usar la interfaz native_activity.h

Para implementar una actividad nativa con la interfaz native_activity.h, sigue estos pasos:

  1. Crea un directorio jni/ en el directorio raíz de tu proyecto. Ese directorio almacenará todo el código nativo.

  2. Declara la actividad nativa en el archivo AndroidManifest.xml.

    Como tu aplicación no tiene código Java, configura android:hasCode como false.

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

    Debes configurar el atributo android:name de la etiqueta de actividad como NativeActivity.

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

    El atributo android:value de la etiqueta meta-data especifica el nombre de la biblioteca compartida que contiene el punto de entrada a la aplicación (p. ej., main en C/C++), y omite el prefijo lib y el sufijo .so del nombre de la biblioteca.

    <manifest>
      <application>
        <activity>
          <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>
    
  3. Crea un archivo para tu actividad nativa e implementa la función nombrada en la variable ANativeActivity_onCreate. La app llama a esta función cuando se inicia la actividad nativa. Esta función, que es análoga a main en C/C++, recibe un puntero para una estructura ANativeActivity, que contiene punteros de función a las distintas implementaciones de devoluciones de llamada 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 llamada.

  4. Configura el campo ANativeActivity->instance con la dirección de cualquier instancia de datos específicos que quieras usar.

  5. Implementa todas las demás acciones que desees que tu actividad realice al iniciarse.

  6. Implementa el resto de las devoluciones de llamada que configuraste en ANativeActivity->callbacks. Para obtener más información sobre cuándo se deben invocar las devoluciones de llamada, consulta Cómo administrar el ciclo de vida de la actividad.

  7. Desarrolla el resto de tu aplicación.

  8. Crea un Android.mk file en el directorio jni/ de tu proyecto para describir el módulo nativo en el sistema de compilación. Para obtener más información, consulta Android.mk.

  9. Una vez que tengas un archivo Android.mk, compila tu código nativo con el comando ndk-build.

    cd <path>/<to>/<project>
    $NDK/ndk-build
    
  10. Compila e instala tu proyecto de 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 esta en el APK.

Código de muestra adicional

Para descargar muestras del NDK, consulta Muestras del NDK.