JNI回调Java

Android FFmpeg专题结构

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270