Свяжите 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.