들어가기 전
가상화 솔루션은 x86 환경에 안드로이드 앱을 올린다. 이 때 안드로이드 앱에서 사용한 네이티브로 작성된 라이브러리(.so)가 x86 환경에서는 실행되지 않는 경우가 있었다.
왜 이런 상황이 발생하는지, 어떻게 NDK를 사용 및 빌드해야 해결할 수 있는지를 알아보고자 이 포스팅을 작성하게 되었다.
NDK란?
네이티브 개발 키트(NDK)는 Android에서 C및 C++코드를 사용할 수 있게 해주는 일련의 도구 모음이다.
NDK의 호환 가능한 아키텍처
ARM
- armeabi-v7a : 32비트 ARM 아키텍처
- arm64-v8a : 64비트 ARM 아키텍처
x86
- x86 : 32비트 Intel 아키텍처
- x86_64 : 64비트 Intel 아키텍처
NDK는 다양한 아키텍처에 대한 지원을 제공하며, 각 아키텍처는 ABI를 통해 정의 된다.
각 ABI는 특정 명령 세트를 지원하며, 이를 통해 개발자는 다양한 기기에서 애플리케이션을 실행할 수 있다.
NDK 통한 라이브러리 빌드 방법
NDK로 C/C++ 코드를 빌드하는 방법은 크게 2가지가 있다.
Make 기반 ndk-build
ndk-build 스크립트를 활용하여 C/C++ 프로젝트를 빌드할 수 있다.
ndk-build 주요 기능
- 프로젝트 빌드 : Android 애플리케이션에 네이티브 코드 (C/C++ 코드)를 컴파일 하고 링크하여 공유 라이브러리
.so
파일을 생성한다. - 구성파일 : 빌드를 위해서
Android.mk
,Application.mk
2가지의 구성 파일이 필요하다.
Android.mk
: 소스파일, 라이브러리 및 모듈 정보 정의
Application.mk
: 빌드 설정 (ex. ABI : 어떤 ABI를 사용할 것인지, 릴리즈/디버그 모드 지정)
CMake
Gradle과 함께 작동하여 네이티브 라이브러리를 빌드할 수 있다.
Android Gradle 플러그인의 ExternalNativeBuild
를 통해서 CMake를 직접 호출하여 사용한다.
→ 다양한 플랫폼 (Android, Linux, Windows, iOS) 지원, 안드로이드 스튜디오 기본 툴인 점에서 ndk-build 보다 CMake를 더 많이 사용한다. 실제로 ndk-build
는 레거시 프로젝트로 분류된다.
NDK 사용 위한 CMake 기본 사항
1. NDK, CMake 설치

- Tools > SDK Manager > SDK Tools 클릭
- NDK (Side by side) 및 CMake 체크박스 선택 후 설치
2. CMake 빌드 스크립트 작성

- CMake 빌드 스크립트는
CMakeLists.txt
라는 이름으로 지정해야 하는 일반 텍스트 파일이다. - CMake가 C/C++ 라이브러리를 빌드하는 데 사용하는 명령어를 포함한다.
CMakeLists.txt 내용
C/C++ 라이브러리를 사용하는 방법에 따라서 내용이 달라진다.
아래 3가지의 케이스에 대해서 어떻게 컴파일 및 빌드를 진행하는지 알아보자.
[ 직접 작성한 소스코드를 라이브러리로 사용 ]
*참고사항 : security library를 만들 때 사용했던 방법
네이티브 코드 (C/C++) 파일을 작성
// 헤더 중략
#define LOG_TAG "AntiDebug"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
// 실제 사용 c언어 파일
jboolean detectDebugger() {
// 중략
}
CMakeLists.txt 내용
- 소스코드를 라이브러리로 빌드하기 위해서 아래와 같이 작성한다.
cmake_minimum_required(VERSION 3.10.2) // 필요한 최소 CMake 버전 지정
add_library(
antidebug // 생성 할 라이브러리 이름
SHARED // SHARED 라이브러리로 지정
antidebug.c) // 라이브러리로 생성 할 c 코드 파일
find_library(
log-lib // 불러올 라이브러리의 경로를 저장할 변수
log ) // 불러올 라이브러리의 이름
target_link_libraries( // 라이브러리들 연결
antidebug
${log-lib} )
add_library
- 빌드 할 라이브러리를 정의한다.
- 만든 소스코드를 빌드 할 라이브러리 이름을 지정하고, SHARED 라이브러리로 생성한다.
- 라이브러리로 생성 할 소스 파일을 추가한다.
- 따라서 antidebug.c 를 → antidebug.so 이름의 공유 라이브러리로 생성한다.
find_library
- 외부 NDK 라이브러리를 사용하기 위해 라이브러리를 찾아온다.
- 네이티브 (C/C++)코드에서 log를 사용하기 위해 log 라이브러리를 가져온다.
- 위 소스코드에서는 log 라이브러리를 가져와 해당 경로를 log-lib에 저장한다.
target_link_libraries
- 생성된 라이브러리에 다른 라이브러리를 링크한다.
- antidebug 라이브러리에 사용된 log 라이브러리를 연결하기 위해서 사용된다.
- antidebug ${log-lib} 로 작성한다. *${log-lib}는 위에서 가져온 log 라이브러리의 경로이다.
라이브러리의 ABI 지정
라이브러리로 컴파일 및 빌드하는 방법에서 abiFilters
를 통해 ABI를 지정할 수 있다.
예를 들어 64비트 ABI만 빌드하려면 abiFilters 'arm64-v8a', 'x86_64'
이렇게 지정할 수 있다.
build.gradle
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
- 위 예시는 arm64, areabi, x86, x86_64 를 모두 지원하는 라이브러리를 생성하겠다는 뜻이다.
- ndk
abiFilters
에서 원하는 ABI를 지정할 수 있다. - 이 후
externalNativeBuild
를 통해 작성한 CMakeLists를 연결하면 된다.
→ CMakeLists.txt
에 작성한 빌드 방법으로 abiFilters
에 지정한 ABI에 대해 각각 빌드를 수행한다.
- 이 프로젝트를 apk로 만들면 4개의 ABI에 지원하는 fat APK가 된다.
- fat APK는 단일 ABI용 바이너리만 포함한 APK 보다 훨씬 크다.
- 따라서 호환성의 폭은 넓지만 APK가 커진다는 단점이 있다.
- 생성된 APK 는
abiFilters
에 지정된 모든 ABI를 지원하는 기기에서 실행 가능하다. - APK가 설치될 때, 시스템은 기기의 주 ABI에 맞는 네이티브 라이브러리를 선택하여 사용한다.
[ NDK API (이미 컴파일 된 NDK 라이브러리) 사용 ]
Android NDK에서는 일련의 네이티브 API 및 라이브러리를 제공한다. 이미 컴파일 되어있기 때문에, 사용하려는 라이브러리의 이름을 CMake에 제공하고, 자체 네이티브 라이브러리와 연결하기만 하면 된다.
https://developer.android.com/ndk/guides/stable_apis?hl=ko
CMakeLists.txt 내용
예시 : 작성한 네이티브 프로젝트에서 OpenGRL ES (NDK라이브러리 중 하나)를 사용
add_library(
openGRLexample // 생성 할 라이브러리 이름
SHARED // SHARED 라이브러리로 지정
openGRLexample.c) // 라이브러리로 생성 할 작성한 c 코드 파일
find_library(
GLESv2-lib // GLES 라이브러리 경로를 해당 변수에 저장한다.
GLESv2 // GLES 라이브러리를 가져온다.
)
target_link_libraries( //연결 할 라이브러리 작성
openGRLexample // 생성 할 나의 라이브러리
${GLESv2-lib} // GLES 라이브러리 연결
)
find_library
- 외부 NDK 라이브러리를 사용하기 위해 라이브러리를 찾아온다.
- 네이티브 (C/C++)코드에서 GLESv2를 사용하기 위해 GLESv2라이브러리를 가져온다.
- 위 소스코드에서는 GLESv2 라이브러리를 가져와 해당 경로를 GLESv2-lib에 저장한다.
target_link_libraries
- 생성된 라이브러리에 다른 라이브러리를 링크한다.
- openGRLexample (예시) 라이브러리에 사용된 GLESv2 라이브러리를 연결하기 위해서 사용된다.
이미 컴파일된 시스템 라이브러리이므로 add_library
에서는 GLESv2를 넣지 않아도 된다.
[ 이미 빌드된 라이브러리 사용 ]
이미 미리 컴파일된 서드파티 라이브러리를 추가하는 방법이다.
- 해당 서드파티 라이브러리가 빌드되었을 때 지정한 ABI에 대해서만 지원한다.
→ 즉 서드파티 라이브러리가 x86 ABI를 지정하여 빌드했다면, 해당 ABI에서만 실행 가능하다.
CMakeLists.txt 내용
add_library(example-lib SHARED **IMPORTED**)
set_target_properties(example-lib PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libexample.so)
- 라이브러리 파일
app/src/main/jniLibs/[ABI]/
디렉토리에 위치 - libexample.so 파일을 프로젝트에 추가하기 위해 CMakeLists.txt를 작성해야 한다.
add_library
- 추가하고자 하는 라이브러리의 이름을 작성한다.
- example-lib SHARED
- 이미 빌드된 서드파티 라이브러리 이므로
IMPORTED
를 지정한다.
set_target_properties
- 추가하고자 하는 라이브러리의 이름과 .so 파일의 위치를 작성한다.
- ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libexample.so
- 해당 프로젝트를 실행하면
ANDROID_ABI
에 빌드 당시에 지정한 ABI가 들어가는 곳이다. - 실행시키는 환경에서
ANDROID_ABI
디렉토리에 파일이 없을 경우 빌드되지 않는다. - 서드파티 라이브러리를 안드로이드에서 사용하는 방법 또한 JNI를 통해서 사용하면 된다.
- Java/Kotlin에서 네이티브 메서드 선언한 후 일반 메소드 처럼 호출하면 된다.
참고 레퍼런스
CMake | Android NDK | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. CMake 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android NDK는 CMake를 사용하여 애플리케이션의 C 및 C
developer.android.com
Android ABI | Android NDK | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Android ABI 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 다양한 Android 기기는 각기 다른 CPU를 사용하
developer.android.com
Android ABI | Android NDK | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Android ABI 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 다양한 Android 기기는 각기 다른 CPU를 사용하
developer.android.com
'Android > 공부' 카테고리의 다른 글
[Android/공부] Compose로 UI 적용기 -1 (0) | 2024.10.14 |
---|---|
[Android] MockWebServer란? : okhttp mockwebserver (0) | 2024.09.25 |
[Android] MockWebServer로 Mock API 활용하기 (2) | 2024.09.25 |
[Android] XML 과 Compose 렌더링 방식의 차이 (0) | 2024.09.23 |
[Android/공부] 안드로이드 라이브러리 / AAR 만들기 (0) | 2024.07.17 |