JNI Basic for NDK -1
C++ 2012. 12. 27. 22:35 |JNI Basic for NDK - 1
이 글은 안드로이드에서 NDK를 사용하기 위해 필요한 Java JNI를 튜토리얼 형식으로 설명합니다. NDK를 사용하기 위해서 알아야할 내용입니다. 또한 안드로이드 플랫폼을 개발하기위해서도 NDK를 이해하는 것이 도움이 될 것입니다.
내용을 새로 작성한 것이 아니고 Oracle(Sun을 인수했죠)에서 무료로 배포하는 아래 책의 내용을 안드로이드에 맞도록 정리한 것입니다.
“Java Native Interface: Programmer's Guide and Specification”
http://java.sun.com/docs/books/jni/ 를 참고하세요.
이 글을 이해하기 위해서는 안드로이드 어플리케이션을 이클립스 환경에서 개발해 본 경험이 있어야합니다. 또한 NDK를 설치하고 sample 예제라도 실행을 해 본 경험이 필요합니다.
NDK 설치는 아래 페이지 참고 바랍니다.
http://developer.android.com/sdk/ndk/index.html
NDK 셈플코드 실행은 아래 페이지에 나와 있습니다.
http://developer.android.com/sdk/ndk/overview.html
JNI는 Sun에서 개발한 Java Native Interface의 줄임말입니다. Java와 호스트 환경에서 사용하는 네이티브 프로그래밍 언어와 Java의 상호 호환을 위한 규격이라고 생각하면 됩니다.
안드로이드에서는 Java가 어플리케이션을 작성하는 언어입니다. 하지만 기존에 C로 된 코드를 재사용하고 싶거나 실행 속도를 빠르게 하려는 이유로 C나 C++로 어플리케이션을 작성하고자 하는 개발자를 위해서 NDK라고 하는 tool을 제공합니다. NDK는 C/C++ 컴파일러를 포함한 tool로 안드로이드 어플리케이션 프로젝트에 쉽게 사용하도록 되어있습니다.
우리가 NDK를 사용해서 C/C++를 사용하려고 하지만 Java로 작성된 어플리케이션은 반드시 있어야합니다. 어쨌는 NDK는 보조적인 수단이니까요.
1. Hello JNI as a starter
대부분의 프로그래밍 학습이 Hello world로 시작하고 있습니다. JNI의 기초를 시작하는 이번 장도 이런 관례를 따라 “Hello NDK and JNI!”를 화면에 출력하는 것으로 시작합니다. 화면에 문자열을 표시는 안드로이드 환경을 사용하고 문자열은 JNI 인터페이스로 얻어오는 간단한 프로그램입니다.
이 장을 통하여서 안드로이드 환경에서 NDK 프로그래밍을 하는 순서를 알게 될 것입니다. 또한 JNI를 구성하는 Java 코드와 C 코드의 구조도 알 수 있게 될 것입니다.
안드로이드에서 JNI를 사용하는 방법은 Java에서 JNI를 사용하는 방법과 동일합니다. 다른 점은 안드로이드 어플을 사용하고 일반 네이티브 컴파일러(C나 C++ 같은)를 사용하는 대신 구글에서 제공하는 NDK tool을 사용한다는 것입니다.
아래와 같은 순서로 하나의 NDK 프로그램이 작성됩니다.
- 네이티브 메써드를 사용하는 안드로이드 어플 작성하기(Java)
- 네이티브 메써드용 헤더 파일 만들기(.h)
- 네이티브 메써드 구현하기(.c)
- 네이티브 메써드 컴파일하기
- 실행하기
꼭 이 순서를 따라하는 것은 아니지만 이 순서를 따르는 것이 여러모로 편할 겁니다.
2. HelloNdkJniActivity 만들기
NDK 프로그램은 Java로 만드는 안드로이드 어플의 일부분입니다. 그러니 안드로이드 어플이 없이는 사용할 수 없죠. 간단한 Hello NDK and JNI라고 하는 어플을 만들어 보도록 하겠습니다.
이클립스에서 새 Android Project를 생성합니다. New Android Project 창에 아래와 같이 입력하고 Finish를 클릭합니다.
Project Name : HelloNdkJni |
이렇게 하면 이클립스가 기본적인 HelloNdkJniActivity.java를 생성할 것입니다. HelloNdkJniActivity.java를 열어서 HelloNdkJniActivity class 를 아래와 같이 수정합니다.
public class HelloNdkJniActivity extends Activity { |
HelloNdkJniActivity class의 앞 부분에 stringFromJNI 네이티브 함수를 선언 하고 있습니다. 이 함수는 앞으로 C로 작성할 겁니다. "Hello NDK and JNI !"라는 문자열을 리턴하는 간단한 함수죠.
그리고 onCreate에서 이 함수를 호출하여 문자열을 받아 옵니다.
맨 마지막에 System.loadLibrary를 호출하여 stringFromJNI가 구현되어 있는 네이티브 라이브러리를 로드합니다.
public native String stringFromJNI();
네이티브 메써드와 일반 메써드를 구분하기 위해서 native라고 하는 형변환자가 쓰입니다. native를 붙여 이 메써드는 java가 아닌 다른 언어로 구현이 되어 있다는 것을 알려줍니다. 당연히 실제 구현이 여기에 없으니 그냥 세미콜론(;)으로 끝나죠.
static {
System.loadLibrary("hello-jni");
}
System.loadLibrary는 네이티브 라이브러리를 어플리케이션으로 읽어 들입니다. 인자로 들어가는 것은 라이브러이 이름인데 NDK의 확장자인 .so를 제외한 이름만 들어갑니다. 이 라이브러리는 당현히 stringFromJNI 가 호출되기 전에 로드가 되어야합니다. 그래서 static을 붙여 이 클래스의 어떤 함수보다 먼저 초기화 되도록 해야합니다.
자 이대로 실행을 한 번 해 볼까요? 하지만 실행이 되지 않습니다. 아직 네이티브 라이브러리를 만들지 않았기 때문이죠.
3. 네이티브 메써드 헤더파일
JNI에서는 네이티브 메써드와 자바 코드 간의 호환을 위해 몇가지 특별한 네이밍 규칙을 가지고 있습니다. 아래 표에는 그 중 하나의 예입니다.
String |
"Ljava/lang/String;" |
Int[] |
"[I" |
Object[] |
"[Ljava/lang/Object;" |
복잡하죠? 차라리 이런 것은 모르고 살고 싶습니다.
다행히도 아주 편리한 방법이 있습니다. 이를 편하게 도와주는 javah라고 하는 툴을 쓰면 됩니다. 이 툴을 사용해서 생성되는 헤더를 보고 실제 메써드를 구현합니다. javah를 사용하기 위해서는 JDK가 설치되어 있는 폴더의 bin 폴더가 환경변수에 path로 등록이 되어있어야 합니다.
제 경우는 |
HelloNdkJni 폴더 밑에 jni라고 하는 폴더를 생성합니다. 커멘드 창을 열어 폴더를 HelloNdkJni\jni로 이동합니다. 아래 명령어를 실행합니다.
javah -classpath ../bin -o hello-ndkjni.h ndk.hellondkjni.HelloNdkJniActivity
저는 윈도우즈에서 cygwin을 설치하여 사용하고 있습니다. 그러므로 여기서 이야기하는 커멘드 창이란 바로 cygwin 커멘드 창을 이야기하는 것입니다. |
classpath 옵션으로는 ../bin 을 줍니다. -o로 생성될 헤더의 이름을 정해 줍니다. 마지막으로 페키지 이름을 포함한 클래스 이름을 지정해 줍니다.
이클립스의 Package Explorer에서 프로젝트 이름에 마우스를 놓고 오른쪽 버튼을 클릭합니다. 그리고 refresh를 해 봅니다. 헤더 파일이 하나 생성된 것을 보실 수 있을 겁니다.
생성된 hello-ndkjni.h 의 내용 중에 중요한 내용은 이것입니다.
JNIEXPORT jstring JNICALL Java_ndk_hellondkjni_HelloNdkJniActivity_stringFromJNI
(JNIEnv *, jobject);
이 함수가 앞의 안드로이드 java 코드에서 사용된 stringFromJNI의 원형입니다. JNIEXPORT 와 JNICALL 에 대해서는 설명을 하지 않도록 하겠습니다. 대신 함수의 인자를 보시기 바랍니다. java에 사용된 네이티브 메써드에는 인자가 없었는데 여기는 두개가 있습니다. 일단 여기서는 javah 가 알아서 필요한 내용을 생성해 주었으니 자세한 내용은 차차 알아보도록 하겠습니다.
자 이제 실제 C 코드를 구현할 차례입니다.
4. 네이티브 메써드 구현
네이티브 메써드는 javah에 의해서 생성된 함수 원형을 사용해서 만들어야 합니다. hello-ndkjni.c를 아래와 같이 작성합니다.
#include <string.h> |
함수의 원형은 hello-ndkjni.h에서 만들어진 것을 그대로 사용하고 있습니다.
메써드 구현의 자세한 내용은 차차 이해가 될 것입니다. 그냥 "Hello NDK and JNI !"를 리턴하는 메써드라는 정도만 알고 넘어 가도록 하겠습니다.
jni.h 는 네이티브 코드가 JNI 함수를 호출하는데 필요한 정보를 담고 있습니다. 그러므로 모든 C나 C++ 소스에 항상 인크루드를 해야하는 헤더입니다.
5. 네이티브 소스 컴파일
앞에서 작성한 C 파일은 NDK를 사용해서 컴파일을 합니다. 컴파일을 하기위해서는 HelloNdkJni\jni 폴더에 Android.mk를 생성해야 합니다. 파일의 내용은 아래와 같습니다.
LOCAL_PATH := $(call my-dir) |
이제 jni 폴더에는 모두 3개의 파일이 만들어 졌습니다.
폴더를 HelloNdkJni로 이동하여 아래 명령어를 실행합니다. 이는 build.xml을 생성하기 위해서입니다.
android.bat update project -p . -s
HelloNdkJni 폴더에서 ndk-build를 실행하여 컴파일을 합니다.
ndk-build
android.bat과 ndk-build를 실행하기 위해서는 이 파일이 있는 경로를 path에 포함시켜두어야 합니다. android.bat는 C:\android-sdk-windows\tools에 있습니다. C:\android-sdk-windows는 안드로이드 SDK가 설치되어 있는 경로입니다. ndk-build는 C:\android-ndk-r6에 있습니다. C:\android-ndk-r6는 NDK가 설치되어 있는 경로입니다. |
6. 실행하기
이클립스의 Package Explorer에서 HelloNdkJni를 클릭한 후 F5눌러 변경된 내용을 반영합니다.
Run As -> android application을 해서 지금까지 작성한 코드를 실행합니다.
위에 보이는 화면과 같이 표시가 되면 Hello NDK and JNI 프로그램은 성공을 한 것입니다.
이제부터는 몇가지 데이터 형을 어떻게 사용해야하는지와 함수를 어떻게 다루어야하는 지를 배우면 됩니다. NDK를 실행하기 위한 과정이 좀 귀찮기는 하지만 그래도 할만 하죠?
'C++' 카테고리의 다른 글
문자열 비교는 == 비교연산자 와 strcmp 어떤걸 써야 하는가.. (0) | 2013.10.10 |
---|---|
자바 객체의 사용 - NDK (0) | 2012.12.27 |
JNI Basic for NDK -4 (0) | 2012.12.27 |
JNI Basic for NDK -3 (0) | 2012.12.27 |
NDK C++ 자료 (0) | 2012.12.27 |