【JNI探索之路系列】之六:动态库的调用过程

来源:互联网 发布:苹果6s淘宝打字输入法 编辑:程序博客网 时间:2024/06/04 18:04

作者:郭嘉
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
github:https://github.com/AllenWells

【JNI探索之路系列】章节索引

【JNI探索之路系列】之一:JNI简介
【JNI探索之路系列】之二:变量类型
【JNI探索之路系列】之三:JNI方法调用
【JNI探索之路系列】之四:JNI应用接口
【JNI探索之路系列】之五:JNI异常处理
【JNI探索之路系列】之六:动态库的调用过程

一 SO库调用流程

1.1 Java层调用SO库

Java层通过以下两种方式调用SO库。
1 直接调用

System.loadLibrary("ibforlinx_runtime");

这里可以把SO库放在Eclipse下工程的lib/eabi(没有可以新建)目录下,Eclipse可以自动寻找路径。
ibforlinx_runtime.so必须是在java.library.path这一jvm变量所指向的路径中
2 绝对路径

System.load("/system/lib/libforlinx_runtime.so");

这个路径必须是在java.library.path这一jvm变量所指向的路径中,可以通过以下函数获得。
System.getProperty(“java.library.path”);
默认情况下,在Windows平台下,该值包含如下位置:

  • 和jre相关的一些目录。
  • 程序当前目录。
  • Windows目录、
  • 系统目录System32
  • 系统环境变量path指定目录。

1.2 JNI_OnLoad()被调用

JNI组件被成功加载时,该函数会被回调,JNI_OnLoad()函数主要有两个方面的作用。
1 指定JNI版本,如果未指定,则默认使用1.1.
2 初始化设定,进行各种资源的初始化。
本地方法结构。

typedef struct{       const char* name;// java方法名称       const char* signature; // java方法签名      void*   fnPtr;// c/c++的函数指针  } JNINativeMethod;jint JNI_OnLoad(JavaVM* vm, void* reserved){    JNIEnv *env;    jvm = vm;    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)    {        LOGE( "^^^^In JNI_OnLoad(): Failed to get the environment using GetEnv()");        return -1;    }    //缓存DataExchanger对象    initClassHelper(env, data_exchanger_path, &data_exchanger_obj);    //本地函数注册    JNINativeMethod methods[] =            {                { "setSkbDataToLibrary",                "(Lcom/sumavision/bnsas/data/SkbDataFromApk;)I",(void*)Java_com_sumavision_bnsas_data_DataProvider_setSkbDataToLibrary },                { "pushMsgToLibrary", "(I)I",(void *) Java_com_sumavision_bnsas_data_DataProvider_pushMsgToLibrary },                { "getSKBCiferFromLibrary",                "()Lcom/sumavision/bnsas/data/SkbCiferData;",(void*)Java_com_sumavision_bnsas_data_DataProvider_getSKBCiferFromLibrary } };    jclass data_exchanger_cls = env->FindClass(data_exchanger_path);    // 类、方法数组、方法长度    if (env->RegisterNatives(data_exchanger_cls, methods,            sizeof(methods) / sizeof(methods[0])))    {        LOGI("^^^^In JNI_OnLoad(): Failed to register native methods");        return -1;    }    return JNI_VERSION_1_4;}

可以把某些类的对象缓存成全局变量。

void initClassHelper(JNIEnv *env, const char *path, jobject *objptr){    jclass cls = env->FindClass(path);    if (NULL == cls)    {        LOGE("failed to get %s class reference", path);        return;    }    jmethodID constr = env->GetMethodID(cls, "<init>", "()V");    if (NULL == constr)    {        LOGE("failed to get %s constructor", path);        return;    }    jobject obj = env->NewObject(cls, constr);    if (NULL == obj)    {        LOGE("failed to create a %s object", path);        return;    }    (*objptr) = env->NewGlobalRef(obj);}

当data_exchanger_obj被缓存成全局变量后,就可以通过以下代码来使用data_exchanger_cls里的函数。

    JNIEnv *env;    jsize nJvms = 0;    int status;    bool isAttatched = false;    if (JNI_GetCreatedJavaVMs(&jvm, 1, &nJvms))    {        LOGE("The method JNI_GetCreatedJavaVMs() execute failed\n");        return 7;    }    status = (*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_4);    if (status < 0)    {        LOGE("THe method GetEnv() fail to get JNI environment, use ""AttachCurrentThread() to get env");        status = (*jvm)->AttachCurrentThread(jvm, &env, NULL);        if (status < 0)        {            LOGE("The method AttachCurrentThread() fail to attach ""current thread");            return -1;        }        isAttatched = true;    }    jclassdata_exchanger_cls=(*env)->GetObjectClass(env,data_exchanger_obj);

1.3 调用SO库中其他业务逻辑

1.4 JNI_OnUnload()被调用

附带说一下JNI的打印调试相关写法:

可以在自己的头文件定义以下宏来进行相关信息的打印。

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)  #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)   #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)  #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)  #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__)  

自己设计了一个打印函数,不过打印的结果有些许问题,待以后验证一下。

int JNILogI(const char *fmt, ...){    int ret = 0;    va_list args;    va_start(args, fmt);    if (JNILogSwitch)    {        ret = __android_log_print(ANDROID_LOG_INFO, TAG, fmt, args);    }    va_end(args);    fflush(stdout);    return ret;}
0 0
原创粉丝点击