JNI学习2

JNI学习记录

使用JNI_OnLoad()来初始化底层环境,并映射java层native方法

1.方法、结构体介绍

1>jint JNI_OnLoad(JavaVM* vm, void* reserved);

    该方法会在Java层调用System.LoadLibrary("")时被第一个调用。因此可以在这里对底层代码进行初始化,还可以加载c层代码到Vm中;
    与之对应的还有一个方法:
        void JNI_OnUnload(JavaVM *jvm, void *reserved);
这个方法会在JNI库被卸载时调用,所以可以在这里对C层的资源进行释放。

2>RegisterNatives(),下面的是RegisterNatives在JNI.h中的声明与调用

//声明
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,
                        jint);
//调用
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods){ 
    return functions->RegisterNatives(this, clazz, methods, nMethods); 
}

RegisterNatives有4个参数,第一个参数是JNIEnv指针,第二个参数是Java层中声明native方法的类,第三个参数是一个JNINativeMethod类型的指针,这个类会在下面提到,第四个参数是java层native参数的个数。

3>JNINativeMethod结构体

typedef struct {
    //name是一个char类型的指针,表示的是Java层native方法名称
    const char* name;
    //signture是一个特殊的char指针,表示该方法的参数类型与返回值类型
    const char* signature;
    //fnPtr是一个void类型的指针,表示的是c/c++层对应的方法,方法前需要加上(void*进行强制转换)
    void* fnPtr;
} JNINativeMethod;

下面简单介绍这个结构体怎么构造:
在我的例子里面:

//JNINativeMethod数组,用来将c层方法与java层方法进行映射
static JNINativeMethod methods[] = {
        {"_sayHello",          "()Ljava/lang/String;",(void *) Udp_sayHello},
        {"getStringFromC",     "()Ljava/lang/String;",(void *) getStringFromC},
        {"getStringFromNative","()Ljava/lang/String;",(void *) getStringFromNative},
};

这里有3个的数据,分别让我Java层的3个方法与native层的3个方法进行映射,第二个方法前面的()表示的是参数,括号后面则是返回值类型,参数的写法为如下:
字符 Jni类型
* I -> jint
* F -> jfloat
* J -> jlong
* Z -> jboolean
* V -> void
* Ljava/lang/String -> jstring
要传入自定义的Java对象的化,使用L+类包名+类名 -> jobject
更多的类型可以参考这篇blog: http://blog.csdn.net/bigapple88/article/details/6756204

4>完整的代码:

  • native层:
        #include <stdio.h>
        #include <jni.h>

        //java层声明native方法的类的完整名,com.jxm.test.NDK需要写成下面的形式;
        static const char *classPathName = "com/jxm/test/NDK";

        //对应Java层的native方法_sayHello
        static jstring Udp_sayHello(JNIEnv* env, jobject thiz) {
            return env->NewStringUTF("from sayHello");
        }
        //对应Java层的native方法getStringFromC
        static jstring getStringFromC(JNIEnv* env, jobject obj) {
            return env->NewStringUTF("getStringFromC");
        }
        //对应Java层的native方法getStringFromNative
        static jstring getStringFromNative(JNIEnv* env, jobject obj) {
          return env->NewStringUTF("getStringFromNative");
        }
        //JNINativeMethod数组,用来将c层方法与java层方法进行映射
        static JNINativeMethod methods[] = {
            {"_sayHello",          "()Ljava/lang/String;",(void *)Udp_sayHello},
            {"getStringFromC",     "()Ljava/lang/String;",(void *)getStringFromC},
            {"getStringFromNative","()Ljava/lang/String;",(void *)getStringFromNative},
        };

        //
        static int registerNativeMethods(JNIEnv* env, const char* className,
                                 JNINativeMethod* gMethods, int numMethods)
        {
            jclass clazz;
         //通过className获取到jclass对象
            clazz = env->FindClass(className);
         if (clazz == NULL) {
             return JNI_FALSE;
         }
         //注册Native方法到JNI
            if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
                return JNI_FALSE;
            }
            return JNI_TRUE;
        }

        static int registerNatives(JNIEnv* env)
        {
         if (!registerNativeMethods(env, classPathName,
                               methods, sizeof(methods) / sizeof(methods[0]))) {
                return JNI_FALSE;
            }
            return JNI_TRUE;
        }

        //一个联合体,JNIEnv* 是指针指向Java环境,第二个暂时不清楚什么意思
        typedef union {
           JNIEnv* env;
         void* venv;
        } UnionJNIEnvToVoid;

        //开始加载SO库
        jint JNI_OnLoad(JavaVM* vm, void* reserved){
         UnionJNIEnvToVoid uenv;
         uenv.venv = NULL;
         jint result = -1;
         JNIEnv* env = NULL;

          //判断当前JNI的版本,如果版本不为1.4,则直接返回
            if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {

              goto bail;
         }
         //获取JNIEnv指针
         env = uenv.env;
            //注册native方法
            if (registerNatives(env) != JNI_TRUE) {

               goto bail;
         }

         result = JNI_VERSION_1_4;

          bail:
         return result;
        }

        void JNI_OnUnload(JavaVM *jvm, void *reserved)
        {
        }
  • Java层代码:

    package com.jxm.test;
    
    /**
    * Created by Administrator on 2016/10/28.
    */
    
    public class NDK {
        public native String getStringFromC() throws IllegalStateException;
    
        public native String getStringFromNative() throws IllegalStateException;
    
        public native String _sayHello() throws IllegalStateException;
    }
    

5>小节

之前看别人分析ikjplayer的源码,发现了这种加载so库的方法,可以避免常规的,使用javah命令生成的头文件那种反人类的方法命名,可以使用自己定义的方法名。在JNI_OnLoad()时对要使用的资源进行加载也是一个值得关注的地方。

猜你喜欢

转载自blog.csdn.net/u013648164/article/details/52984282