Android Studio에서 JNI 빌드하기

Reading time ~1 minute

우선,

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 부분 유의)

kiwi campus

가족여행차 샌 프란시스코에 갔다가 오랜만에 버클리 교정을 구경하러 갔다.돌아다니다보니 Sather gate 근처의 분수대 주변에 이런 게 돌아다니고 있었다.처음엔 학생이 만든 장난감 정도로 생각했다. 폰에 외장(?)렌즈를 달아서 꽂아놨는데 이게 ...… Continue reading