- AntiStroy 님의 블러그에서 퍼왔음을 알립니다.

프로젝트를 진행하면서 JNI에 대해 알게 되었었다. Java에서 C함수를 호출하거나 C에서 Java의 메소드를 호출할 때 사용하는 것인데, 안드로이드 공부를 하다가 조금 더 파고들게 되었다. 

* 안드로이드는 자바로 프로그래밍을 하는데 왜 JNI에 대해 알아야 할까?
우선 안드로이드 플랫폼은 순수하게 Java로만 구성되어진 것이 아닌 Java 레이어와 C/C++레이어가 서로 상호 작용하면서 동작한다. 이 두 레이어가 유기적으로 동작하게 만들려면 JNI에 대해 알아야 한다. 
그리고 일반적으로 Java는 C/C++에 비해 느리다. 성능이 중요할 경우 C/C++로 작성하고 이를 JNI를 통해 Java에서 호출할 수 있다.

* NDK(Native Development Kit)
안드로이드 애플리케이션에서 사용할 네이티브 라이브러리를 작성하기 위한 도구 모음.
실제로 모듈을 구현하기 위해서는 JNI에 대한 지식 필요

* JNI의 개발 순서
1. Java 코드 작성
2. Java 코드 컴파일
3. C 헤더 파일 생성
4. C 코드 작성
5. C 공유 라이브러리 생성
6. Java 프로그램 실행

1. Java 코드 작성


public class AtinJNI {
// 네이티브 메소드 선언 
native void printAtin();
native void printString(String str);
  // 라이브러리 로딩
static {System.loadLibrary("atinjni");}
public static void main(String args[]){
AtinJNI myJNI = new AtinJNI();
  // 네이티브 메소드 호출
myJNI.printAtin();
myJNI.printString("Hello Atin");
}
}

[Source 1] AtinJNI.java

[표 1] 플랫폼별 System.loadLibrary() 메소드가 로딩하는 C라이브러리 형식
 호스트 플랫폼  실제 로드되는 C 라이브러리 
 Window   libname.dll 
 Linux   libname.so 


2. Java 코드 컴파일
javac를 통해서 .java파일을 .class로 컴파일한다.
 javac AtinJNI.java

위와 같은 명령을 실행할 경우 아무 에러가 나지 않는다면 .class로 컴파일 된 것이다.

3. C 헤더 파일 생성
이제 Java 소스를 만들었으니 C 소스를 짜야 한다. 그런데 중요한 점은 어떻게 Java에서 호출되는 C 소스를 짤 것인가이다. 이것을 위해 javah라는 툴이 있다. 이 툴을 사용하면 .class파일을 파싱해서 만들어야 할 C 함수의 헤더를 만들어준다. 

javah <native로 선언된 메소드를 포함한 자바 클래스명>
- <JDK_HOME>\bin에 포함.
- Java 네이티브 메소드와 연결될 수 있는 C함수의 원형 생성

 javah AtinJNI
위와 같은 명령어를 실행하면 AtinJNI.h라는 헤더 파일이 생성됀다.

내용은 아래와 같다.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class AtinJNI */

#ifndef _Included_AtinJNI
#define _Included_AtinJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     AtinJNI
 * Method:    printHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_AtinJNI_printHello
  (JNIEnv *, jobject);

/*
 * Class:     AtinJNI
 * Method:    printString
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_AtinJNI_printString
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

[Source 2] AtinJNI.h

4. C 코드 작성
위에서 생성한 AtinJNI.h의 헤더를 기준으로 C 소스를 작성하면 됀다. 


#include "HelloJNI.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_AtinJNI_printAtin
  (JNIEnv *env, jobject obj)
{
printf("Hello Atin \n");
return;
}

JNIEXPORT void JNICALL Java_AtinJNI_printString
  (JNIEnv *env, jobject obj, jstring string)
{
const char *str = (*env)->GetStringUTFChars(env, string, 0);
printf("%s! \n", str);
return;
}

[Source 3] atinjni.c

5. C 공유 라이브러리 생성
중요한건 Window는 dll, Linux는 so파일을 만들어야 한다. 책에서는 C 소스를 작성 후 cl명령을 통해 dll을 만들었다.

cl 명령은 Visual Studio 2008 명령 프롬프트에서 실행해야 하며 VC 2008 Express Editions(http://www.microsoft.com/express/download/)가 설치되어 있어야 한다.

cl -I"<JDK_HOME>\include" -I"<JDK_HOME>\include\win32" -LD atinjni.c -Featinjni.dll
cl : vc++ 컴파일 명령어
-I<dir> : 헤더 파일을 검색할 디렉토리 경로 <dir> 추가
-LD : DLL 생성
-FE<파일명> : 컴파일 결과 파일 이름 지정

나는 VC에서 직접하고 싶어서 [1]과 같이 dll을 만들었다.
그런데 다음과 같은 문제에 직면했다.


Can't load IA 32-bit .dll on a AMD 64-bit platform
그래. 나는 64비트용 윈도우 7 운영체제였다. 32비트 dll이 문제가 되는 것이었다. 구글링해보니 32비트용으로 Java를 다시 까니 실행 명령어에 옵션을 주는 몇 방법들이 있었는데 마음에 들지 않았다. dll을 64비트용으로 하면 될 것 같았다. 그래서 [2]와 같은 방법으로 dll을 64비트용으로 만들어주었고 문제를 해결했다.

6. Java 프로그램 실행
이클립스라면 그냥 실행하면 되고 콘솔에서라면 "java AtinJNI"로 실행하면 결과를 볼 수 있다.

Reference
[1] VC에서 DLL 만드는 방법 (http://sol9501.blog.me/70102942944)
[2] 64비트용 컴파일 (http://blog.naver.com/PostView.nhn?blogId=honnak&logNo=70085595801&viewDate=&currentPage=1&listtype=0
[3] 인사이드 안드로이드(Inside the Android Framework, 위키북스)


+ Recent posts