No JNI_OnLoad问题解决

报错问题如下:

04-29 13:53:12.184: D/dalvikvm(361): Trying to load lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98

04-29 13:53:12.204: D/dalvikvm(361): Added shared lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98

04-29 13:53:12.204: D/dalvikvm(361): No JNI_OnLoad found in /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98, skipping init


原理描述:

Android的应用层的类都是以Java写的,这些Java类编译为Dex文件之后,必须靠Dalvik虚拟机( Virtual Machine)来执行。而C&C++的函数并不是在Dalvik虚拟机中运行的,所以效率和速度要比在Dalvik虚拟机中运行得快很多。假如在执行java程序时,需要载入C&C++函数时,那么加载是怎么样一个过程呢?

1.  java通知Dalvik虚拟机去加载本地C/C++库,调用函数:System.loadLibrary("Name");

2. Dalvik虚拟机成功加载库之后,就会自动地寻找库里面的JNI_OnLoad函数,所以本地方法要实现JNI_OnLoad函数,有一些版本并不需要实现这函数,加载的时候可以调用系统默认的JNI_OnLoad函数。也就是如果你的库里面没有写明JNI_OnLoad()函数,VM会默认该库使用最老的JNI 1.1版本。但是新版的JNI做了很多的扩充,也优化了一些内容,如果需要使用JNI的新版功能,就必须在JNI_OnLoad()函数声明JNI的版本。

(1)告诉Dalvik虚拟机此C库使用哪一个JNI版本当没有JNI_OnLoad()函数并且你又调用了新扩展的jni函数时,Android调试信息会做出如下提示(No JNI_OnLoad found)。

#include
#define LOG    "TEST-L" //自定义的LOG的标识  
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG,__VA_ARGS__) // 定义LOGD类型  
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG,__VA_ARGS__) // 定义LOGI类型  
#define LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG,__VA_ARGS__) // 定义LOGW类型  
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG,__VA_ARGS__) // 定义LOGE类型  
#define LOGF(...)  __android_log_print(ANDROID_LOG_FATAL,LOG,__VA_ARGS__) // 定义LOGF类型


JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {  
    JNIEnv* env = NULL;
    jint result = -1;  
	
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
        LOGE("jni_load failed\n");  
        return result;  
    }  
	
    result = JNI_VERSION_1_4;  
  
    return result;  
} 


(2)因为Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数,所以我们可以在JNI_OnLoad()里面进行一些初始化工作,如注册JNI函数等等。注册本地函数,可以加快java层调用本地函数的效率,但是这种方法仅仅适应于在android源码下编译。以下是C++版本的加载函数:

jint JNI_OnLoad(JavaVM* vm, void* reserved) {  
    JNIEnv* env = NULL;//定义JNI Env  
    jint result = -1;  
    /*JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint);   
     */  
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
        LOGE(ANDROID_LOG_ERROR, TAG, "GetEnv failed!");  
        return result;  
    }  
  
    LOGI(ANDROID_LOG_INFO, TAG, "loading . . .");  
  
/*开始注册 
 * 传入参数是JNI env 
 * 以register_android_test_myclass_myffunc(env)为例说明 
 */  
      
    if(register_android_test_myclass_myffunc(env) != JNI_OK) {  
        LOGE(ANDROID_LOG_ERROR, TAG, 
		"can't load register_android_test_myclass_myffunc");  
        goto end;  
    }  
  
    LOGI(ANDROID_LOG_INFO, TAG, "loaded");  
  
    result = JNI_VERSION_1_4;  
  
    return result;  
}  
static const char* const kClassPathName = "com/test/myclass"; 

static JNINativeMethod gMethods[] = {  
    {"setParam",       "(Ljava/lang/String;)V",        (void *)com_test_myclass_setParam},  
    {"prepare",             "()V",                     (void *)com_test_myclass_prepare},  
};

int register_android_test_myclass_myffunc(JNIEnv *env) {  
    return jniRegisterNativeMethods(env, kClassPathName, gMethods, 
			sizeof(gMethods) / sizeof(gMethods[0]));  
    /*跳到OnLoad.cpp文件中的 
     * jint jniRegisterNativeMethods(JNIEnv* env, 
                             const char* className, 
                             const JNINativeMethod* gMethods, 
                             int numMethods) 
                              
     */  
}



注意:

其中AndroidRuntime::registerNativeMethods是在头文件android_runtime/AndroidRuntime.h中定义,使用时得

且得在Android.mk文件中加上:

LOCAL_SHARED_LIBRARIES += \

         libandroid_runtime 

GetEnv()函数返回的  Jni 环境对每个线程来说是不同的   

由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时

所用的JNI Env是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取


 


(3)与JNI_OnLoad()函数相对应的函数JNI_OnUnload(),当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行清理动作。

/*
 *如果该函数没有实现,系统会调用默认的函数
 *如果不知道怎么用,最好不要自己实现改函数
 *因为你不知道系统回收了什么资源
 */
void JNI_OnUnload(JavaVM* vm, void* reserved){  
     LOGI("call JNI_OnUnload ~~!!");
} 


你可能感兴趣的:(No JNI_OnLoad问题解决)