해당 포스트는 "안드로이드 ndk의 모든 것", "안드로이드 NDK 네이티브 프로그래밍" 책의 내용을 요약한 것이다.



해당 포스트를 읽기 전에 JNI에 대해서 설명한 이전 포스트를 읽기 바란다.




이번 포스트에서 jni를 활용해서 위 사진과 같은 앱을 만들 예정이다.


1. 안드로이드 프로젝트를 하나 생성하고 위 사진과 같이 TextView를 Activity에 배치한다.

2. 자바 클래스 "JNIClass.java" 파일을 생성하고 다음과 같이 입력한다.

public class JNIClass {
static
{
System.loadLibrary("example");
}

public native String getNumString(String str, int num);

private String callback(String str, int num){

return str+" : "+num;
}
}

System.loadLibrary는 네이티브 코드를 통해 만들어진 네이티브 라이브러리를 로딩한다. 인자로는 네이티브 라이브러리 이름을 넣으면 된다. 네이티브 라이브러리가 로딩되야 네이티브 함수가 호출될 수 있기 때문에 static 문 안에 System.loadLibrary를 호출해서 앱이 시작될 때 바로 로딩되게 한다. getNumString 함수는 native 키워드가 있는 걸로 보아 네이티브 함수인 것을 알 수 있다. callback 메서드는 네이티브 코드에서 호출하기 위해 임의적으로 만든 함수이다. 여기서 우리는 네이티브 코드를 사용하는 부분만 JNIClass파일에 별도로 작성했다. 이렇게 네이티브 코드를 사용하는 부분만 따로 빼놓으면  javah를 사용하기 편하다.


3. Activty 코드를 수정한다. 


public class MainActivity extends AppCompatActivity {

JNIClass jni;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

jni = new JNIClass();

TextView tv = (TextView) findViewById(R.id.textView1);
tv.setText(jni.getNumString("Native Method 호출",1));
}
}

Activity에서 JNIClass 클래스 내부 함수 getNumString을 호출하고 반환 값을 TextView에 넣는 것을 알 수 있다.


4. javah를 이용해서 헤더파일을 생성한다.

<File> 메뉴에서 <Settings> 하위 메뉴를 클릭하면 위와 같은 창이 나온다. 해당 창에서 <Tools> 안에 External Tools를 누르면 '+' 아이콘이 생기는 데 해당 아이콘을 누른다. 그러면 다음과 같은 창이 뜨고 입력란에 다음과 같이 입력한다.

- Name : javah

- Description : Android Tool - javah

- Options에서 밑에 두 개 체크한다.

- Program : 설치된 jdk 폴더 안에 bin\javah.exe 경로를 입력한다.

- Parameters : -v -jni -d $ModuleFileDir$/src/main/jni $FileClass$

- Working directory : $SourcepathEntry$


입력하고 OK를 누르면 javah 항목이 생긴다. 그 다음 네이티브 소스가 있는 파일을 우클릭 한후 'External Tools' 메뉴에서 'javah'를 누른다. 해당 예제에서는 'JNIClass' 파일을 우클릭 한후 'External Tools' -> 'javah'를 누른다. 그러면 src/main 폴더안에 jni라는 폴더가 생기고 JNIClass안 네이티브 함수에 대한 헤더파일이 생성된다. 다음은 생성된 헤더파일 소스이다.

#include <jni.h>
/* Header for class com_example_user_myapplication_JNIClass */

#ifndef _Included_com_example_user_myapplication_JNIClass
#define _Included_com_example_user_myapplication_JNIClass
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_user_myapplication_JNIClass
* Method: getNumString
* Signature: (Ljava/lang/String;I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_user_myapplication_JNIClass_getNumString
(JNIEnv *, jobject, jstring, jint);

#ifdef __cplusplus
}
#endif
#endif

'native String getNumString(string str, int num) 메서드에 대해서 위와 같은 헤더 파일이 자동으로 생겼다. 


5. 네이티브 함수를 정의한다.

jni 폴더에 c++ 소스코드를 만들고 자동으로 생성된 헤더파일을 포함 시킨 후 함수를 정의한다. 


#include "com_example_user_myapplication_JNIClass.h"

JNIEXPORT jstring JNICALL Java_com_example_user_myapplication_JNIClass_getNumString
(JNIEnv * a, jobject b, jstring str, jint num)
{
jclass cls = a->GetObjectClass(b);
jmethodID func = a->GetMethodID(cls, "callback", "(Ljava/lang/String;I)Ljava/lang/String;");
return (jstring)a->CallObjectMethod(b, func, str, num);
}

위 코드는 자바에 있는 함수를 호출한다. 'JNIClass' 클래스 내부의 callback 함수를 호출한다. GetMethodID를 통해서 함수 이름과 함수형에 대해서 기술하는 걸 알 수 있다. 주의할 것이 String에 대해서 마지막에 세미콜론을 붙여 줘야 한다. 그리고 CallObjectMethod를 호출해서 str과 num 인자를 전달하고 String 객체를 반환 받아 jstring 으로 형변환 후 리턴한다. 


6. Android.mk 파일을 생성한다.

안드로이드에서 작성한 네이티브 코드를 빌드하기 위해선 Android.mk을 만들어야 한다. 마찬가지로 jni 폴더에 Android.mk 파일을 만든다. 그리고 다음의 코드를 넣는다.

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := example
LOCAL_SRC_FILES := main.cpp

include $(BUILD_SHARED_LIBRARY)


7. 프로젝트를 빌드하면 정상적으로 실행된다.

'안드로이드 > 기본' 카테고리의 다른 글

NDK(4) - JNI_OnLoad 예제, 한글처리  (0) 2017.07.02
NDK(3) - 라이브러리 의존성 문제  (0) 2017.07.01
NDK(1) - JNI, Android.mk  (0) 2017.06.30
안드로이드 mvp  (0) 2017.06.15
안드로이드 mvc, mvp, mvvm  (0) 2017.06.09

+ Recent posts