Vincular o Gradle à sua biblioteca nativa

Para incluir seu projeto de biblioteca nativa como uma dependência de compilação do Gradle, é necessário que o Gradle tenha o caminho para seu arquivo de script CMake ou ndk-build. Quando você cria um app, o Gradle executa o CMake ou o ndk-build e empacota bibliotecas compartilhadas no app. 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 build para as origens nativas, é necessário criar um script de build 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 criar e empacotar saídas de vários projetos CMake, será necessário usar um arquivo CMakeLists.txt como seu script de compilação CMake de nível superior, ao qual o Gradle será vinculado, e incluir outros projetos do CMake como dependências desse script. Da mesma forma, se você estiver usando o ndk-build, poderá incluir outros Makefiles (link em inglês) 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: ao fazer mudanças na configuração do Gradle, aplique-as clicando em Sync Project na barra de ferramentas. Além disso, se você mudar o arquivo de script CMake ou ndk-build após vinculá-lo ao Gradle, selecione Build > Refresh Linked C++ Projects na barra de menus para sincronizar o Android Studio com as mudanças.

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ê 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 no mesmo diretório que o 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 o Gradle manualmente e vinculá-lo à sua biblioteca nativa, é necessário adicionar o bloco externalNativeBuild ao arquivo build.gradle do módulo e configurá-lo com o bloco cmake ou ndkBuild (links em inglês):

Groovy

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"
    }
  }
}

Kotlin

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 = file("CMakeLists.txt")
    }
  }
}

Observação: caso queira vincular o Gradle a um projeto existente do ndk-build, use o bloco ndkBuild em vez do bloco cmake (links em inglês) 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 que o Android.mk.

Especificar outras configurações

Você pode especificar outros argumentos e sinalizações para o CMake ou ndk-build configurando outro bloco externalNativeBuild (link em inglês) no bloco defaultConfig do arquivo build.gradle do módulo. Assim como em outras propriedades no bloco defaultConfig, é possível substituir 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 nativas e executáveis, você poderá usar a propriedade targets (link em inglês) para criar e empacotar apenas um subconjunto desses artefatos para determinada variação de produto. O exemplo de código a seguir descreve algumas das propriedades que podem ser configuradas:

Groovy

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

Kotlin

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 += listOf("-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang")

        // Sets a flag to enable format macro constants for the C compiler.
        cFlags += listOf("-D__STDC_FORMAT_MACROS")

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

  buildTypes {...}

  productFlavors {
    ...
    create("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 app.
          targets += listOf("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 app satisfies most project requirements.
                  "my-executible-demo")
        }
      }
    }

    create("paid") {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets += listOf("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 variantes de build, consulte Configurar variantes de build. Para ver uma lista de variáveis que podem ser configuradas para o CMake com a propriedade arguments, consulte Usar variáveis do CMake.

Incluir bibliotecas nativas pré-compiladas

Se você quiser que o Gradle empacote bibliotecas nativas pré-compiladas que não sejam usadas em nenhum build nativo externo, adicione-as ao diretório src/main/jniLibs/ABI do módulo.

Versões do Plug-in do Android para Gradle anteriores à 4.0 exigiam a inclusão de destinos IMPORTED do CMake no diretório jniLibs para que elas fossem incluídas no app. Se você estiver migrando de uma versão anterior do plug-in, poderá encontrar um erro como este:

* What went wrong:
Execution failed for task ':app:mergeDebugNativeLibs'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > More than one file was found with OS independent path 'lib/x86/libprebuilt.so'

Se você estiver usando o Plug-in do Android para Gradle 4.0, remova do diretório jniLibs todas as bibliotecas usadas pelos destinos IMPORTED do CMake para evitar esse erro.

Especificar ABIs

Por padrão, o Gradle cria sua biblioteca nativa em arquivos .so separados para as Interfaces binárias do aplicativo (ABIs) compatíveis com o NDK e inclui todas elas no app. Se você quiser que o Gradle crie e inclua somente determinadas configurações de ABI das suas bibliotecas nativas, especifique-as com a sinalização ndk.abiFilters (link em inglês) no arquivo build.gradle no nível do módulo, conforme mostrado abaixo:

Groovy

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 app.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

Kotlin

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 app.
      abiFilters += listOf("x86", "x86_64", "armeabi", "armeabi-v7a",
                   "arm64-v8a")
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

Na maioria dos casos, basta especificar abiFilters no bloco ndk, conforme mostrado acima, porque isso instrui o Gradle a criar e empacotar essas versões das suas bibliotecas nativas. No entanto, caso queira controlar o que o Gradle vai criar, independente do que você quer que ele empacote no app, configure outra sinalização abiFilters no bloco defaultConfig.externalNativeBuild.cmake (ou o bloco defaultConfig.externalNativeBuild.ndkBuild). O Gradle cria essas configurações de ABI, mas só empacota as especificadas no bloco defaultConfig.ndk (link em inglês).

É recomendável publicar usando Android App Bundles para reduzir ainda mais o tamanho do app, já que somente as bibliotecas nativas correspondentes à ABI do dispositivo do usuário serão enviadas com o download.

Para apps legados que publicam usando APKs (criados antes de agosto de 2021), considere configurar vários APKs com base na ABI, em vez de criar um APK grande com todas as versões das bibliotecas nativas. O Gradle cria um APK separado para cada ABI compatível e inclui apenas os arquivos necessários para cada uma. Se você configurar vários APKs por ABI sem especificar a sinalização abiFilters, conforme mostrado no exemplo de código acima, o Gradle criará 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 criação de versões indesejadas das bibliotecas nativas, informe a mesma lista de ABIs para a sinalização abiFilters e para sua configuração de vários APKs por ABI.