Programming/Android & Java2012. 10. 25. 11:03

JNI 를 사용하면서 java 함수와 c / c++ 함수를 mapping 하는 방법은 두가지가 있다.


c / c++ 함수의 이름을 규칙에 맞게 선언해서 사용하는 방법을 지난 번에 알아보았고,


두번째 방법인 JNI_OnLoad 함수에 대해 지금부터 알아보겠다.


JNI 에 대해서 가장 많은 예제를 포함하고 있는 것이 android 소스이므로,


android 소스에서 사용법은 확인하도록 해보자.


===== (7) JNI_OnLoad 함t수 =========================================================


1. JNI_OnLoad, JNI_OnUnload 의 선언


JNI 에서 java 로 library 가 load 되는 과정을 살펴보면,

System.loadLibrary() 함수가 호출될 때, 우선 JNI_OnLoad 함수하는지 확인하고

있을 경우에는 함수의 내용을 수행하고 없을 경우에는 기본 규칙에 따라 함수를 mapping 하게 된다.


따라서 JNI_OnLoad 를 구현할 경우 함수의 mapping 을 직접해줘야 한다

하지만 경우에 따라서는 JNI_OnLoad 함수에 부가적이 내용 끼워넣어서

필요한 초기화 작업이 자동으로 이루어지도록 할 수도 있다.

jni.h 에서 함수의 선언을 찾을 수 있다.


/usr/lib/jvm/java-6-openjdk/include/jni.h

....

1943 /* Defined by native libraries. */

1944 JNIEXPORT jint JNICALL

1945 JNI_OnLoad(JavaVM *vm, void *reserved);

1946 

1947 JNIEXPORT void JNICALL

1948 JNI_OnUnload(JavaVM *vm, void *reserved);

....


2. android 에서의 구현


직접적인 사용의 예를 찾아보기 위해 android 소스를 살펴보자.


..../frameworks/base/services/jni/onload.cpp

#include "JNIHelp.h"

#include "jni.h"

#include "utils/Log.h"

#include "utils/misc.h"


namespace android {

int register_android_server_AlarmManagerService(JNIEnv* env);

....

};


using namespace android;


extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)

{

    JNIEnv* env = NULL;

    jint result = -1; 


    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

        LOGE("GetEnv failed!");

        return result;

    }   

    LOG_ASSERT(env, "Could not retrieve the env!");


    register_android_server_AlarmManagerService(env);

    ....


    return JNI_VERSION_1_4;


jni  소스 부분에서 JNI_OnLoad 함수를 찾을 수 있다.

jni 에서 필요한 함수들을 포함하고 있는 'JNIEnv* env' 를 얻어와서 register_android_server_XXX 함수들에

넘겨주고 실제적인 함수 등록은 각 소스파일에 구현되어 있는 register_android_server_XXX 함수에서 한다.


..../frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp

 ....

static JNINativeMethod sMethods[] = { 

     /* name, signature, funcPtr */

    {"init", "()I", (void*)android_server_AlarmManagerService_init},

    {"close", "(I)V", (void*)android_server_AlarmManagerService_close},

    {"set", "(IIJJ)V", (void*)android_server_AlarmManagerService_set},

    {"waitForAlarm", "(I)I", (void*)android_server_AlarmManagerService_waitForAlarm},

    {"setKernelTimezone", "(II)I", (void*)android_server_AlarmManagerService_setKernelTimezone},

};


int register_android_server_AlarmManagerService(JNIEnv* env)

{

    jclass clazz = env->FindClass("com/android/server/AlarmManagerService");


    if (clazz == NULL)

    {   

        LOGE("Can't find com/android/server/AlarmManagerService");

        return -1; 

    }   


    return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService",

                                    sMethods, NELEM(sMethods));

}


mapping 할 함수들의 정보는 "sMethods[]" 에 넣어둔다.

해당 클래스가 java 에 있는지 확인하고, 클래스 이름과 함수의 정보를 "jniRegisterNativeMethods" 에 넘겨준다.

"NELEM" 은 sMethods[] 의 길이를 알려주는 메크로이다.

모든 jni 소스는 이와 같은 형태로 구성되어 있으므로 "jniRegisterNativeMethods" 함수만 확인해서 이해하고 있으면 된다.


..../dalvik/libnativehelper/JNIHelp.c

....

 /*

 * Register native JNI-callable methods.

 *

 * "className" looks like "java/lang/String".

 */

int jniRegisterNativeMethods(JNIEnv* env, const char* className,

    const JNINativeMethod* gMethods, int numMethods)

{

    jclass clazz;


    LOGV("Registering %s natives\n", className);

    clazz = (*env)->FindClass(env, className);

    if (clazz == NULL) {

        LOGE("Native registration unable to find class '%s'\n", className);

        return -1; 

    }   


    int result = 0;

    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {

        LOGE("RegisterNatives failed for '%s'\n", className);

        result = -1; 

    }   


    (*env)->DeleteLocalRef(env, clazz);

    return result;

}

....


함수들의 이름은 낯설지만 생각보다 단순하다.

JNIEnv 에 선언되어 있는 RegisterNatives 함수에 mapping 에 필요한 몇가지 정보만 넘겨주면 된다.


android 가 아닌 다른 JNI 프로그램에서도 이 모듈을 때어다 써도 아무 지장에 없다.

잘 만들어진 모듈이므로 적절히 배치해서 그대로 사용해도 된다.


다만, JNINativeMethod 구조체에 함수 정보를 넣을 때 사용되는 문자열의 내용이 마치 암호와 같이 되어있다.


static JNINativeMethod sMethods[] = { 

     /* name, signature, funcPtr */

    {"init", "()I", (void*)android_server_AlarmManagerService_init},

    ....

}; 


결국 argument 와 반환형, 함수 이름에 대한 내용들인데...

이 부분에 대해서는 다음에 적도록 하겠다.


http://forum.falinux.com/zbxe/?_filter=search&mid=lecture_tip&search_target=title&search_keyword=jni_onload&document_srl=571936

Posted by Mocker