메모형식으로 작성한 글입니다..
안드로이드 스튜디오에서 NDK로 C++11과 STL을 사용한 소스를 빌드하는 법.
C++가 아닌 C를 사용할 때는 makefile과 소스코드에서 약간의 차이가 있다. (본문 내에서 설명)
테스트 환경
Android Studio : 2.2.2
Gradle : 2.2.2
buildToolsVersion : 24.0.0
테스트 소스 코드
https://github.com/tibyte/practice-unclassified/tree/master/Android/NDKTest3
NDK 설치
File - Settings - Appearance & Behavior - System Settings - Android SDK
에서 SDK Tools로 들어가면 NDK를 설치할 수 있다.
javah 추가
File - Settings - Tools - External Tools에서 추가를 눌러 javah를 추가한다.
하단의 Tool settings에 들어갈 내용은 아래와 같다.
Program : jdk에 포함되어 있는 javah의 경로
Parameters : -v -jni -d $ModuleFileDir$/src/main/jni $FileClass$
Working directory : $SourcepathEntry$
java코드에서 네이티브 함수 사용
System.loadLibrary()로 사용할 라이브러리를 로드한다. 라이브러리 이름은 임의로 정해도 되는데, 이 글에서는 main.cpp를 만들 것이므로 여기에서는 main으로 정한다.
그리고 사용할 네이티브 함수명을 native키워드를 통해 선언한다.
package kr.tibyte.ndktest3; } |
JNI용 헤더파일 생성
프로젝트 탭에서 보기 설정을 Project로 한다.
네이티브 함수를 사용한 Java파일을 우클릭하여 External Tools - javah를 클릭한다.
그러면 src/main/jni 경로에 프로젝트_경로.h파일이 생성된다.
C++ 소스코드 작성
JNI디렉토리 내에 cpp파일을 만들고 소스코드를 작성하는데,
함수 이름에 패키지 경로가 들어있어야 한다. 이 부분이 실제 패키지 경로와 다르면 빌드가 되지 않는다. 아래 예제에서는 테스트 목적으로 STL과 C++11 기능을 사용했다.
#include <jni.h> #include <vector> #include <string> #include "kr_tibyte_ndktest3_MainActivity.h" using namespace std; extern "C" { JNIEXPORT jstring JNICALL Java_kr_tibyte_ndktest3_MainActivity_getNativeText(JNIEnv *env, jobject obj) { string str = ""; vector<char> vec; vec.push_back('a'); vec.push_back('b'); vec.push_back('c'); for(auto& x : vec) { x ^= 32; str += x; } return env->NewStringUTF(str.c_str()); } } |
이 부분에서 C와 C++의 차이는 다음과 같다.
- C++에서 extern으로 네임 맹글링을 어떤 규칙으로 할 것인지 지정(참고링크)
Makefile 설정
jni디렉토리 안에 Android.mk와 Application.mk 파일을 생성하고 각각 아래와 같은 내용을 적는다.
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_DEFAULT_CPP_EXTENSION := cpp LOCAL_MODULE := main LOCAL_SRC_FILES := main.cpp LOCAL_LDLIBS := -llog -latomic LOCAL_C_INCLUDES := $(LOCAL_PATH)/include-all include $(BUILD_STATIC_LIBRARY) |
C로 빌드할 때에는 LOCAL_DEFAULT_CPP_EXTENSION := cpp가 필요하지 않다.
LOCAL_MODULE과 LOCAL_SRC_FILES는 작성한 C/C++네이티브 소스파일과 관련된 내용을 적는다.
Application.mk
NDK_TOOLCHAIN_VERSION := 4.9 APP_STL := gnustl_static APP_MODULED := main APP_ABI := armeabi-v7a APP_CPPFLAGS += -std=c++11 LOCAL_C_INCLUDES += ${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.9/include |
NDK_TOOLCHAIN_VERSION : NDK설치경로/sources/cxx-stl/gnu-libstdc++ 에 있는 툴체인 버전을 적는다.
APP_STL : 정적 라이브러리를 사용하고 싶을 경우 gnustl_static를, 공유라이브러리를 쓸 대는 gnustl_shared를 적을 수 있다.
APP_ABI : 빌드할 대상 아키텍쳐를 적는다. all로 적으면 모든 대상으로 빌드할 수 있다.
APP_CPPFLAGS : C++11로 빌드하려면 -std=c++11 플래그를 넣는다.
LOCAL_C_INCLUDES : 자신이 가진 버전에 맞는 경로를 적는다. (NDK설치경로 확인)
build.gradle ndk 정보 추가
src디렉토리 내에 있는 defaultConfig안에 다음과 같은 내용을 추가한다.
ndk { stl "gnustl_static" moduleName "main" } |
stl에서 gnustl_static은 Application.mk에서 설정했던 것과 같은 것이고,
moduleName은 Android.mk, Application.mk, java파일에서 썼던 것과 같다.
만약 STL을 사용하지 않을 것이면 stl은 작성하지 않아도 된다..
실행화면
STL vector와 C++11의 auto를 사용한 코드가 정상적으로 빌드된다.