JNI 笔记

最近在看《深入理解 Android: 卷 I》,Framework 层要和 Native 层打交道,一开始肯定得介绍 JNI;

13 年项目中用到 WebP 的时候虽然接触过 JNI,但没有系统学习过,所以这里算是做个笔记。

JNI方法注册:

静态注册

  1. 对所有声明了 native 方法的 Java 类,运行 javah -o output packageName.className 生成 .h 头文件;

  2. 生成的 JNI 函数名带有包路径。

动态注册

  1. 通过 JNINativeMethod 结构体定义 Java native 方法和 JNI 中函数的对应关系;

  2. 调用 AndroidRunTime::registerNativeMethods() 函数完成注册,该函数内部调用 (*env)->RegisterNatives() 函数;

  3. Java 层调用 System.loadLibrary(),动态注册自动在 JNI_OnLoad() 函数中进行的。

JNINativeMethod类型定义:

typedef struct {
	const char* name; //Native函数名,不带包名
	const char* signature; //方法签名,格式:“(参数类型1,参数类型2...)返回类型”
	void* funcPtr; //JNI层对应的函数指针
} JNINativeMethod;

JNIEnv:

  • 实际上就是封装了一些 JNI 系统函数,通过这些函数可以调用 Java 函数、操作 jobject 对象。

  • 它是线程相关的,通过调用 JavaVM 的 AttachCurrentThread() 函数可以得到当前线程的 JNIEnv 对象;在后台线程退出前,也需要调用 DetachCurrentThread() 函数来释放资源;

  • 得到 Java 类、方法、属性: env->FindClass()env->GetMethodID()env->GetFieldID();

  • 调用 Java 方法: Call<Type>Method()CallStatic<Type>Method()

  • 读取 Java 属性值:Get<Type>Field()Set<Type>Field();

  • 字符串操作:

    • 创建 jstring: env->NewString()env->NewStringUTF();

    • 字符操作:env->GetStringChars()env->GetStringUTFChars()env->ReleaseStringChars()env->ReleaseStringUTFChars();

JNI 引用类型:

  • Local Reference: 一旦 JNI 层函数返回,jobject 对象就会被回收;

  • Global Reference: 全局(静态)引用,如不调用 env->DeleteGlobalRef() 主动释放,永远不会被回收;

  • Weak Global Reference: 使用前需要调用 env->ISSameObject() 判断是否被回收;

JNI 异常处理:

  • JNI 层出错不会中断 native 代码运行,直到返回 Java 层后,虚拟机才会抛出异常;

  • JNI 层出现异常后,需要做资源释放;

  • JNIEnv 提供的异常处理函数:

    • env->ExceptionOccured(): 判断是否发生异常;

    • env->ExceptionClear(): 清理当前 JNI 层中发生的异常;

    • env->ThrowNew(): 向 Java 层抛出异常;