• Android JNI代码语法解释


    JNI中的JNIEXPORT、JNIIMPORT和JNICALL

    两个关键字的定义都可以在jni_md.h下找到

    均用于定义与平台相关的宏

    用于标识函数用途

    • JNIEXPORT:(实则为C++规则)放置在函数、变量或对象的声明前面,指示编译器将其导出为动态链接库的一部分。这使得其他程序可以通过在运行时加载动态链接库并使用导出的函数、变量或对象
    • JNIIMPORT:(实则为C++规则)放置在函数、变量或对象的声明前面,指示编译器将其标记为从动态链接库中导入的符号
    • JNICALL:(实则为C++规则)一种标准的函数调用约定,也被称为 “标准调用”;具有以下性质:
      函数的参数按照从右到左的顺序依次入栈。这意味着最右边的参数首先被压入栈中。
      调用方清理栈上的参数。这意味着在函数调用结束后,由调用方负责从栈上移除函数的参数。
      函数的返回值通常存储在 EAX 寄存器中
    //Windows下的定义
    #define JNIEXPORT __declspec(dllexport)
    #define JNIIMPORT __declspec(dllimport)
    #define JNICALL __stacall
    
    //Linux下的定义(实际是空定义)
    #define JNIEXPORT
    #define JNIIMPORT
    #define JNICALL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    JVM如何查找native方法

    ①按照JNI规范的命名规则

    即根据JNI所约定的命名规则来指定函数的命名,具体规则如下:Java_类全路径_方法名

    JNIEXPORT jstring JNICALL Java_com_kqli_jni_JniTest_getStringFromC(JNIEnv *env, jclass jclass);
    JNIEXPORT jstring JNICALL Jave_com_test_jni_HelloWord_func(JNIEnv* env, jclass class, jstring str);
    
    • 1
    • 2
    • 第一个jstring为返回值(string类型)
    • 函数名中com_kqli_jni_JniTest_getStringFromC,代表Java类com_kqli_jni_JniTest,getStringFromC代表具体的函数名称
      JNIEnv,指向JVM函数表的指针
    • jclass,调用Java中native方法的实例对象

    ②调用JNI提供的RegsterNatives函数,将本地函数注册到JVM中

    //函数原型
    jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
    
    • 1
    • 2
    • clazz:声明native方法的类
    • methods:JNINativeMethod结构的数组
    typedef struct {
        char *name;	//java方法名称
        char *signature;//java方法签名
        void *fnPtr;//c/c++的函数指针
    } JNINativeMethod;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • nMethods:指定methods数组中的本地方法数,通常写法为
    nMethods = sizeof(methods) / sizeof(JNINativeMethod);
    
    • 1

    示例代码

    java

    package com.test.jni;
    
    public class A{
    	static{
    		System.loadLibrary("A");	
    	}
    	
    	public static native int a(String str);
    	public static native boolean b();
    	public static native int c(Object obj);
    	
    	public static void main(String[] args){
    		......
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    native

    jint a(JNIEnv *env ,jclass class, jstring str){
       ....
    }
    
    jboolean b(JNIEnv *env ,jclass class){
       ....
    }
    
    jint c(JNIEnv *env ,jclass class, jobject obj){
       ....
    }
    
    static JNINativeMethod method_table[] = 
    {
    	{"a", "(Ljava/lang/String;)I", (void *)a},
    	{"b", "()Z", (void *)b},
    	{"c", "(Ljava/lang/Object;)I", (void *)c},
    };
    
    jint JNI_OnLoad(JavaVM* vm, void* reserved)
    {
        ......
        jclass clz = env ->FindClass(JNIREG_CLASS);
        env ->RegisterNatives(clz, method_table, sizeof(method_table) / sizeof(JNINativeMethod));
        ......
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    JNI数据类型

    在这里插入图片描述

    类: 例如String的签名为Ljava/lang/String; 注意: 包名和类名用/隔开, 结尾有一个;
    数组:用[表示数组签名, 例如int[]的签名为[I

    JNI字符串的处理

    ①获取字符串

    JNI通过jstring来处理字符串数据,但是jstring是指向JVM内部的字符串,和C风格的字符串类型char * 不同,因此必须使用合适的JNI函数来访问JVM内部的字符串。
    因为Java默认使用unicode编码,而C/C++默认使用UTF编码,所以要注意进行编码转换。

    const char* GetStringUTFChars(jstring str, jbbolean *isCopy);
    
    • 1
    • str为需要获取的字符串
    • isCopy取值JNI_TRUE和JNI_FALSE,一般填NULL即可
    • JNI_TRUE:返回JVM内部源字符串的拷贝,并为新产生的字符串分配内存空间
    • JNI_FALSE:返回JVM内部源字符串的指针,并可以指针修改源字符串的内容

    ②释放字符串

    通过GetStringUTFChars获取到字符串并返回的为源字符串拷贝后,在使用完毕要记得释放内存。

    void ReleaseStringUTFChars(jstring str, const char* utf);
    
    • 1
    • str为需要释放的字符串指针
    • utf为字节编码

    ③创建字符串

    jstring NewStringUTF(const char * bytes);
    
    • 1
    • bytes为C/C++的字符串数据源

    ④其他字符串处理API

    • GetStringChars、ReleaseStringChars:用于获取/释放Unicode格式的字符串
    • GetStringUTFLength、GetStringLength:用于获取UTF-8/Unicode格式的字符串长度
    • GetStringCritical、ReleaseStringCritical:用于直接返回/释放源字符串的指针,获取这个指针会导致暂停GC线程,如果GC线程暂停时又被其他线程触发GC的话,会出现系统死锁的阻塞调用
    • GetStringUTFRegion、GetStringRegion:用于获取UTF-8/Unicode格式字符指定范围内的内容,并会将源字符串复制到一个预先分配的缓冲区内
  • 相关阅读:
    图像相似度对比分析软件,图像相似度计算方法
    Redis基础知识(四):使用redis-cli命令测试状态
    PMP认证,对项目经理有什么用?
    用Python绘制了若干张词云图,超级惊艳
    小游戏实战-Python实现石头剪刀布+扫雷小游戏
    【C语言】通讯录的简单实现
    vue3之echarts区域折线图
    qtcreator调试webkit
    Java异常:基本概念、分类和处理
    史上最全 JVM 性能调优:线程 + 子系统 + 类加载 + 内存分配 + 垃圾回收(内附最全学习笔记),不得不服,面试官也问不出毛病
  • 原文地址:https://blog.csdn.net/qq_43357394/article/details/133790121