본문 바로가기
IT 기술/Android

JNI 를 사용한 간단한 Android Project

by 땅뚱 2011. 12. 22.
간단한 예제를 통해서 JNI 를 사용하는 방법에 대해서 알아보자. 알아보기 전에 개발환경에 필요한 내용이 설치되어 있어야 한다. 1) android sdk, 2) android ndk, 3) 윈도환경이라면 cygwin, 4) jdk, 5) eclipse 등...  이와 같은 안드로이드 개발에 필요한 개발환경 세팅은 구축되어 있다고 가정한다. 앞선 내용에 대한 설치는 go google~

JNI 를 사용하는 프로젝트를 진행하는데 내 경우 자바도 잘 모르고 안드로이드도 잘 모르며, 심지어 이클립스 사용법도 익숙치 않아서 구글과 주위분들의 도움을 받아 진행했다.

<Android Project 생성>
1. eclipse 에서 JNITest 라는 Android Project 생성한다.
  - package name 은 임의로 준다. 여기서는 com.android.hellojni 라는 이름을 사용한다.
  - Android Project 생성을 마치면 필요한 디렉토리들과 JNITest.java 라는 파일이 생성된다.

<Java Code 작성>
이클립스에서 아래와 같은 코드를 작성하고 저장한다.
package com.android.hellojni;

import android.app.Activity;
import android.os.Bundle;

public class JNITest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        NativePrintString("JNI Test Application\n");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    static { System.loadLibrary("native_hello"); }      // libnative_hello.so 또는 native_hello.dll 을 로드한다.
   
    public native static void native_print(String str);   // 호출할 native 함수 prototype
    public static void NativePrintString(String str)      // native 함수를 호출하는 Java method
    {
        native_print(str);
    }
}
이클립스는 자동 컴파일을 해주기 때문에 저장하는 순간에 컴파일되어 JNITest.class 파일이 생성된다.
cygwin bash terminal 프로그램을 통해서 이클립스 workspace 에 가보면 JNITest 라는 디렉토리가 생성되어 있을 것이다.

JNITest 디렉토리는 다음과 같은 디렉토리로 되어 있다.


JNITest.java (source file)은 src 아래 있고, JNITest.class 파일은 bin/classes/com/android/hellojni 아래에 생성된다.



이 상태에서 관련된 native code 를 작성해야 할 차례이다. 그런데, java 에서 호출한 native 함수(예제의 native_print() 함수 이름이 그대로 native 함수로 매핑되지 않는다. java 의 JNI 에서 요구하는 형태에 따라서 작성해야할 함수 prototye 이 변경된다. 이를 위해서 필요한 header file 을 우선 만들어야 한다.
JNI header file 을 작성해주는 것이 javah 라는 유틸리티이다. javah 는 jdk 설치될 때 설치되는 프로그램이다.

<JNI Header 작성>
우선 설치된 cygwin terminal 을 통하여 JNITest 디렉토리로 이동한다. 아래와 같은 명령을 수행한다.

$ javah -classpath <pathname> <class name>


에러가 없이 수행되었다면, javah 를 실행한 디렉토리에 class name 에 해당하는 header file 하나가 생성되었을 것이다.
이 예제의 경우에 com_android_hellojni_JNITest.h 파일이 생성된다. 해당 파일을 열어보자.


JNIEXPORT 로 시작하는 함수 prototype 이 선언되어 있는 것을 확인할 수 있다.
Java_com_android_helljni_JNITest_native_1print(JNIEnv *, jclass, jstring); 에 해당하는 native 함수를 만들어주면 된다.

<Native Code 작성>
이클립스로 돌아가 보자. JNITest 프로젝트에 new->directory 에서 jni 라는 디렉토리를 만들고 jni 디렉토리에 위 함수에 선언된 함수의 body 를 구현할 파일을 추가한다. 여기서는 new->file 에서 hello_jni.c 라는 파일을 만들고 아래와 같이 코드를 작성한다.



위 코드에서 보듯이 jstring 이라는 jni 에서 사용하는 type 을 char 로 변경하기 위해서 GetStringUTFChars() 를 사용한다. 이 함수는 JNIEnv type 으로 넘어온 env 를 사용하여 호출할 수 있다. JNIEnv type 은 native 에서 java/jni 에서 제공하는 함수 및 java 의 method 등을 호출하기 위해서 사용되는 파라미터이다. 보통 JNIEnv 와 jclass 가 세팅된다. 자세한 내용은 JNI 관련 내용을 검색해보면 된다.

작성한 c/cpp 파일을 컴파일하여 shared library 형태로 만들어야 한다. 이것을 해주는 것이 ndk-build 이다.
안드로이드 ndk 를 설치하면 ndk-build 를 사용할 수 있다. 여기서 ndk-build 를 사용하려면 jni 디렉토리에 안드로이드가 알 수 있는 make file 형태인 Android.mk 파일을 작성하여야 한다.

Android.mk 는 다음과 같이 만든다.



LOCAL_PATH 는 현재 디렉토리를 가리키고, CLEAR_ARS 는 이전에 선언되었던 LOCAL_ 로 시작하는 변수들을 초기화 한다.(LOCAL_PATH 제외)

LOCAL_MODULE 은 처음에 java 코드에서 static { System.loadLibrary("native_hello"); } 에서 사용한 native_hello 를 적어준다. 이렇게 적으면 안드로이드에서 libnative_hello.so 파일을 생성해준다.

LOCAL_SRC_FILES 는 컴파일될 소스파일을 모두 적어주면 된다.

이렇게 Android.mk 를 작성하였으면 ndk-build 를 실행한다. ndk-build 는 프로젝트의 제일 상위디렉토리에서 수행한다.



위와 같이 에러없이 컴파일이 잘 수행되었다면, libs/armeabi 디렉토리가 생성되고 그 안에 libnative_hello.so 라는 이름으로 native binary 가 생성된다.

이클립스에서 실행해보면, 까만창에 Hello World, JNITest! 라는 화면이 나온다.



정상적으로 수행된 것 같으나, JNI 함수를 호출했는지 안했는지 확인할 방법이 없다.
다음에는 Native Code 에서 Android Log 를 사용하는 방법에 대해서 알아봐야 겠다.

여기서 끝~