출처: http://blog.naver.com/chc07ktm/220198738523
http://i5on9i.blogspot.kr/2015/02/android-studio-ndk-hello-world.html
http://mydevspace.blogspot.kr/2015/03/110-ndk.html
eclipse에서 Android studio로 옮긴뒤, ndk를 빌드해야할 일이 생겼습니다.
(역어샘블링을 힘들게 하기위해서...)
기본 안드로이 개발자 사이트에는 Comming soon으로 되어 있어 불가능한줄 알았지만,
구글선생님 덕분에 아주 쉽게 셋팅했습니다.
설명은 ndk에 기본 샘플인 hello-jni를 기준으로 하겠습니다.
먼저 android studio에서 hello-jni프로젝트를 오픈합니다.
소스를 담기위한 기본 폴더를 위와 같이생성해줍니다.
- src/main 폴더 밑에 jni폴더와 libs폴더를 만들어 줍니다.
local.properties파일에 다음과 같이 ndk경로를 추가해 줍니다.
ndk.dir=/Users/youngchilcho/Documents/Development/android/ndk/android-ndk-r10c
마지막으로 build.gradle파일내에 jni컴파일을 위한 작업을 추가합니다.
------------------------------------------------------------------------------------------------------------------------------------
ndk {
moduleName "hello-jni"
}
sourceSets.main{
jni.srcDirs = []
jniLibs.srcDir 'src/main/libs'
}
task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
def ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder()
commandLine "$ndkDir/ndk-build",
'-C', file('src/main/jni').absolutePath,
'-j', Runtime.runtime.availableProcessors(),
'all',
'NDK_DEBUG=1'
}
task cleanNative(type: Exec, description: 'Clean JNI object files') {
def ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder()
commandLine "$ndkDir/ndk-build",
'-C', file('src/main/jni').absolutePath,
'clean'
}
clean.dependsOn 'cleanNative'
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildNative
}
------------------------------------------------------------------------------------------------------------------------------------
혹시나 소스가 필요한 분들을위해 컴파일 가능한 (ndk 경로는 사용자의 설정에 맞게 셋팅하세요 :) 소스를 첨부합니다.
참고로 샘플에 사용된 ndk 경로는 아래와 같습니다.
[출처] [MAC] - Android Studio에서 NDK 빌드하기 :) |작성자 게으른 곰
Anroid studio 에서 NDK 사용하기
Android studio 에서 NDK 를 사용해 보자. 이 글은 아래 동영상의 내용보면서 작성한 글이다.
class 에 jni definition 넣고 rebuild
class 에 jni 정의 를 넣어주고, project 를 rebuild 한다.(이 때 생긴 class 를 이용해서 javah 를 실행하게 된다.)
javah 실행
rebuild project 를 해준다.
D:\mine\programming\androidStudio\AndroidSamples\NdkHelloWorld\app\src\main>"c:\Program Files\Java\jdk1.7.0_25\bin\javah.exe" -d jni -classpath "D:\Program Files\Android\sdk\platforms\android-19\a
ndroid.jar";"d:\Program Files\Android\sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar";"d:\Program Files\Android\sdk\extras\android\support\v7\appcompat\libs\android-
support-v4.jar";..\..\build\intermediates\classes\debug com.namh.ndkhelloworld.MainActivity
그러면 jni folder 가 생성된다.
.c 만들기
이 header 파일중에 우리는 MainActivity.h 에 jni 함수정의를 넣어놨으니, *_MainActivity.h 를 확인하면 함수정의를 찾을 수 있다. 이 함수정의를 이용해서 이제 .c 를 만들자.
// com_namh_ndkhelloworld_MainActivity.h /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_namh_ndkhelloworld_MainActivity */ #ifndef _Included_com_namh_ndkhelloworld_MainActivity #define _Included_com_namh_ndkhelloworld_MainActivity #ifdef __cplusplus extern "C" { #endif #undef com_namh_ndkhelloworld_MainActivity_MODE_PRIVATE #define com_namh_ndkhelloworld_MainActivity_MODE_PRIVATE 0L #undef com_namh_ndkhelloworld_MainActivity_MODE_WORLD_READABLE #define com_namh_ndkhelloworld_MainActivity_MODE_WORLD_READABLE 1L ... ... ... /* * Class: com_namh_ndkhelloworld_MainActivity * Method: stringFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_namh_ndkhelloworld_MainActivity_stringFromJNI (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
아래처럼 .c 를 만들어 줬다.
// hello.c #include "com_namh_ndkhelloworld_MainActivity.h" JNIEXPORT jstring JNICALL Java_com_namh_ndkhelloworld_MainActivity_stringFromJNI (JNIEnv * env, jobject obj) { return (*env)->NewStringUTF(env, "hello jni !"); }
No rule to make target
.c 가 한개밖에 없을 때 "No rule to make target" 를 내뿝는다. 이게 windows ndk bug 라고 하니, 빈 .c 파일을 하나 만들어서 추가하자.
local.properties 에 ndk.dir 추가
이제 ndk 경로를 추가해 주자. <project_path>/local.properties 에 ndk.dir 을 추가해 주면 된다. 단, 주의할 것은 공백(space) 가 있는 경로는 안된다.[ref. 2]
sdk.dir=D\:\\Program Files\\Android\\sdk
ndk.dir=D\:\\Android\\ndk\\android-ndk-r10d
library 를 application 이 시작할 때 load 하기 위해서 activity class 에
static {
System.loadLibrary("hello-jni");
}
를 넣어주자. 이 때 "hello-jni" 는 library 의 이름이 되는데, 이녀석은 app/build.gradle 에 정의해 줘야 한다.
apply plugin: 'com.android.application' android { ... defaultConfig { applicationId "com.namh.ndkhelloworld" minSdkVersion 16 targetSdkVersion 21 versionCode 1 versionName "1.0" ndk{ moduleName "hello-jni" } } ... }
제대로 compile 이 되면 아래 그림처럼 .so 들이 만들어진다.
이제 Make project 를 다시 실행하면 된다.
이제 실제 동작하는지 activity 에서 jni 함수를 넣은 code 를 작성해서 한 번 실행 해 보면 된다.
Custom Android.mk 사용하기
jni.srcDirs=[]
ref.4 를 보면 gradle Android plugin 에서 convention 으로 잡혀있는 path 를 바꿀 수 있는데, jni 도 그렇게 바꿀 수 있다.
Build project 를 실행할 때 jni 가 folder 가 있으면 자동으로 NdkCompile task(code : NdkCompile.groovy) 를 실행되게 되는데,[ref. 2]
- jni.srcDirs = []
를 하면 jni 가 없다는 뜻이 되고, jni 에 대한 compile(NdkCompile) 을 시도하지 않을 것이다.
그러면 원래 NdkCompile 이 ndk-build 를 수행하면서 만드는 아래의 파일들을 만들지 않게 된다.
- <project_path>\app\build\intermediates\ndk\debug
- 자동으로 만들어지는 파일
- Android.mk
- lib/<eabi_folder>/libhello-jni.so
android{
sourceSets.main {
jniLibs.srcDir ' src/main/libs'
jni.srcDirs = [] //disable automatic ndk-build call with auto-generated Android.mk
}
tasks.withType(JavaCompile)
tasks.withType(JavaCompile) 을 추가해 주자. 이 부분에서 compile 시에 ndkLibsToJar 이 호출되도록 dependency 를 추가해 주게 된다.
참고로 이전에는 tasks.withType(Compile) 도 됐는데, 현재는 안된다. ref. 5를 참고하자.
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkLibsToJar
}
ndkLibsToJar
이제 ndkLibsToJar task 가 필요하다. ndkLibsToJar 은 ref. 2에서 가져왔다. 여기에 보면 dependsOn 에 'ndkBuild' 가 있다. 이제 ndkBuild task 를 만들자.
task ndkLibsToJar(type: Zip, dependsOn: 'ndkBuild', description: 'Create a JAR of the native libs') { destinationDir new File(buildDir, 'libs') baseName 'ndk-libs' extension 'jar' from(new File(buildDir, 'libs')) { include '**/*.so' } into 'lib/' }
ndkBuild
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') { println('executing ndkBuild') commandLine "echo", "hello world" }
ndkBuild 가 잘 동작하는지 확인하는 차원에서 ref. 2 에서 처럼 hello world 를 한 번 찍어보자. 이부분을 나중에는 아래처럼 수정할 것이다.
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
def ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder()
println(ndkDir)
commandLine "$ndkDir/ndk-build.cmd",
'NDK_PROJECT_PATH=build',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}
이제, Build > Make Project 를 하자.
위처럼 executing ndkBuild 는 task 를 define 하는 시점에 실행되고, commandLine 부분은 실제 task 가 실행되는 시점에 실행된다.[ref. 2] 그렇기 때문에 위와 같은 결과화면이 나온다.
이제 진짜 ndkBuild 부분을 넣고 Make project 를 실행하면 된다.
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
def ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder()
println(ndkDir)
commandLine "$ndkDir/ndk-build.cmd",
'NDK_PROJECT_PATH=build',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}
source git
------------------------------------------------
gradle 을 이용해서 architecture 별로 build
이제 architecture 별로 build 할 수 있는 법을 살펴보자. 동영상은 아래 link 를 확인하면 된다.
source 는 ndk 의 sample source 를 참고했다.
<ndk_path>\android-ndk-r10d\samples\hello-jni\src\com\example\hellojni\HelloJni.java
- Android Project 만들기
- terminal 에서 javah 실행
- copy <hello-jni>\jni ---> <NdkHelloWorld>\app\src\main\jni
- <NdkHelloWorld>\app\src\main\libs 만들기 :ndk-build에서 출력 디렉토리를 설정할 수가 없기 때문에 결과 파일이 src->main->libs 에 생긴다[ref. 1]
- <NdkHelloWorld>\app\src\main\libs 아래 eabi platform 별로 directory 만들기
- <NdkHelloWorld>\local.properties 에 ndk.dir 추가
- <NdkHelloWorld>\app\build.gradle 에 ndk 관련 설정 추가(설명은 ref. 2에서 확인할 수 있다.)
android { ... ndk { moduleName "hello-jni" } sourceSets.main{ jni.srcDirs = [] jniLibs.srcDir 'src/main/libs' } task buildNative(type: Exec, description: 'Compile JNI source via NDK') { def ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder() commandLine "$ndkDir/ndk-build", '-C', file('src/main/jni').absolutePath, '-j', Runtime.runtime.availableProcessors(), 'all', 'NDK_DEBUG=1' } task cleanNative(type: Exec, description: 'Clean JNI object files') { def ndkDir = project.plugins.findPlugin('com.android.application').getNdkFolder() commandLine "$ndkDir/ndk-build", '-C', file('src/main/jni').absolutePath, 'clean' } clean.dependsOn 'cleanNative' tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn buildNative } }
See Also
References
- ndk-build를 Android Studio에서 사용하기 - Burt K.
- Using custom Android.mk with Gradle/Android Studio
- Android Studio, gradle and NDK integration | ph0b's
- Android Tools Project Site > 3.2.1 Configuring the Structure
- After upgrading to Gradle 2.0: Could not find property 'Compile' on root project
안드로이드 스튜디오 1.1.0 업데이트 + NDK
안드로이드 스튜디오를 좀 늦게 1.1.0 으로 업데이트 하고
'IT_Programming > Dev Tools' 카테고리의 다른 글
안드로이드 스튜디오 단축키 (0) | 2015.04.09 |
---|---|
안드로이드 스튜디오에서 Git 시작하기 (0) | 2015.04.06 |
[Android Studio] Using SVN in Android Studio (0) | 2015.04.05 |
Android Studio Tips: Project 설정 (0) | 2015.03.30 |
Android Studio 1.1.x 에서 unsigned apk 만들기 (0) | 2015.03.25 |