Android JNI编程

Android JNI编程使用

参考文档:

https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html

https://blog.csdn.net/qq_37858386/article/details/103765111

https://my.oschina.net/wolfcs/blog/111309

JNI注册方式

JNI是Java Native Interface的缩写,它允许Java代码和其他语言写的代码进行交互。Java支持调用C/C++,但是不能直接调用,需要使用一个中间层来进行转换,JNI的作用就是粘合Java代码和C++代码。JNI注册包括静态注册和动态注册。静态注册效率低,每次使用native方法时都要进行查找;动态注册方式一次注册,查找效率高。

JNI静态注册

静态注册原理:

根据函数名来建立 java 方法与 JNI 函数的一一对应关系。静态注册的对应关系以Java__包名__类名_方法名()的方式找到对应的native方法。
其实就是:Java+包名+类名+方法名(native方法)

例如:Java_packagename_classname_methodname(JNIEnv *env,jclass/jobject,...)

静态注册方法步骤

1,在Java文件中定义native方法。
2,在cmd命令行模式中切换目录到定义native方法class文件(或者java文件)存放位置。
3,用javah 和javac命令生成包含native方法的.h头文件。
4,实现native方法,用ndk-build编译生成.so库。

静态方法注册JNI的弊端

1. 必须遵循某些规则 
2. 名字过长 
3. 多个class需Javah多遍,其实Android Studio中可不用这么做 
4. 运行时去找效率不高

JNI动态注册

动态注册原理

动态注册是在JNi层实现的,JAVA层不需要关心,因为在system.load时就会去调用JNI_OnLoad。动态注册的原理:JNI 允许我们提供一个函数映射表(JNINativeMethod),注册给 JVM,这样 JVM 就可以用函数映射表来调用相应的函数, 而不必通过函数名来查找相关函数,流程更加清晰可控,效率更高。

实现流程

  • 当Java层代码中执行System.loadLibrary("NativeLib");时,Native中的JNI_OnLoad(JavaVM *vm, void *reserved)方法会被调用,此时可以注册对应于Java层调用的native方法。

  • JNINativeMethod的定义如下,包含三个元素:方法名、方法签名、native函数指针。该结构体用于描述需要注册的方法信息。

    typedef struct{
    	const char* name;         // Java方法名称
    	const char* signature;    // Java方法签名
    	void*       fnPtr;        // native方法函数指针
    } JNINativeMethod;
    
    // 定义方法映射关系
    static JNINativeMethod methods[] = {
    	{"sayHello", "(Ljava/lang/String;)Ljava/lang/String;", (void*)sayHello},
    }
    
  • FindClass:加载一个本地定义的类

    jclass FindClass(JNIEnv *env, const char *name);
    
  • GetMethodID(查找java类中的方法)

    jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
    clazz:查找的java类
    name: 方法名
    sig:方法的返回值表示 (II)V表示void (int, int)类型的方法
    // 获取 ArrayList 类
    jclass list_jclass = env->FindClass("java/util/ArrayList");
    // 获取 ArrayList 构造函数id
    jmethodID list_init = env->GetMethodID(list_jcs, "<init>", "()V");
    
  • RegisterNatives:手动注册native方法

    static int registerNativeMethods(JNIEnv* env)
    {
        jclass clazz;
        clazz = env->FindClass("com/example/test/MainActivity");
        if (clazz == NULL) {
            return JNI_FALSE;
        }
        if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
            return JNI_FALSE;
        }
        return JNI_TRUE;
    }
    
    • RegisterNatives中第二个参数gMethods是一个二维数组,代表着Java类里每个native方法所对应的实现方法。
      • JNI字段描述符: ()Ljava/lang/String; 这是一种对函数参数和返回值的编码,这种编码叫作JNI字段描述符(JavaNative Interface FieldDescriptors)。规则是:括号里面放置参数,在括号后面放置返回类型。(参数描述符)返回类型
    static JNINativeMethod gMethods[] = {
        {"helloworld", "()Ljava/lang/String;", (void*)Jni_helloworld}};
    
    • 第三个参数代表要指定的native的数量。
  • 整体流程

    1、实现 JNI_OnLoad 方法,在加载动态库后,执行动态注册.
    2、利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系.
    3、调用 FindClass 方法,获取 java 对象.
    4、调用 RegisterNatives 方法,传入 java 对象,以及 JNINativeMethod 数组,以及注册数目完成注册.

动态注册关键

3、动态注册的关键字是两个:

1、JNI_OnLoad()方法,这个是载入Jni库后调用的第一个方法,在这里可以将方法对应表注册给JNI环境
2、JNINativeMethod结构,这个结构是将jni层的方法映射到Java端方法的关键,name:JNI层的方法名
posted @ 2022-05-07 19:31  overfitover  阅读(155)  评论(0编辑  收藏  举报