해당 포스트는 "안드로이드 ndk의 모든 것", "안드로이드 NDK 네이티브 프로그래밍" 책의 내용을 요약한 것이다.
※ JNI_OnLoad
우리는 네이티브 코드를 작성할 때 javah를 통해 함수명을 만들어 내거나, 규칙에 맞게 함수명을 만들었다. 이 방법 말고 JNI_OnLoad를 통해 네이티브 함수를 만들 수 있다. JNI_OnLoad 안에서 JNI 버전 체크나 Global Reference 세팅이나 기타 초기화 작업을 하고 자바-네이티브 간 함수 연결을 할 수 있다.
Dalvik에서 네이티브 라이브러리를 로딩하면 가장 먼저 JNI_OnLoad 함수를 찾는다. 만약 JNI_OnLoad가 없다면 건너뛰고 다음 작업을 진행을 진행한다. JNI_OnLoad를 이용하는 이유는 네이티브 함수명을 패키지명, 클래스명, 메서드명을 연결하여 사용하지 않아도 되기 때문입니다. 함수명을 복잡하게 사용하면 코드의 가독성이 떨어지게 됩니다. 또한 네이티브 단에서 자바 함수에 능동적으로 접근하는 일이 생기는 데 이 때 JVM이 필요합니다. JNI_OnLoad를 사용하면 JVM에 쉽게 접근할 수 있습니다.
이전 포스트 안드로이드 스튜디오 NDK JNI 예제 프로젝트를 JNI_OnLoad 함수를 이용해 구현해보겠다. 이전 프로젝트에 있었던 javah로 만들어진 헤더파일과 main.cpp를 지우고 새로운 onLoad.cpp 파일을 만들었다. onLoad.cpp 코드는 다음과 같다.
#define LOG_TAG "JNI_OnLoad_Example"
#include <android/log.h>
#include <stdio.h>
#include "jni.h"
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
jstring 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);
}
//자바 안에서 네이티브 키워드를 붙인 함수가 있는 클래스를 설정한다.
static const char* classPathName = "com/example/user/myapplication/JNIClass";
//methods에 등록된 데이터를 통해서 자바-네이티브 간 함수들을 연결한다//첫 번째 인자는 이름으로 자바 상에서 native 키워드가 붙여진 함수 이름을 넣는다
//두 번째 인자는 함수의 signature이다.
//세 번째 인자는 cpp 파일 안에서 정의된 네이티브 함수의 포인터를 나타낸다.
static JNINativeMethod methods[] = {
{"getNumString", "(Ljava/lang/String;I)Ljava/lang/String;", (void*)getNumString},
};
//Register several native methods for one class
int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if(clazz==NULL)
{
LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
//실제 네이티브와 자바간에 함수를 연결한다.
if(env->RegisterNatives(clazz, gMethods, numMethods) < 0)
{
LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
//Register native methods for all classes we know about
int registerNatives(JNIEnv* env)
{
if(!registerNativeMethods(env, classPathName, methods, sizeof(methods)/sizeof(methods[0])))
{
return JNI_FALSE;
}
return JNI_TRUE;
}
//This is called by the VM when the shared library is first loaded
typedef union
{
JNIEnv* env;
void* venv;
}UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
LOGI("JNI_OnLoad");
if(vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK)
{
LOGE("ERROR: GetEnv failed");
goto bail;
}
env = uenv.env;
if(registerNatives(env) != JNI_TRUE)
{
LOGE("ERROR: registerNatives failed");
goto bail;
}
result = JNI_VERSION_1_6;
bail:
return result;
}
※ 한글 처리
자바는 UTF-8 형태의 문자셋을 사용하고 C는 그렇지 않기 때문에 한글처리하는 데 과정이 필요하다. 네이티브 함수에서 생성된 한글 문자열을 자바로 가져가기 위해서는 한글 문자열을 바이트형 배열에 복사해 준 후 자바에서 바이트형 배열을 String 으로 바꿔주면 된다.
//native 단 ........StringFromJNI() { jbyte buffer[] = "가나다라Test123"; jbyteArray jb = (*env)->NewByteArray(env, sizeof(buffer)); (*env)->SetByteArrayRegion(env, jb, 0, sizeof(buffer), buffer); return jb }
//// 자바 .... byte[] result; result = StringFromJNI(); String output = new String(result, "KSC5601"); ....
'안드로이드 > 기본' 카테고리의 다른 글
안드로이드 기초적인 성능 개선 (0) | 2017.07.05 |
---|---|
NDK(5) - 네이티브 스레드 (0) | 2017.07.02 |
NDK(3) - 라이브러리 의존성 문제 (0) | 2017.07.01 |
NDK(2) - 안드로이드 스튜디오 NDK JNI 기본 예제 (2) | 2017.07.01 |
NDK(1) - JNI, Android.mk (0) | 2017.06.30 |