티스토리 뷰

728x90

 Android Studio 1.3 RC 3 이상 버전과 Gradle 2.5 버전 이상에서 Android NDK를 지원하기 시작하였습니다. 구글에서 제공하는 예제코드를 이용하여 Android NDK 예제를 살펴보도록 하겠습니다.

 Android NDK를 빌드하기 위해서는 Gradle 2.5 버전을 사용해야 합니다. 이 2.5 버전은 기존 2.4 버전과는 전혀 다른 형태를 가지고 있습니다. 기존 Gradle과 호환되지 않아 별도의 작업을 해주어야 합니다.

 이 변경된 부분에 대해서 살펴보겠습니다.



살펴보기 전에

  • NDK Build는 Gradle 2.5에서만 동작합니다.
  • Android NDK r10e가 필요합니다.(Android Studio에서 다운로드 받을 경우 10e 버전이 자동으로 다운로드 됩니다.)
  • SDK Build Tools는 19.0.0 이상이 필요합니다.
  • Android Studio는 당연히 1.3 Preview 이상이 필요합니다.(카나리 채널을 선택하여 업데이트 하시면 됩니다.)
  • Build를 위한 Java 버전은 1.7


Android NDK 살펴볼 자료들

 Android NDK  예제 코드는 github에 공개되어 있습니다.

 Github Android-ndk 예제 코드 https://github.com/googlesamples/android-ndk

 새로운 Gradle 관련 설명http://tools.android.com/tech-docs/new-build-system/gradle-experimental#TOC-Samples


2주전에 GDG Korea에서 진행한 GDG DevFest 에서 김용욱(dalinaum)님의 발표 자료입니다. 발표 제목은 Veni, Vidi, Built라는 주제로 발표를 진행하셨습니다.

Gradle 2.5에서 달라진 내용을 담고 있습니다.

발표 자료 : http://www.slideshare.net/dalinaum?utm_campaign=profiletracking&utm_medium=sssite&utm_source=ssnewsfeed



Android NDK 설치하기

 안드로이드 스튜디오 NDK 설치하는 방법은 아래 NDK 설치하는 방법을 참고하시면 설치가 가능합니다.

 Android Studio NDK 설치하기http://thdev.net/626



시작하기에 앞서 아래 설명은 모두 아래와 같이 Project 보기 형태에서 진행합니다. Android Studio는 폴더를 생성할 경우 기본 값이 Android 보기 형태입니다.



Android Studio로 JNI 작서하기

 Android Studio로 JNI를 작성하기 위해서는 Android Studio 1.3 RC 3 이상 버전과 Gradle 2.5 버전을 사용해야 합니다.

 설치하는 방법은 위의 링크를 참고해주시면 설치가 가능합니다.

 

 설치하기 전에 설치가 완료되어야 진행이 가능합니다. Android Studio 기반 JNI는 기존의 JNI와 다른 점이 있습니다.

JNI 폴더 생성

 JNI 폴더를 생성합니다. jni 폴더의 생성 위치는 다음과 같습니다.

 app(Main Application 폴더 위치)  src main jni 폴더 생성

 아래와 같이 main 폴더에서 오른쪽 마우스를 눌러 New → Directory 눌러 폴더를 생성합니다. 폴더 명은 jni 입니다.

 만약 아래와 같이 C++ Class, C/C++ Source File, C/C++ Header File 이 목록에 표시되지 않는다면 카나리 채널을 선택하여 Android Studio를 업데이트 해주셔야 합니다.


 생성된 jni 폴더는 아래와 같습니다. 생성된 폴더에는 C/C++ 의 파일만 들어가게 됩니다.

 기존 ndk-build를 하기 위해서 생성하였던 Android.mk 파일과 Application.mk 파일은 더 이상 생성하지 않아도 되게 되었습니다. 

 이 부분이 Gradle 2.5에 통합되었기 때문에 불필요한 작업이 되었습니다.



Gradle 버전 변경

 gradle 폴더 → wrapper gradle-wapper.properties 파일을 열어서 버전을 수정합니다.

 아직 정식 버전으로 제공되지 않아 앱 폴더 설정으로 Gradle 코드를 작성하지는 못합니다.


아래 코드와 같이 gradle-2.4-all.zip 버전을 gradle-2.5-all.zip으로 변경하시면 됩니다. 

 Android NDK 예제 코드에는 gradle-2.5-rc-1-bin.zip 버전을 테스트할 수 있습니다. 

#Tue Jul 28 21:46:58 KST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip


build.gradle 파일의 내용 변경

 gradle-2.5 버전으로 변경하였으니 이제 프로젝트 전체에 있는 build.gradle 파일을 수정할 차례입니다. 

 build.gradle의 경로는 아래와 같습니다.

 최상위 폴더  → build.gradle을 더블클릭하여 열어주시면 됩니다.


buildscript의 dependencies의 코드를 아래와 같이 수정해주시면 됩니다. gradle-experimental:0.1.0 이 현재 최신버전으로 아래와 같이 수정해주시면 됩니다.

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
repositories {
jcenter()
}
dependencies {
// classpath 'com.android.tools.build:gradle:1.2.3'
// 기존 gradle 1.2.3으로 설정된 부분을 아래와 같이 변경합니다.
classpath 'com.android.tools.build:gradle-experimental:0.1.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
jcenter()
}
}


 위와 같이 변경후 sync 를 누르면 아래와 같이 com.android.application을 찾지 못한다는 오류가 발생합니다. 

 이 코드는 원래 build.gradle에 다음 코드로 적용되어 있었습니다. apply plugin: 'com.android.application' 이 코드를 gradle 2.5에 맞도록 수정해주어야 합니다.




Gradle 2.5 코드 적용 하기 전에 살펴보자!

 app 내에 build.gradle을 열어 코드를 수정해주어야 합니다.

 Projection 폴더 app build.gradle 실행하여 코드 수정.



build.gradle의 최상위 코드에 아래와 같은 코드가 적용됩니다.

기존 plugin에는 com.android.application 이였지만 가운데 model이 붙은 새로운 plugin을 사용합니다.

// apply plugin: 'com.android.application'
apply plugin: 'com.android.model.application'


우선 Gradle 2.4와 Gradle 2.5의 코드 차이를 살펴보겠습니다.

 Gradle 2.4는 위의 android { ... } 사이의 코드입니다.

 Gradle 2.5는 아래와 같이 model { ... } 이라는 부분이 추가되어 한번 더 감싸게 되며, android { ... } 이 안쪽으로 들어갔습니다. 

 그외 또 다른 점은 = 이 추가되었고, 일부 코드가 android. 으로 시작하는 등 전반 적인 코드 스타일도 변경되었습니다.

 현재 Gradle 2.5 버전에 맞는 UI는 아직 적용되지 않아서 App UI에서 설정을 변경하시면 적용되지 않습니다.

android {
compileSdkVersion 22
buildToolsVersion "23.0.0 rc3"

defaultConfig {
...
}
}
model {
android {
compileSdkVersion = 21
buildToolsVersion = "22.0.1"

defaultConfig.with {
...
}
}
}


Gradle 2.5 코드 적용

위와 같은 코드가 만들어져서 최종적인 gradle 코드는 아래와 같이 변경되었습니다.

 - NDK가 포함되는 코드는 아래에서 설명하겠습니다.

아래와 같은 형태의 gradle 코드가 완성되게 됩니다. 전체적으로 기존의 android { ... } 대신 model { ... } 이 감싸는 형태로 변경되었습니다.

 android { ... } 안에는 defaultConfig.with { ... } 과 compileSdkVersion, buildToolsVersion 정보가 포함되며, 

buildTypes 설정은 android.buildTypes { ... } 으로 android { ... } 밖으로 빠져있습니다. 

가장 중요한 점은 = 또는 +=과 같은 수식을 사용할 수 있다는 점입니다. 이를 통해 NDK 빌드시에 Android.mk에서 사용하던 수식과 비슷하게 gradle을 사용할 수 있게 되었습니다.

현재 구성은 아래와 같을 뿐 추후 변경될 수 있습니다.

apply plugin: 'com.android.model.application'

model {
android {
compileSdkVersion = 22
buildToolsVersion = "22.0.1"

defaultConfig.with {
applicationId = "com.example.hellojni"
minSdkVersion.apiLevel = 16
targetSdkVersion.apiLevel = 22
versionCode = 1
versionName = "1.0"
}
}
android.buildTypes {
release {
isMinifyEnabled = false
proguardFiles += file('proguard-rules.txt')
}
}
}


Gradle 셋팅을 다시 로드하기 전에 Android studio 설정에 들어가서 아래와 같이 gradle 설정을 변경해주세요.

 Android Studio 설정 → Build Tools → Gradle 로 접근합니다.



Gradle 셋팅을 다시 로드하면 아래와 같이 .gradle 폴더 아래 2.5 버전 gradle이 다운로드 받아지게 됩니다.



Android NDK 빌드를 위한 gradle 정의 부분 살펴보기

 Android studio NDK 예제를 참고하여 작성한 글입니다. 

 원문 코드 : https://github.com/googlesamples/android-ndk/blob/master/hello-jni/app/build.gradle

model { ... } 안에 다음의 코드가 추가되면 됩니다. android.ndk 설정은 아래와 같은 정의를 할 수 있습니다. 기존 Android Application.mk 파일(NDK 빌드를 위한 파일)을 대체하여 아래와 같은 코드를 작성할 수 있습니다. 일단 단순하게 1개의 파일을 so 로 빌드하여 테스트하는 예제로써 아래와 같습니다.

 jni 폴더 아래 hello-jni.c 파일을 생성합니다. android.ndk { ... }의 첫번째 줄인 moduleName = "hello-jni"로 정의합니다. 모듈 이름은 다른것으로 수정하여도 됩니다.

 그외 ndk flags 로 cppFlags, ldLibs, stl 등의 설정을 할 수 있습니다. 이런 부분은 안드로이드 ndk 예제에 잘 나와 있으니 적용이 가능합니다.

/*
* native build settings
*/
android.ndk {
moduleName = "hello-jni"
/*
* Other ndk flags configurable here are
* cppFlags += "-fno-rtti"
* cppFlags += "-fno-exceptions"
* ldLibs = ["android", "log"]
* stl = "system"
*/
}


 android.productFlavors 역시 아래와 같습니다. 아래 예제는 모든 ndk-build에 대한 대응으로 아래와 같이 작성되어 있습니다. 

 arm, arm7, arm8, x86, x86-64, mips, mips-64, all 로 선택적으로 빌드를 할 수 있습니다. 이는 기존 NDK buildApplication.mk 파일에 정의하였던 APP_ABI을 정의하는것과 매칭됩니다.

 마지막의  all을 정의하면 모든 시스템에 맞는 so를 생성하게 됩니다.

android.productFlavors {
// for detailed abiFilter descriptions, refer to "Supported ABIs" @
// https://developer.android.com/ndk/guides/abis.html#sa
create("arm") {
ndk.abiFilters += "armeabi"
}
create("arm7") {
ndk.abiFilters += "armeabi-v7a"
}
create("arm8") {
ndk.abiFilters += "arm64-v8a"
}
create("x86") {
ndk.abiFilters += "x86"
}
create("x86-64") {
ndk.abiFilters += "x86_64"
}
create("mips") {
ndk.abiFilters += "mips"
}
create("mips-64") {
ndk.abiFilters += "mips64"
}
// To include all cpu architectures, leaves abiFilters empty
create("all")
}


jni 예제 파일 작성

 jni 예제 파일은 Android hello-jni를 테스트하였습니다. jni예제 코드에서 package name의 경우 저는 아래와 같습니다.

net.thdev.hellojni/HelloJni.java 파일을 생성하였습니다. native 함수 명은 stringFromJNI 을 사용합니다.

최종 적인 jni 예제코드는 사용하는 기기에 따라 자동으로 #Define의 ABI에 정의된 이름이 출력되게 됩니다.

#include <string.h>
#include <jni.h>

/* This is a trivial JNI example where we use a native method
* to return a new VM String. See the corresponding Java source
* file located at:
*
* apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java
*/
jstring
Java_net_thdev_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a/NEON (hard-float)"
#else
#define ABI "armeabi-v7a/NEON"
#endif
#else
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a (hard-float)"
#else
#define ABI "armeabi-v7a"
#endif
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__x86_64__)
#define ABI "x86_64"
#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "arm64-v8a"
#else
#define ABI "unknown"
#endif

return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");
}


여기서 부터 주의점

 코드가 모두 작성되었고, 위와 같이 동작을 한다면 다음을 주의해주시면 됩니다. 저는 이걸 가지고 2일나 못찾고 있었습니다.

 app:dexAllDebug 로 시작하는 이 메시지.. 

 빌드는 잘 되는데 Run 을 하면 아래와 같은 오류가 발생! 아.. 여기서부터 전 2일간 검색과 삽질을 했습니다. 정말 별거 아닙니다.

 최상단에 준비하는 과정에서 적어두었는데요. Java 1.7 버전으로 빌드를 해야 Run 시에 오류가 발생하지 않습니다.



Gradle에 Java 버전 명시

 아래와 같이 allprojects { ... } 버전을 명시해주고 run을 하시면 오류가 발생하지 않습니다. ㅜㅜ 그냥 가장 기본적인것인데 이렇네요.

model {
android {
...

defaultConfig.with {
...
}

allprojects {
sourceCompatibility = 1.7
targetCompatibility = 1.7
}
}

android.buildTypes {
...
}
}


마무리

 그래도 이런 삽질을 경험하면서 결과를 봤습니다.

 제 Nexus 4 에서는 armeabi-v7a 라고 표시되고 있습니다. 별거 아니지만... ^^;



작성한 예제 코드

 작성한 예제 코드는 github에 올렸습니다. Android Example을 그대로 테스트한 것이라... 크게 다를건 없습니다.

 Github : https://github.com/taehwandev/HelloJni



댓글