우선,
https://codelabs.developers.google.com/codelabs/android-studio-jni
여기에 속아선 안된다!
아직 Android Studio에서는 공식적으로 지원하지 않는 것으로 생각하는 게 정신건강에 좋다. 오로지 android ndk를 위해서만 작성한 코드만 있다면 상관없지만, 3rd party 라이브러리같은 걸 Android Studio에서 소스 컴파일 해서 사용하는 것은 불가능하다.
gradle 파일에 task를 추가하는 식으로 사용하는 것이 가장 효과적이다.
이 포스팅에서는 다음과 같은 방식을 추천한다.
디렉토리 구조
my_project/ my_module/ jni/ libs/ src/main/java/
Gradle 파일
my_module/build.gradle 에 아래 내용을 추가해서 jni 코드가 있음을 Android Studio가 알아채지 못하도록 하자
android { sourceSets { main { jni.srcDirs = [] jniLibs.srcDirs 'libs' } } }
(참고로 기본적으로 Android Studio가 인식하는 경로는 my_project/my_module/src/main/jni 이다)
jniLibs는 .so 파일(소스코드가 아닌)이 있는 위치로 지정한 것이다. 소스코드는 Android Studio가 빌드할 필요가 없지만 .so 파일들은 패키지에 포함되어야하므로 경로를 지정해주어야한다.
이제 my_project/my_module/jni/ 의 코드들을 my_project/my_module/libs/ 로 빌드하는 task를 생성해야한다. (출처)
task ndkBuild(type: Exec) { workingDir file(‘./‘) commandLine getNdkBuildCmd() } tasks.withType(JavaCompile) { compileTask -> compileTask.dependOn ndkBuild } task cleanNative(type: Exec) { workingDir file(‘./‘) commandLine getNdkBuildCmd(), 'clean' } clean.dependsOn cleanNative
마지막 줄에 clean task의 dependsOn을 지정하는 부분 유의
ndk-build 의 위치를 가져오는 함수를 선언해준다.
def getNdkDir() { if (System.env.ANDROID_NDK_ROOT != null) return System.env.ANDROID_NDK_ROOT Properties properties = new Properties() properties.load(project.rootProject.file(‘local.properties’).newDataInputStream()) def ndkdir = properties.getProperty(‘ndk.dir’, null) if (ndkdir == null) throw new GradleException(“NDK location not found. Define location with ndk.dir int the local.properties file or with an ANDROID_NDK_ROOT environment variable.”) return ndkdir } def getNdkBuildCmd() { def ndkbuild = getNdkDir() + "/ndk-build" if (Os.isFamily(Os.FAMILY_WINDOWS)) ndkbuild += ".cmd" return ndkbuild }
위 내용들을 모두 적용한 모습은 아래와 같다.
import org.apache.tools.ant.taskdefs.condition.Os android { sourceSets { main { jni.srcDirs = [] jniLibs.srcDirs 'libs' } } task ndkBuild(type: Exec) { workingDir file(‘./‘) commandLine getNdkBuildCmd() } tasks.withType(JavaCompile) { compileTask -> compileTask.dependOn ndkBuild } task cleanNative(type: Exec) { workingDir file(‘./‘) commandLine getNdkBuildCmd(), 'clean' } clean.dependsOn cleanNative } def getNdkDir() { if (System.env.ANDROID_NDK_ROOT != null) return System.env.ANDROID_NDK_ROOT Properties properties = new Properties() properties.load(project.rootProject.file(‘local.properties’).newDataInputStream()) def ndkdir = properties.getProperty(‘ndk.dir’, null) if (ndkdir == null) throw new GradleException(“NDK location not found. Define location with ndk.dir int the local.properties file or with an ANDROID_NDK_ROOT environment variable.”) return ndkdir } def getNdkBuildCmd() { def ndkbuild = getNdkDir() + "/ndk-build" if (Os.isFamily(Os.FAMILY_WINDOWS)) ndkbuild += “.cmd" return ndkbuild }
(맨 윗줄 import 부분 유의)