The Android Developer Challenge is back! Submit your idea before December 2.

Cómo vincular Gradle con tu biblioteca nativa

Para incluir tu proyecto de biblioteca nativa como una dependencia de compilación de Gradle, debes proporcionarle a Gradle la ruta de acceso al archivo de secuencia de comandos de CMake o ndk-build. Cuando compilas tu app, Gradle ejecuta CMake o ndk-build y empaqueta las bibliotecas compartidas con tu APK. Gradle también usa la secuencia de comandos de compilación a fin de determinar los archivos que se incluirán en tu proyecto de Android Studio, para que puedas acceder a ellos desde la ventana Project. Si no dispones de una secuencia de comandos de compilación para tus fuentes nativas, debes crear una secuencia de comandos de compilación de CMake para proceder.

Cada módulo del proyecto de Android puede vincularse a un solo archivo de secuencia de comandos de CMake o ndk-build. Por ejemplo, si deseas compilar y empaquetar resultados de varios proyectos CMake, debes usar un archivo CMakeLists.txt como secuencia de comandos de compilación de CMake de nivel superior (que luego se vincula a Gradle) y agregar otros proyectos CMake como dependencias de esa secuencia de comandos de compilación. Del mismo modo, si estás usando ndk-build, puedes incluir otros Makefiles en el archivo de secuencia de comandos Android.mk superior.

Una vez que vinculas Gradle con un proyecto nativo, Android Studio actualiza el panel Project para mostrar tus archivos de origen y bibliotecas nativas en el grupo cpp y tus secuencias de comandos de compilación externas en el grupo External Build Files.

Nota: Cuando se realicen cambios en la configuración de Gradle, asegúrate de aplicar tus cambios haciendo clic en Sync Project en la barra de herramientas. Además, cuando se realicen cambios en tu archivo de secuencia de comandos de CMake o ndk-build después de haberlo vinculado con Gradle, debes sincronizar Android Studio con tus cambios seleccionando Build > Refresh Linked C++ Projects en la barra de menú.

Puedes vincular Gradle con un proyecto externo de CMake o ndk-build usando la IU de Android Studio:

  1. Abre el panel Project del lado izquierdo de IDE y selecciona la vista de Android.
  2. Haz clic con el botón secundario en el módulo que desees vincular con tu biblioteca nativa (por ejemplo, el módulo de app) y selecciona Link C++ Project with Gradle en el menú. Verás un diálogo similar al que se muestra en la figura 4.
  3. En el menú desplegable, selecciona CMake o ndk-build.
    1. Si seleccionas CMake, usa el campo junto a Project Path para especificar el archivo de secuencia de comandos CMakeLists.txt de tu proyecto de CMake externo.
    2. Si seleccionas ndk-build, usa el campo junto a Project Path para especificar el archivo de secuencia de comandos Android.mk de tu proyecto de ndk-build externo. Android Studio también incluye el archivo Application.mk si se encuentra en el mismo directorio que tu archivo Android.mk.

    Figura 4: Vinculación de un proyecto externo de C ++ desde el cuadro de diálogo de Android Studio

  4. Haz clic en OK.

Cómo configurar Gradle manualmente

Para establecer manualmente que Gradle se vincule con tu biblioteca nativa, debes agregar el bloque externalNativeBuild a tu archivo de nivel de módulo build.gradle y configurarlo con el bloque cmake o ndkBuild:

    android {
      ...
      defaultConfig {...}
      buildTypes {...}

      // Encapsulates your external native build configurations.
      externalNativeBuild {

        // Encapsulates your CMake build configurations.
        cmake {

          // Provides a relative path to your CMake build script.
          path "CMakeLists.txt"
        }
      }
    }
    

Nota: Si deseas vincular Gradle con un proyecto ndk-build existente, usa el bloque ndkBuild de cmake y proporciona una ruta de acceso relativa a tu archivo Android.mk. Gradle también incluye el archivo Application.mk si se encuentra en el mismo directorio que tu archivo Android.mk.

Cómo especificar configuraciones opcionales

Puedes especificar argumentos e indicadores opcionales para CMake o ndk-build configurando otro bloque externalNativeBuild dentro del bloque defaultConfig de tu archivo de nivel de módulo build.gradle. Como en el caso de otras propiedades del bloque defaultConfig, puedes anular estas propiedades para cada clase de producto de tu configuración de compilación.

Por ejemplo, si tu proyecto CMake o ndk-build define varias bibliotecas nativas, puedes usar la propiedad targets para compilar y empaquetar solo un subconjunto de esas bibliotecas para una clase de producto dado. En el siguiente ejemplo de código se describen algunas de las propiedades que puedes configurar:

    android {
      ...
      defaultConfig {
        ...
        // This block is different from the one you use to link Gradle
        // to your CMake or ndk-build script.
        externalNativeBuild {

          // For ndk-build, instead use the ndkBuild block.
          cmake {

            // Passes optional arguments to CMake.
            arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

            // Sets a flag to enable format macro constants for the C compiler.
            cFlags "-D__STDC_FORMAT_MACROS"

            // Sets optional flags for the C++ compiler.
            cppFlags "-fexceptions", "-frtti"
          }
        }
      }

      buildTypes {...}

      productFlavors {
        ...
        demo {
          ...
          externalNativeBuild {
            cmake {
              ...
              // Specifies which native libraries or executables to build and package
              // for this product flavor. The following tells Gradle to build only the
              // "native-lib-demo" and "my-executible-demo" outputs from the linked
              // CMake project. If you don't configure this property, Gradle builds all
              // executables and shared object libraries that you define in your CMake
              // (or ndk-build) project. However, by default, Gradle packages only the
              // shared libraries in your APK.
              targets "native-lib-demo",
                      // You need to specify this executable and its sources in your CMakeLists.txt
                      // using the add_executable() command. However, building executables from your
                      // native sources is optional, and building native libraries to package into
                      // your APK satisfies most project requirements.
                      "my-executible-demo"
            }
          }
        }

        paid {
          ...
          externalNativeBuild {
            cmake {
              ...
              targets "native-lib-paid",
                      "my-executible-paid"
            }
          }
        }
      }

      // Use this block to link Gradle to your CMake or ndk-build script.
      externalNativeBuild {
        cmake {...}
        // or ndkBuild {...}
      }
    }
    

Para obtener más información sobre la configuración de clases de productos y variantes de compilación, consulta Cómo configurar variantes de compilación. Para ver una lista de variables que puedes configurar para CMake con la propiedad arguments, consulta Cómo usar variables de CMake.

Cómo incluir bibliotecas nativas compiladas previamente

Si deseas que Gradle empaque las bibliotecas nativas compiladas previamente con tu APK, modifica la configuración predeterminada del conjunto de fuentes de forma que se incluya el directorio de los archivos .so, tal como se muestra a continuación. Ten en cuenta que este paso no es necesario para incluir artefactos de secuencia de comandos de compilación de CMake que vincules a Gradle.

    android {
        ...
        sourceSets {
            main {
                jniLibs.srcDirs 'imported-lib/src/', 'more-imported-libs/src/'
            }
        }
    }
    

Cómo especificar las ABI

De forma predeterminada, Gradle compila la biblioteca nativa en archivos .so separados para las interfaces binarias de app (ABI) que admite el NDK y los empaqueta en el APK. Si deseas que Gradle compile y empaquete solo ciertas configuraciones de ABI de las bibliotecas nativas, puedes marcarlas con el indicador ndk.abiFilters en el archivo build.gradle de nivel de módulo, tal como se muestra a continuación:

    android {
      ...
      defaultConfig {
        ...
        externalNativeBuild {
          cmake {...}
          // or ndkBuild {...}
        }

        // Similar to other properties in the defaultConfig block,
        // you can configure the ndk block for each product flavor
        // in your build configuration.
        ndk {
          // Specifies the ABI configurations of your native
          // libraries Gradle should build and package with your APK.
          abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                       'arm64-v8a'
        }
      }
      buildTypes {...}
      externalNativeBuild {...}
    }
    

En la mayoría de los casos, solo es necesario especificar abiFilters en el bloque ndk, tal como se muestra arriba, ya que le indica a Gradle que debe compilar y empaquetar esas versiones de las bibliotecas nativas. Sin embargo, si deseas controlar lo que Gradle debe compilar, independientemente de lo que desees que empaquete en tu APK, configura otro indicador abiFilters en el bloque defaultConfig.externalNativeBuild.cmake (o el bloque defaultConfig.externalNativeBuild.ndkBuild). Gradle compila esas configuraciones de ABI pero solo empaqueta las especificadas en el bloque defaultConfig.ndk.

Para reducir aún más el tamaño del APK, una posibilidad es configurar varios APK basados en ABI. De esta forma, en lugar de crear un APK grande con todas las versiones de las bibliotecas nativas, Gradle crea un APK separado para cada ABI que se desea admitir y solo empaqueta los archivos que cada ABI necesita. Si configuras varios APK por ABI sin especificar el indicador abiFilters como se muestra en el ejemplo de código anterior, Gradle compila todas las versiones de ABI compatibles de las bibliotecas nativas, pero solo empaqueta las que se especifican en la configuración de varios APK. Para evitar compilar versiones de bibliotecas nativas que no desees, incluye la misma lista de ABI tanto para el indicador abiFilters como para la configuración de varios APK por ABI.