Свяжите Gradle с вашей родной библиотекой

Чтобы включить проект собственной библиотеки в качестве зависимости сборки Gradle, вам необходимо предоставить Gradle путь к файлу сценария CMake или ndk-build. Когда вы создаете свое приложение, Gradle запускает CMake или ndk-build и упаковывает общие библиотеки вместе с вашим приложением. Gradle также использует сценарий сборки, чтобы знать, какие файлы следует вставить в проект Android Studio, чтобы вы могли получить к ним доступ из окна проекта . Если у вас нет сценария сборки для собственных источников, вам необходимо создать сценарий сборки CMake, прежде чем продолжить.

Каждый модуль в вашем проекте Android может быть связан только с одним файлом сценария CMake или ndk-build. Так, например, если вы хотите собрать и упаковать выходные данные из нескольких проектов CMake, вам нужно использовать один файл CMakeLists.txt в качестве сценария сборки CMake верхнего уровня (с которым вы затем связываете Gradle) и добавлять другие проекты CMake в качестве зависимостей. этого сценария сборки. Аналогично, если вы используете ndk-build, вы можете включить другие файлы Makefile в файл сценария Android.mk верхнего уровня.

После того как вы свяжете Gradle с собственным проектом, Android Studio обновит панель «Проект» , чтобы отобразить исходные файлы и собственные библиотеки в группе cpp , а также ваши внешние сценарии сборки в группе «Внешние файлы сборки» .

Примечание. При внесении изменений в конфигурацию Gradle обязательно примените изменения, нажав «Синхронизировать проект». на панели инструментов. Кроме того, при внесении изменений в файл сценария CMake или ndk-build после того, как вы уже связали его с Gradle, вам следует синхронизировать Android Studio с вашими изменениями, выбрав «Сборка» > «Обновить связанные проекты C++» в строке меню.

Вы можете связать Gradle с внешним проектом CMake или ndk-build, используя пользовательский интерфейс Android Studio:

  1. Откройте панель «Проект» в левой части IDE и выберите представление Android .
  2. Щелкните правой кнопкой мыши модуль, который вы хотите связать с собственной библиотекой, например модуль приложения , и выберите в меню «Связать проект C++ с Gradle» . Вы должны увидеть диалоговое окно, подобное показанному на рисунке 4.
  3. В раскрывающемся меню выберите CMake или ndk-build .
    1. Если вы выберете CMake , используйте поле рядом с «Путь к проекту» , чтобы указать файл сценария CMakeLists.txt для вашего внешнего проекта CMake.
    2. Если вы выберете ndk-build , используйте поле рядом с «Путь к проекту» , чтобы указать файл сценария Android.mk для вашего внешнего проекта ndk-build. Android Studio также включает файл Application.mk если он находится в том же каталоге, что и ваш файл Android.mk .

    Рисунок 4. Связывание внешнего проекта C++ с помощью диалогового окна Android Studio.

  4. Нажмите ОК .

Настройте Gradle вручную

Чтобы вручную настроить Gradle для связи с вашей собственной библиотекой, вам необходимо добавить блок externalNativeBuild в файл build.gradle на уровне модуля и настроить его с помощью блока cmake или 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"
   
}
 
}
}
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")
   
}
 
}
}

Примечание. Если вы хотите связать Gradle с существующим проектом ndk-build, используйте блок ndkBuild вместо блока cmake и укажите относительный путь к файлу Android.mk . Gradle также включает файл Application.mk если он находится в том же каталоге, что и ваш файл Android.mk .

Укажите дополнительные конфигурации

Вы можете указать дополнительные аргументы и флаги для CMake или ndk-build, настроив другой блок externalNativeBuild в блоке defaultConfig вашего файла build.gradle на уровне модуля. Подобно другим свойствам в блоке defaultConfig , вы можете переопределить эти свойства для каждого варианта продукта в вашей конфигурации сборки.

Например, если ваш проект CMake или ndk-build определяет несколько собственных библиотек и исполняемых файлов, вы можете использовать свойство targets для сборки и упаковки только подмножества этих артефактов для данного варианта продукта. В следующем примере кода описаны некоторые свойства, которые можно настроить:

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

Чтобы узнать больше о настройке разновидностей продукта и вариантов сборки, перейдите в раздел Настройка вариантов сборки . Список переменных, которые можно настроить для CMake с помощью свойства arguments , см. в разделе Использование переменных CMake .

Включить готовые собственные библиотеки

Если вы хотите, чтобы Gradle упаковывал готовые собственные библиотеки, которые не используются ни в одной внешней собственной сборке, добавьте их в каталог src/main/jniLibs/ ABI вашего модуля.

Версии подключаемого модуля Android Gradle до 4.0 требуют включения IMPORTED целей CMake в каталог jniLibs , чтобы они могли быть включены в ваше приложение. Если вы переходите с более ранней версии плагина, вы можете столкнуться с такой ошибкой:

* 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'

Если вы используете плагин Android Gradle 4.0, переместите все библиотеки, используемые IMPORTED целями CMake, из каталога jniLibs , чтобы избежать этой ошибки.

Укажите ABI

По умолчанию Gradle собирает вашу собственную библиотеку в отдельные файлы .so для двоичных интерфейсов приложений (ABI), которые поддерживает NDK, и упаковывает их все в ваше приложение. Если вы хотите, чтобы Gradle собирал и упаковывал только определенные конфигурации ABI ваших собственных библиотек, вы можете указать их с помощью флага ndk.abiFilters в файле build.gradle на уровне модуля, как показано ниже:

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

В большинстве случаев вам нужно указать только abiFilters в блоке ndk , как показано выше, поскольку он сообщает Gradle как собрать, так и упаковать эти версии ваших собственных библиотек. Однако, если вы хотите контролировать, что Gradle должен создавать, независимо от того, что вы хотите, чтобы он упаковал в ваше приложение, настройте еще один флаг abiFilters в блоке defaultConfig.externalNativeBuild.cmake (или блоке defaultConfig.externalNativeBuild.ndkBuild ). Gradle создает эти конфигурации ABI, но упаковывает только те, которые вы указываете в блоке defaultConfig.ndk .

Рекомендуется публиковать с помощью пакетов приложений Android, чтобы еще больше уменьшить размер вашего приложения, поскольку при загрузке будут доставлены только собственные библиотеки, соответствующие ABI устройства пользователя.

Для публикации устаревших приложений с использованием APK (созданных до августа 2021 г.) рассмотрите возможность настройки нескольких APK на основе ABI — вместо создания одного большого APK со всеми версиями собственных библиотек Gradle создает отдельный APK для каждого ABI, который вы хотите поддерживать, и только упаковывает файлы, необходимые каждому ABI. Если вы настраиваете несколько APK для каждого ABI без указания флага abiFilters , как показано в примере кода выше, Gradle создает все поддерживаемые версии ABI ваших собственных библиотек, но упаковывает только те, которые вы указываете в конфигурации с несколькими APK. Чтобы избежать создания ненужных версий собственных библиотек, предоставьте один и тот же список ABI как для флага abiFilters , так и для конфигурации нескольких APK для каждого ABI.