Vincular o Gradle à biblioteca nativa

Para incluir seu projeto de biblioteca nativa como uma dependência de compilação do Gradle, é preciso que o Gradle tenha o caminho para seu arquivo de script CMake ou ndk-build. Quando você compila um app, o Gradle executa o CMake ou o ndk-build e empacota bibliotecas compartilhadas no APK. O Gradle também usa o script de compilação para saber quais arquivos incluir no projeto do Android Studio, para que você possa acessá-los na janela Project. Se você não tem um script de compilação para as origens nativas, é preciso criar um script de compilação CMake antes de continuar.

Cada módulo do projeto Android pode vincular apenas um arquivo de script CMake ou ndk-build. Por exemplo, se você quiser compilar e empacotar saídas de vários projetos CMake, será necessário usar um arquivo CMakeLists.txt como seu script de compilação de nível superior do CMake (a que o Gradle será vinculado) e adicionar outros projetos CMake como dependências desse script de compilação. Da mesma forma, se você estiver usando o ndk-build, poderá incluir outros Makefiles ao arquivo de script Android.mk de nível superior.

Depois de vincular o Gradle a um projeto nativo, o Android Studio atualiza o painel Project para mostrar os arquivos de origem e as bibliotecas nativas no grupo cpp, e os scripts de compilação externos no grupo External Build Files.

Observação: quando você fizer alterações na configuração do Gradle, aplique-as clicando em Sync Project , na barra de ferramentas. Além disso, se você alterar o arquivo de script CMake ou ndk-build depois de vinculá-lo ao Gradle, selecione Build > Refresh Linked C++ Projects na barra de menus para sincronizar o Android Studio com as alterações.

Você pode usar a IU do Android Studio para vincular o Gradle a um projeto CMake ou ndk-build externo:

  1. Abra o painel Project à esquerda do ambiente de desenvolvimento integrado e selecione a visualização Android.
  2. Clique com o botão direito no módulo que você quer vincular à biblioteca nativa, por exemplo, o módulo app, e selecione {Link C++ Project with Gradle/0} no menu. Você deve ver uma caixa de diálogo semelhante à mostrada na figura 4.
  3. No menu suspenso, selecione CMake ou ndk-build.
    1. Se você selecionar CMake, use o campo ao lado de Project Path para especificar o arquivo de script CMakeLists.txt para o projeto CMake externo.
    2. Se você selecionar ndk-build, use o campo ao lado de Project Path para especificar o arquivo de script Android.mk para o projeto ndk-build externo. O Android Studio também incluirá o arquivo Application.mk se ele estiver localizado no mesmo diretório do arquivo Android.mk.

    Figura 4. Vincular um projeto C++ externo usando a caixa de diálogo do Android Studio.

  4. Clique em OK.

Configurar manualmente o Gradle

Para configurar manualmente o Gradle para que ele seja vinculado à biblioteca nativa, adicione o bloco externalNativeBuild ao arquivo build.gradle do módulo e configure-o com o bloco cmake ou 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"
        }
      }
    }
    

Observação: para vincular o Gradle a um projeto ndk-build existente, use o bloco ndkBuild em vez de cmake e informe um caminho relativo para o arquivo Android.mk. O Gradle também incluirá o arquivo Application.mk se ele estiver localizado no mesmo diretório do arquivo Android.mk.

Especificar configurações opcionais

Para especificar argumentos e sinalizadores opcionais para o CMake ou o ndk-build, configure outro bloco externalNativeBuild dentro do bloco defaultConfig do arquivo build.gradle do módulo. Assim como em outras propriedades do bloco defaultConfig, é possível modificar essas propriedades para cada variação de produto na configuração da compilação.

Por exemplo: se o projeto CMake ou ndk-build definir várias bibliotecas e executáveis nativos, você poderá usar a propriedade targets para compilar e empacotar apenas um subconjunto desses artefatos para uma determinada variação de produto. O exemplo de código a seguir descreve algumas das propriedades que podem ser configuradas:

    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 saber mais sobre como configurar variações de produtos e de compilação, consulte Configurar variações de compilação. Para ver uma lista de variáveis que podem ser configuradas para o CMake com a propriedade arguments, consulte Como usar variáveis do CMake no Gradle.

Incluir bibliotecas nativas pré-compiladas

Se você quiser que o Gradle empacote bibliotecas nativas pré-compiladas com seu APK, modifique a configuração do conjunto de origem padrão para incluir o diretório dos arquivos .so pré-compilados, como mostrado abaixo. Não é necessário fazer isso para incluir artefatos de scripts de compilação do CMake vinculados ao Gradle.

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

Especificar ABIs

Por padrão, o Gradle compila a biblioteca nativa em arquivos .so separados para as Interfaces binárias de aplicativos (ABIs, na sigla em inglês) com que o NDK é compatível e os empacota no APK. Se você quiser que o Gradle compile e empacote somente algumas configurações de ABI das suas bibliotecas nativas, especifique-as com o sinalizador ndk.abiFilters no arquivo build.gradle do módulo, como mostrado abaixo:

    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 {...}
    }
    

Na maioria dos casos, basta especificar abiFilters no bloco ndk, como mostrado acima, porque isso instrui o Gradle a compilar e compactar essas versões das bibliotecas nativas. No entanto, se você quiser controlar o que o Gradle deve compilar, independentemente daquilo que você quer que ele empacote no APK, configure outro sinalizador abiFilters no bloco defaultConfig.externalNativeBuild.cmake (ou no bloco defaultConfig.externalNativeBuild.ndkBuild). O Gradle compila essas configurações de ABI, mas empacota apenas aquelas especificadas no bloco defaultConfig.ndk.

Para reduzir ainda mais o tamanho do APK, é possível configurar vários APKs baseados em ABIs. Em vez de criar um único APK grande com todas as versões das bibliotecas nativas, o Gradle cria um APK separado para cada ABI compatível e empacota apenas os arquivos necessários para cada ABI. Se você configurar vários APKs por ABI sem especificar o sinalizador abiFilters, como mostrado no exemplo de código acima, o Gradle compilará todas as versões de ABI compatíveis das bibliotecas nativas, mas empacotará somente aquelas especificadas na configuração de vários APKs. Para evitar a compilação de versões indesejadas de bibliotecas nativas, disponibilize a mesma lista de ABIs para o sinalizador abiFilters e para sua configuração de vários APKs por ABI.