Android JNI代码语法解释

文章目录

  • JNI中的JNIEXPORT、JNIIMPORT和JNICALL
  • JVM如何查找native方法
    • ①按照JNI规范的命名规则
    • ②调用JNI提供的RegsterNatives函数,将本地函数注册到JVM中
    • 示例代码
  • JNI数据类型
  • JNI字符串的处理
    • ①获取字符串
    • ②释放字符串
    • ③创建字符串
    • ④其他字符串处理API

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

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);
  • 第一个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);
  • clazz:声明native方法的类
  • methods:JNINativeMethod结构的数组
typedef struct {
    char *name;	//java方法名称
    char *signature;//java方法签名
    void *fnPtr;//c/c++的函数指针
} JNINativeMethod;
  • nMethods:指定methods数组中的本地方法数,通常写法为
nMethods = sizeof(methods) / sizeof(JNINativeMethod);

示例代码

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){
		......
	}
}

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));
    ......
}

JNI数据类型

Android JNI代码语法解释_第1张图片

类: 例如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);
  • str为需要获取的字符串
  • isCopy取值JNI_TRUE和JNI_FALSE,一般填NULL即可
  • JNI_TRUE:返回JVM内部源字符串的拷贝,并为新产生的字符串分配内存空间
  • JNI_FALSE:返回JVM内部源字符串的指针,并可以指针修改源字符串的内容

②释放字符串

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

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

③创建字符串

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

④其他字符串处理API

  • GetStringChars、ReleaseStringChars:用于获取/释放Unicode格式的字符串
  • GetStringUTFLength、GetStringLength:用于获取UTF-8/Unicode格式的字符串长度
  • GetStringCritical、ReleaseStringCritical:用于直接返回/释放源字符串的指针,获取这个指针会导致暂停GC线程,如果GC线程暂停时又被其他线程触发GC的话,会出现系统死锁的阻塞调用
  • GetStringUTFRegion、GetStringRegion:用于获取UTF-8/Unicode格式字符指定范围内的内容,并会将源字符串复制到一个预先分配的缓冲区内

你可能感兴趣的:(android,android)