Collegare Gradle alla libreria nativa

Per includere il tuo progetto di libreria nativa come dipendenza di build Gradle, devi fornire a Gradle il percorso al file di script CMake o ndk-build. Quando crei la tua app, Gradle esegue CMake o ndk-build e pacchettizza le librerie condivise con la tua app. Gradle utilizza anche lo script di build per sapere quali file trasferire nel tuo progetto Android Studio, in modo che tu possa accedervi dalla finestra Progetto. Se non disponi di uno script di build per le origini native, devi creare uno script di build CMake prima di procedere.

Ogni modulo nel tuo progetto Android può essere collegato a un solo file di script CMake o ndk-build. Ad esempio, se vuoi creare e pacchettizzare output da più progetti CMake, devi utilizzare un file CMakeLists.txt come script di build CMake di primo livello (a cui dovrai poi collegare Gradle) e aggiungere altri progetti CMake come dipendenze dello script di build. Allo stesso modo, se utilizzi ndk-build, puoi includere altri makefile nel file di script Android.mk di primo livello.

Dopo aver collegato Gradle a un progetto nativo, Android Studio aggiorna il riquadro Progetto per mostrare i file di origine e le librerie native nel gruppo cpp, mentre gli script di build esterni nel gruppo File di build esterni.

Nota: quando apporti modifiche alla configurazione di Gradle, assicurati di applicare le modifiche facendo clic su Sincronizza progetto nella barra degli strumenti. Inoltre, quando apporti modifiche al file di script CMake o ndk-build dopo averlo già collegato a Gradle, devi sincronizzare Android Studio con le modifiche selezionando Crea > Aggiorna progetti C++ collegati dalla barra dei menu.

Puoi collegare Gradle a un progetto CMake o ndk-build esterno utilizzando l'interfaccia utente di Android Studio:

  1. Apri il riquadro Progetto sul lato sinistro dell'IDE e seleziona la visualizzazione Android.
  2. Fai clic con il pulsante destro del mouse sul modulo che vuoi collegare alla tua libreria nativa, ad esempio il modulo app, e seleziona Collega progetto C++ con Gradle dal menu. Dovresti visualizzare una finestra di dialogo simile a quella mostrata nella Figura 4.
  3. Dal menu a discesa, seleziona CMake o ndk-build.
    1. Se selezioni CMake, utilizza il campo accanto a Percorso progetto per specificare il file di script CMakeLists.txt per il progetto CMake esterno.
    2. Se selezioni ndk-build, utilizza il campo accanto a Percorso progetto per specificare il file di script Android.mk per il progetto ndk-build esterno. Android Studio include anche il file Application.mk se si trova nella stessa directory del file Android.mk.

    Figura 4. Collegamento di un progetto C++ esterno utilizzando la finestra di dialogo di Android Studio.

  4. Fai clic su OK.

Configurare manualmente Gradle

Per configurare manualmente Gradle in modo che si colleghi alla tua libreria nativa, devi aggiungere il blocco externalNativeBuild al file build.gradle a livello di modulo e configurarlo con il blocco cmake o ndkBuild:

Trendy

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

Nota: se vuoi collegare Gradle a un progetto ndk-build esistente, utilizza il blocco ndkBuild anziché il blocco cmake e fornisci un percorso relativo al file Android.mk. Gradle include anche il file Application.mk se si trova nella stessa directory del file Android.mk.

Specifica configurazioni facoltative

Puoi specificare argomenti e flag facoltativi per CMake o ndk-build configurando un altro blocco externalNativeBuild all'interno del blocco defaultConfig del file build.gradle a livello di modulo. Analogamente ad altre proprietà nel blocco defaultConfig, puoi eseguire l'override di queste proprietà per ogni versione di prodotto nella configurazione della build.

Ad esempio, se il tuo progetto CMake o ndk-build definisce più librerie native ed eseguibili, puoi utilizzare la proprietà targets per creare e pacchettizzare solo un sottoinsieme di questi artefatti per una determinata versione di prodotto. Il seguente esempio di codice descrive alcune delle proprietà che puoi configurare:

Trendy

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

Per scoprire di più sulla configurazione delle versioni di prodotto e delle varianti di build, consulta Configurare le varianti della build. Per un elenco delle variabili configurabili per CMake con la proprietà arguments, consulta Utilizzo di variabili CMake.

Includi librerie native predefinite

Se vuoi che Gradle pacchettizzino librerie native predefinite che non sono utilizzate in nessuna creazione nativa esterna, aggiungile alla directory src/main/jniLibs/ABI del tuo modulo.

Le versioni del plug-in Android per Gradle precedenti alla 4.0 devono includere i target CMake IMPORTED nella directory jniLibs affinché vengano inclusi nella tua app. Se esegui la migrazione da una versione precedente del plug-in, potrebbe riscontrare un errore come il seguente:

* 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 usi il plug-in Android per Gradle 4.0, sposta tutte le librerie utilizzate da IMPORTED CMake target fuori dalla directory jniLibs per evitare questo errore.

Specifica ABI

Per impostazione predefinita, Gradle crea la tua libreria nativa in file .so separati per le Application Binary Interfaces (ABI) supportate da NDK e li pacchettizza tutti nella tua app. Se vuoi che Gradle crei e pacchettizzi solo alcune configurazioni ABI delle tue librerie native, puoi specificarle con il flag ndk.abiFilters nel file build.gradle a livello di modulo, come mostrato di seguito:

Trendy

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

Nella maggior parte dei casi, devi solo specificare abiFilters nel blocco ndk, come mostrato sopra, perché indica a Gradle di creare e pacchettizzare queste versioni delle tue librerie native. Tuttavia, se vuoi controllare cosa deve creare Gradle, indipendentemente da ciò che vuoi pacchettizzare nella tua app, configura un altro flag abiFilters nel blocco defaultConfig.externalNativeBuild.cmake (o nel blocco defaultConfig.externalNativeBuild.ndkBuild). Gradle crea queste configurazioni ABI, ma pacchettizza solo quelle specificate nel blocco defaultConfig.ndk.

Ti consigliamo di pubblicare l'app utilizzando Android App Bundle per ridurre ulteriormente le dimensioni dell'app, poiché con il download verranno caricate solo le librerie native corrispondenti all'ABI del dispositivo di un utente.

Per la pubblicazione di app legacy tramite APK (creati prima di agosto 2021), valuta la possibilità di configurare più APK basati su ABI. Anziché creare un unico APK di grandi dimensioni con tutte le versioni delle librerie native, Gradle crea un APK separato per ogni ABI che vuoi supportare e pacchettizza solo i file necessari per ciascuna ABI. Se configuri più APK per ABI senza specificare il flag abiFilters come mostrato nell'esempio di codice riportato sopra, Gradle crea tutte le versioni ABI supportate delle tue librerie native, ma pacchettizza solo quelle specificate nella configurazione con più APK. Per evitare di creare versioni indesiderate delle tue librerie native, fornisci lo stesso elenco di ABI sia per il flag abiFilters sia per la configurazione di più APK per ABI.