JNI回调Java
jclass、jobject、jmethodID 和 jfieldID
jni回调java是通过反射来实现的,这些反射的接口都定义在 JNIEnv中。
jclass
java类引用 可以通过FindClass来获取
const char* className = "pri/tool/ffmediaplayer/MediaPlayer";
jclass clazz;
clazz = env->FindClass(className);
另一种获取方法是已经知道对象/实例的引用,通过GetObjectClass来获取
jclass clazz = env->GetObjectClass(thiz);
jobject
实例/对象的引用 两种应用场景: 第一种情况是Java层将实例传递下来。 第二种情况是native层拿到jclass对象后,创建jobject实例,并将实例返回给java层,以如何创建一个java层的ArrayList为例:
jclass list_class = env->FindClass("java/util/ArrayList");
if (list_class == NULL) {
return 0;
}
jmethodID list_costruct = env->GetMethodID(list_class , "<init>","()V"); //获得得构造函数Id
jobject list_obj = env->NewObject(list_class , list_costruct); //创建一个Arraylist集合对象
分3步:1 获取类应用jclass;2 获取构造函数jmethodID; 3 通过NewObject创建对象的引用. <init>是构造函数统一的方法名,()V 为函数签名
jmethodID
方法的id,通过jmethodID 可以操作java类对应的方法。 获取方法id的接口为
jmethodID GetMethodID(JNIEnv *env,jclass clazz,const char*name,const char* sig);
调用非static java方法的函数为
NativeType Call<type>Method(JNIEnv *env,jobject obj,jmethodID methodID,...);
同样以上面ArrayList为例,说明下如何使用
//创建一个Arraylist集合对象
jobject listFrameRate_obj = env->NewObject(list_class , list_costruct);
//获取ArrayList的add方法
jmethodID list_add = env->GetMethodID(list_class, "add", "(Ljava/lang/Object;)Z");
//获取自定义类的jclass和构造方法,该自定义类作为ArrayList模板类成员
jclass frameRate_cls = env->FindClass("com/iview/camera/module/Data/FrameRate");//获得Student类引用
jmethodID frameRate_costruct = env->GetMethodID(frameRate_cls , "<init>", "(II)V");
for (FrameRate frameRate : frame.frameRate) {
//创建自定义类,后面两个参数为构造函数的方法
jobject frameRate_obj = env->NewObject(frameRate_cls, frameRate_costruct, frameRate.numerator, frameRate.denominator);
//调用ArrayList的add方法将 frameRate_obj 添加到ArrayList中
env->CallBooleanMethod(listFrameRate_obj , list_add , frameRate_obj);
}
jfieldID
java类属性的id 可以通过以下函数来获取
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
比如MediaPlayer中
fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
获取到的是java层的
private long mNativeContext; // accessed by native method
也可以使用对应的Get<type>Field来获取
jobject GetObjectField(jobject obj, jfieldID fieldID)
jboolean GetBooleanField(jobject obj, jfieldID fieldID)
jbyte GetByteField(jobject obj, jfieldID fieldID)
jchar GetCharField(jobject obj, jfieldID fieldID)
jshort GetShortField(jobject obj, jfieldID fieldID)
jint GetIntField(jobject obj, jfieldID fieldID)
jlong GetLongField(jobject obj, jfieldID fieldID)
jfloat GetFloatField(jobject obj, jfieldID fieldID)
jdouble GetDoubleField(jobject obj, jfieldID fieldID)
修改fieldID可以通过对应的Set<type>Field来设置
void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
void SetCharField(jobject obj, jfieldID fieldID, jchar value)
void SetShortField(jobject obj, jfieldID fieldID, jshort value)
void SetIntField(jobject obj, jfieldID fieldID, jint value)
void SetLongField(jobject obj, jfieldID fieldID, jlong value)
void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
关于Static和非Static
我们知道类的方法和属性有static和非static之分,对应的jni反射的接口也有差异 常见的获取方法有:
jfieldID GetFieldID(jclass clazz, const char *name, const char *sig);
jmethodID GetMethodID(jclass clazz, const char *name, const char *sig);
jfieldID GetStaticFieldID(jclass clazz, const char *name, const char *sig);
jmethodID GetStaticMethodID( jclass clazz,const char *name, const char *sig);
常见的设置方法
void Set<type>Field(jobject clazz,jfieldID fieldID,NativeType value);
void SetStatic<type>Field(jclass clazz,jfieldID fieldID,NativeType value);
NativeType Call<type>Method(jobject clazz,jmethodID methodID,...);
NativeType CallStatic<type>Method(jclass clazz,jmethodID methodID,...);
设置的函数除了方法名不一样外, static 的参数用了jclass, 非static用的是jobject,jobject必须是已经实例化的引用
常见问题解答:为什么 FindClass 找不到我的类?
确保类名称字符串的格式正确无误。JNI 类名称以软件包名称开头,并用斜线分隔,例如 java/lang/String。如果您要查找某个数组类,则需要以适当数量的英文方括号开头,并且还必须用“L”和“;”将该类括起来,因此 String 的一维数组将是 [Ljava/lang/String;。如果您要查找内部类,请使用“$”而不是“.”。通常,在 .class 文件上使用 javap 是查找类的内部名称的好方法。 如果类名称形式正确,则可能是您遇到了类加载器问题。FindClass 需要在与您的代码关联的类加载器中启动类搜索。它会检查调用堆栈,如下所示:
Foo.myfunc(Native Method)
Foo.main(Foo.java:10)
最顶层的方法是 Foo.myfunc。FindClass 会查找与 Foo 类关联的 ClassLoader 对象并使用它。
采用这种方法通常会完成您想要执行的操作。如果您自行创建线程(可能通过调用 pthread_create,然后使用 AttachCurrentThread 进行附加),可能会遇到麻烦。现在您的应用中没有堆栈帧。如果从此线程调用 FindClass,JavaVM 会在“系统”类加载器(而不是与应用关联的类加载器)中启动,因此尝试查找特定于应用的类将失败。
您可以通过以下几种方法来解决此问题:
- 在 JNI_OnLoad 中执行一次 FindClass 查找,然后缓存类引用以供日后使用。在执行 JNI_OnLoad 过程中发出的任何 FindClass 调用都会使用与调用 System.loadLibrary 的函数关联的类加载器(这是一条特殊规则,用于更方便地进行库初始化)。如果您的应用代码要加载库,FindClass 会使用正确的类加载器。
- 通过声明原生方法来获取 Class 参数,然后传入 Foo.class,从而将类的实例传递给需要它的函数。
- 在某个便捷位置缓存对 ClassLoader 对象的引用,然后直接发出 loadClass 调用。这需要花费一些精力来完成。
参考 https://www.jianshu.com/p/67081d9b0a9c https://developer.android.google.cn/training/articles/perf-jni#64-bit-considerations
- 游戏服务器之多线程发送(下)
- 【团队分享】手机QQ:升级iOS8.3后,发图就崩,为哪般?
- golang 字符串操作实例
- 【团队分享】刀锋铁骑:常见Android Native崩溃及错误原因
- OpenShift企业版安装:单Master集群
- http线程池的设计与实现(c++)
- iOS崩溃堆栈符号化,定位问题分分钟搞定!
- Duang~ Android堆栈慘遭毁容?精神哥揭露毁容真相!
- Java学习笔记第一篇:坦克大战游戏
- 腾讯Bugly Unity3D Plugin使用指南
- 远丰集团旗下CMS疑有官方后门
- 前端黑魔法之远程控制地址栏
- 信息收集利器:ZoomEye
- go sync.Mutex 设计思想与演化过程 --转
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释