네이티브 라이브러리에 Gradle 연결

네이티브 라이브러리 프로젝트를 Gradle 빌드 종속 항목으로 포함하려면 Gradle에 CMake 또는 ndk-build 스크립트 파일의 경로를 제공해야 합니다. 앱을 빌드하면 Gradle은 CMake 또는 ndk-build를 실행하고 공유 라이브러리를 APK로 패키징합니다. 또한, Gradle은 빌드 스크립트를 사용하여 Android 스튜디오 프로젝트로 가져올 파일을 파악하므로 Project 창에서 이러한 파일에 액세스할 수 있습니다. 네이티브 소스용 빌드 스크립트가 없다면 계속하기 전에 CMake 빌드 스크립트를 생성해야 합니다.

Android 프로젝트의 각 모듈은 하나의 CMake 또는 ndk-build 스크립트 파일에만 연결할 수 있습니다. 따라서, 예를 들어 여러 CMake 프로젝트의 출력을 빌드하고 패키징하려면 하나의 CMakeLists.txt 파일을 최상위 CMake 빌드 스크립트로 사용하고(그런 다음 Gradle을 연결) 빌드 스크립트의 종속성으로 다른 CMake 프로젝트를 추가해야 합니다. 마찬가지로, ndk-build를 사용하는 경우 최상위 Android.mk 스크립트 파일에 다른 Makefile을 포함하면 됩니다.

Gradle을 네이티브 프로젝트에 연결하면 Android 스튜디오는 Project 창을 업데이트하여 cpp 그룹에 소스 파일과 네이티브 라이브러리를 표시하고 External Build Files 그룹에는 외부 빌드 스크립트를 표시합니다.

참고: Gradle 구성을 변경할 때에는 툴바에서 Sync Project 를 클릭하여 변경사항을 적용해야 합니다. 또한, CMake 또는 ndk-build 스크립트 파일을 이미 Gradle에 연결한 후 이 스크립트 파일을 변경한다면 메뉴 바에서 Build > Refresh Linked C++ Projects를 선택하여 Android 스튜디오를 변경사항과 동기화해야 합니다.

Android 스튜디오 UI를 사용하여 Gradle을 외부 CMake 또는 ndk-build 프로젝트에 연결할 수 있습니다.

  1. IDE 왼쪽에서 Project 창을 열고 Android 뷰를 선택합니다.
  2. 모듈과 같이 네이티브 라이브러리에 연결할 모듈을 마우스 오른쪽 버튼으로 클릭한 후 메뉴에서 Link C++ Project with Gradle을 선택합니다. 그림 4와 비슷한 대화상자가 표시됩니다.
  3. 드롭다운 메뉴에서 CMake 또는 ndk-build를 선택합니다.
    1. CMake를 선택한 경우 Project Path 옆에 있는 필드를 사용하여 외부 CMake 프로젝트에 사용할 CMakeLists.txt 스크립트 파일을 지정합니다.
    2. ndk-build를 선택한 경우 Project Path 옆에 있는 필드를 사용하여 외부 ndk-build 프로젝트에 사용할 Android.mk 스크립트 파일을 지정합니다. Application.mk 파일이 Android.mk 파일과 같은 디렉터리에 있다면 이 파일도 Android 스튜디오에 포함됩니다.

    그림 4. Android 스튜디오 대화상자를 이용하여 외부 C++ 프로젝트 연결

  4. OK를 클릭합니다.

수동으로 Gradle 구성

수동으로 Gradle을 구성하여 네이티브 라이브러리에 연결하려면 다음과 같이 모듈 레벨 build.gradle 파일에 externalNativeBuild 블록을 추가하고 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"
        }
      }
    }
    

참고: Gradle을 기존 ndk-build 프로젝트에 연결하려면 cmake 블록 대신 ndkBuild 블록을 사용하고 Android.mk 파일의 상대 경로를 제공합니다. Application.mk 파일이 Android.mk 파일과 같은 디렉터리에 있다면 이 파일도 Gradle에 포함됩니다.

선택적 구성 지정

모듈 레벨 build.gradle 파일의 defaultConfig 블록 내에 다른 externalNativeBuild 블록을 구성하여 CMake 또는 ndk-build의 선택적 인수 및 플래그를 지정할 수 있습니다. 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 APK.
              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 APK 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 {...}
      }
    }
    

제품 버전 및 빌드 변형을 구성하는 방법에 관한 자세한 내용은 빌드 변형 구성을 참조하세요. arguments 속성을 사용하여 CMake를 구성할 수 있는 변수 목록은 CMake 변수 사용을 참조하세요.

미리 빌드된 네이티브 라이브러리 포함

Gradle에서 미리 빌드된 네이티브 라이브러리를 APK로 패키징하려면 아래와 같이 기본 소스 세트 구성을 수정하여 미리 빌드된 .so 파일의 디렉터리를 포함합니다. Gradle에 연결하는 CMake 빌드 스크립트의 아티팩트를 포함하기 위해 이 작업을 할 필요는 없습니다.

    android {
        ...
        sourceSets {
            main {
                jniLibs.srcDirs 'imported-lib/src/', 'more-imported-libs/src/'
            }
        }
    }
    

ABI 지정

기본적으로 Gradle은 NDK가 지원하는 Application Binary Interface(ABI)의 별도 .so 파일로 네이티브 라이브러리를 빌드하고 이를 모두 APK로 패키징합니다. Gradle에서 네이티브 라이브러리의 특정 ABI 구성만 빌드하고 패키징하려면 아래와 같이 모듈 레벨 build.gradle 파일에서 ndk.abiFilters 플래그를 사용하여 이를 지정하면 됩니다.

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

대부분의 경우 위와 같이 ndk 블록에 abiFilters를 지정하기만 하면 됩니다. 왜냐하면 이를 통해 Gradle은 이 버전의 네이티브 라이브러리를 빌드하고 패키징하기 때문입니다. 하지만, APK로 패키징하려는 항목과 별개로 Gradle이 빌드해야 하는 항목을 제어하려면 defaultConfig.externalNativeBuild.cmake 블록(또는 defaultConfig.externalNativeBuild.ndkBuild 블록)에 다른 abiFilters 플래그를 구성합니다. Gradle은 이러한 ABI 구성을 빌드하지만 defaultConfig.ndk 블록에 지정한 구성만 패키징합니다.

APK 크기를 더 줄이려면 모든 버전의 네이티브 라이브러리를 포함하는 하나의 큰 APK를 만드는 대신 ABI를 기반으로 여러 개의 APK를 구성하는 것이 좋습니다. Gradle은 지원할 각 ABI에 하나씩 APK를 만들고 각 ABI에 필요한 파일만 패키징합니다. 위의 코드 샘플에서 보는 바와 같이 abiFilters 플래그를 지정하지 않고 ABI별로 여러 APK를 구성하면 Gradle은 지원되는 모든 ABI 버전의 네이티브 라이브러리를 빌드하고 여러 APK 구성에서 지정한 버전만 패키징합니다. 원하지 않는 버전의 네이티브 라이브러리를 빌드하지 않으려면 abiFilters 플래그와 ABI별 여러 APK 구성 모두에 동일한 ABI 목록을 제공해야 합니다.