Gradle mit Ihrer nativen Bibliothek verknüpfen

Wenn Sie das Projekt Ihrer nativen Bibliothek als Gradle-Build-Abhängigkeit hinzufügen möchten, müssen Sie Gradle den Pfad zu Ihrer CMake- oder ndk-build-Skriptdatei zur Verfügung stellen. Wenn Sie Ihre App erstellen, führt Gradle CMake oder ndk-build aus und verpackt gemeinsam genutzte Bibliotheken mit Ihrer App. Gradle verwendet außerdem das Build-Skript, um zu ermitteln, welche Dateien in Ihr Android Studio-Projekt geladen werden müssen, sodass Sie im Fenster Projekt darauf zugreifen können. Wenn Sie kein Build-Skript für Ihre nativen Quellen haben, müssen Sie ein Build-Script von CMake erstellen, bevor Sie fortfahren.

Jedes Modul in Ihrem Android-Projekt kann nur mit einer CMake- oder ndk-build-Skriptdatei verknüpft werden. Wenn Sie also beispielsweise Ausgaben aus mehreren CMake-Projekten erstellen und verpacken möchten, müssen Sie eine CMakeLists.txt-Datei als übergeordnetes CMake-Build-Skript verwenden, mit dem Sie dann Gradle verknüpfen, und weitere CMake-Projekte als Abhängigkeiten dieses Build-Skripts hinzufügen. Wenn Sie „ndk-build“ verwenden, können Sie andere Makefiles in die übergeordnete Android.mk-Skriptdatei einfügen.

Wenn Sie Gradle mit einem nativen Projekt verknüpfen, wird in Android Studio der Bereich Projekt aktualisiert. Dort werden Ihre Quelldateien und nativen Bibliotheken in der Gruppe cpp und Ihre externen Build-Skripts in der Gruppe Externe Build-Dateien angezeigt.

Hinweis:Wenn Sie Änderungen an der Gradle-Konfiguration vornehmen, müssen Sie sie übernehmen. Klicken Sie dazu in der Symbolleiste auf Projekt synchronisieren . Wenn Sie Änderungen an Ihrer CMake- oder ndk-build-Skriptdatei vornehmen, nachdem Sie sie bereits mit Gradle verknüpft haben, sollten Sie Android Studio mit Ihren Änderungen synchronisieren. Wählen Sie dazu in der Menüleiste Build > Refresh Linked C++ Projects (Verknüpfte C++-Projekte aktualisieren) aus.

Sie können Gradle über die Android Studio-UI mit einem externen CMake- oder ndk-build-Projekt verknüpfen:

  1. Öffnen Sie links in der IDE den Bereich Projekt und wählen Sie die Ansicht Android aus.
  2. Klicken Sie mit der rechten Maustaste auf das Modul, das Sie mit Ihrer nativen Bibliothek verknüpfen möchten, z. B. das app-Modul, und wählen Sie im Menü C++ Project with Gradle (C++-Projekt mit Gradle verknüpfen) aus. Es sollte ein Dialogfeld wie in Abbildung 4 angezeigt werden.
  3. Wählen Sie im Drop-down-Menü entweder CMake oder ndk-build aus.
    1. Wenn Sie CMake auswählen, verwenden Sie das Feld neben Projektpfad, um die Skriptdatei CMakeLists.txt für Ihr externes CMake-Projekt anzugeben.
    2. Wenn Sie ndk-build auswählen, verwenden Sie das Feld neben Project Path (Projektpfad), um die Skriptdatei Android.mk für Ihr externes ndk-build-Projekt anzugeben. Android Studio enthält auch die Datei Application.mk, wenn sie sich im selben Verzeichnis wie die Datei Android.mk befindet.

    Abbildung 4: Ein externes C++-Projekt über das Dialogfeld „Android Studio“ verknüpfen.

  4. Klicke auf OK.

Gradle manuell konfigurieren

Wenn Sie Gradle manuell für die Verknüpfung mit Ihrer nativen Bibliothek konfigurieren möchten, müssen Sie der Datei build.gradle auf Modulebene den Block externalNativeBuild hinzufügen und entweder mit dem Block cmake oder ndkBuild konfigurieren:

Groovig

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

Hinweis:Wenn Sie Gradle mit einem vorhandenen ndk-build-Projekt verknüpfen möchten, verwenden Sie den Block ndkBuild anstelle des Blocks cmake und geben Sie einen relativen Pfad zu Ihrer Datei Android.mk an. Gradle enthält auch die Datei Application.mk, wenn sie sich im selben Verzeichnis wie die Datei Android.mk befindet.

Optionale Konfigurationen angeben

Sie können optionale Argumente und Flags für CMake oder ndk-build angeben. Dazu konfigurieren Sie einen weiteren externalNativeBuild-Block im Block defaultConfig Ihrer build.gradle-Datei auf Modulebene. Ähnlich wie bei anderen Eigenschaften im defaultConfig-Block können Sie diese Eigenschaften für jede Produktvariante in Ihrer Build-Konfiguration überschreiben.

Wenn in Ihrem CMake- oder ndk-build-Projekt beispielsweise mehrere native Bibliotheken und ausführbare Dateien definiert sind, können Sie mit der Property targets nur einen Teil dieser Artefakte für eine bestimmte Produktvariante erstellen und verpacken. Im folgenden Codebeispiel werden einige der Attribute beschrieben, die konfiguriert werden können:

Groovig

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

Weitere Informationen zum Konfigurieren von Produktvarianten und Build-Varianten finden Sie unter Build-Varianten konfigurieren. Eine Liste der Variablen, die Sie für CMake mit dem Attribut arguments konfigurieren können, finden Sie unter CMake-Variablen verwenden.

Vordefinierte native Bibliotheken einschließen

Wenn Gradle vordefinierte native Bibliotheken verpacken soll, die in keinem externen nativen Build verwendet werden, fügen Sie sie dem Verzeichnis src/main/jniLibs/ABI Ihres Moduls hinzu.

Es sind Versionen des Android-Gradle-Plug-ins vor 4.0 erforderlich, einschließlich CMake-IMPORTED-Ziele in Ihrem jniLibs-Verzeichnis, damit sie in Ihre Anwendung aufgenommen werden. Wenn Sie von einer früheren Version des Plug-ins migrieren, kann ein Fehler wie der folgende auftreten:

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

Wenn Sie das Android Gradle-Plug-in 4.0 verwenden, verschieben Sie alle von IMPORTED-CMake-Zielen verwendeten Bibliotheken aus dem jniLibs-Verzeichnis, um diesen Fehler zu vermeiden.

ABIs angeben

Gradle erstellt Ihre native Bibliothek standardmäßig in separaten .so-Dateien für die vom NDK unterstützten Binärschnittstellen (Application Binary Interfaces, ABIs) und packt sie in Ihre App. Wenn Gradle nur bestimmte ABI-Konfigurationen Ihrer nativen Bibliotheken erstellen und verpacken soll, können Sie diese wie unten gezeigt mit dem Flag ndk.abiFilters in der Datei build.gradle auf Modulebene angeben:

Groovig

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

In den meisten Fällen musst du nur abiFilters im ndk-Block angeben, wie oben gezeigt, da Gradle dann anweist, diese Versionen deiner nativen Bibliotheken sowohl zu erstellen als auch zu verpacken. Wenn Sie jedoch steuern möchten, was Gradle erstellen soll, unabhängig davon, was in Ihrer Anwendung verpackt werden soll, konfigurieren Sie ein weiteres abiFilters-Flag im Block defaultConfig.externalNativeBuild.cmake (oder im Block defaultConfig.externalNativeBuild.ndkBuild). Gradle erstellt diese ABI-Konfigurationen, erstellt aber nur die Pakete, die Sie im defaultConfig.ndk-Block angeben.

Wir empfehlen, die App mit Android App Bundles zu veröffentlichen, um die Größe deiner App weiter zu reduzieren, da nur native Bibliotheken, die dem ABI des Geräts eines Nutzers entsprechen, mit dem Download bereitgestellt werden.

Wenn du ältere Apps mit APKs veröffentlichen möchtest, die vor August 2021 erstellt wurden, solltest du mehrere APKs basierend auf ABI konfigurieren. Statt ein großes APK mit allen Versionen deiner nativen Bibliotheken zu erstellen, erstellt Gradle ein separates APK für jedes ABI, das unterstützt werden soll, und verpackt nur die Dateien, die von ABI benötigt werden. Wenn du mehrere APKs pro ABI konfigurierst, ohne das Flag abiFilters anzugeben, wie im obigen Codebeispiel gezeigt, erstellt Gradle alle unterstützten ABI-Versionen deiner nativen Bibliotheken, aber nur Pakete, die du in der Konfiguration für mehrere APKs angegeben hast. Damit keine unerwünschten Versionen deiner nativen Bibliotheken erstellt werden, gib für das Flag abiFilters und für die Konfiguration mit mehreren APK-Dateien pro ABI dieselbe Liste von ABIs an.