FFmpeg4Android:jni中c/c++调用java

7 FFmpeg4Android:jni中c/c++调用java

7.1 c/c++访问java属性

先来看一个函数定义:

JNIEXPORT jstring JNICALL Java_com_test_jni_TestNative_stringFromJNI
  (JNIEnv * env, jobject jobj) {
    return (*env)->NewStringUTF(env, "jni development.");
}

参数说明:
1)env:是一个结构体指针的指针,主要用来在C/C++中使用虚拟机的功能,比如说:访问Java方法、属性、创建Java对象、处理字符串等等。
2)jobj:是代表对象或类的结构体
如果native方法不是静态方法,jobj代表该方法所属的java对象
如果native方法是静态方法,jobj代表该方法所属Java类的class对象 //TestNative.class

c与java数据类型对应关系
Java基本数据类型与JNI数据类型的映射关系(在C/C++中用特定的类型来表示Java的数据类型),在反射访问java中属性时需要指定属性的签名,三者的对应关系如下:

java类型 c/c++类型 签名
boolean jboolean Z
byte jbyte B
char jchar C
short jshort S
int jint I
long jlong L
float jfloat F
double jdouble D
void void V

引用类型(对象)

java类型 c/c++类型 签名
String jstring Ljava/lang/String;
Object jobject Ljava/lang/Object;
byte[] jByteArray [B
Class jclass

对象类型以L开头,然后/分隔包的完整类型,后面再加";"。
数组类型以[开头,在加上数据元素类型的签名,如int[],签名就是[I;再比如int[][],签名就是[[I,object数组签名就是[Ljva/lang/Object;。

7.1.1 访问一般属性

如有java类JniTest:

package com.lzp.jni;

import java.util.Random;
import java.util.UUID;

public class JniTest {

    public String key = "walker";
    
    public static int count = 9;
    
    public native static String getStringFromC();
    
    public native String getString2FromC(int i);
    // 访问属性,返回修改之后的属性内容
    public native String accessField();
    
    public native void accessStaticField();
    
    public native void accessMethod();
    
    public native void accessStaticMethod();
    
    public static void main(String[] args) {
        String text = getStringFromC();
        System.out.println(text);
        JniTest t = new JniTest();
        text = t.getString2FromC(6);
        System.out.println(text);
        
        System.out.println("key修改前:"+t.key);
        t.accessField();
        System.out.println("key修改后:"+t.key);
        
        System.out.println("count修改前:"+count);
        t.accessStaticField();
        System.out.println("count修改后:"+count);
        
        t.accessMethod();
        t.accessStaticMethod();
    }
    
    // 产生指定范围的随机数
    public int genRandomInt(int max){
        System.out.println("genRandomInt 执行了...");
        return new Random().nextInt(max); 
    }
    
    // 产生UUID字符串
    public static String getUUID(){
        return UUID.randomUUID().toString();
    }
    
    // 加载动态库
    static{ 
        System.loadLibrary("jni_study");
    }
}

我们想在jni中访问该类中的属性key,并修改。

// 修改属性key的字符串
JNIEXPORT jstring JNICALL Java_com_tz_jni_TestNative_accessField(JNIEnv * env, jobject obj){
    //得到class
    jclass cls = (*env)->GetObjectClass(env, obj);
    //jfieldID
    //签名:类型的简称
    //属性,方法
    jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");

    //获取key属性的值
    //注意:key为基本数据类型,规则如下
    //(*env)->GetIntField(); (*env)->Get<Type>Field();
    jstring jstr = (*env)->GetObjectField(env, obj, fid);

    //jstring转为 C/C++字符串
    char *str = (*env)->GetStringUTFChars(env, jstr, NULL);
    //拼接字符串
    char text[50] = "super ";
    strcat(text,str);

    //拼接完成之后,从C字符串转为jstring
    jstr = (*env)->NewStringUTF(env, text);

    //修改key的属性
    //注意规则:Set<Type>Field
    (*env)->SetObjectField(env, obj, fid, jstr);

    return jstr;
}

7.1.2访问静态属性

//count属性+10
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_accessStaticField(JNIEnv * env, jobject obj) {
    //jclass
    jclass cls = (*env)->GetObjectClass(env, obj);
    //jfieldID
    jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
    //获取静态属性的值
    //规则:GetStatic<Type>Field
    jint count = (*env)->GetStaticIntField(env, cls, fid);
    count += 10;
    //修改属性的值
    //规则:SetStatic<Type>Field
    (*env)->SetStaticIntField(env, cls, fid, count);
}

7.2 c/c++访问java方法

回顾一下java反射,一般分为3个步骤:
1)加载calss(字节码),获取class的对象;
2)获取对应的方法或属性;
3)修改属性,或执行方法。
例如:
有java类Reflect:

public class Reflect {
    public void print(String s) {
        System. out.println(s);
    }
}

另一个Test类来反射此类,执行print(String)方法:

public class Test {
    public static void main(String[] args) {
        try {
            Class clazz = Test.class.getClassLoader().loadClass("Reflect");
            Method method = clazz.getDeclaredMethod("print", new Class[] {String.class});
            method.invoke(clazz.newInstance(), new String[] {"java反射"});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台输出:java反射

接下来看c/c++中的反射。
而对一个方法,其签名就是其参数类型签名和返回值类型签名的字符串,其形式如下:
**(类型签名1类型签名2...)返回值类型签名 **

下面看看两个例子:
方法 1):public string addTail(String tail, int index)
方法签名为:(Ljava/util/String;I)Ljava/util/String;
方法 2):public int addValue(int index, String value,int[] arr)
方法 签名为:(ILjava/util/String;[I)I

也可以通过命令,获取属性与方法签名。在class文件夹中,输入:javap -s -p 类名
1)访问java方法

// 借用java的api产生指定大小的随机数
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_accessMethod(JNIEnv * env, jobject obj) {
    //jclass
    jclass cls = (*env)->GetObjectClass(env, obj);
    //jmethodID
    jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
    //调用方法,产生了一个随机数
    //规则:Call<Type>Method 返回值类型
    jint random = (*env)->CallIntMethod(env, obj, mid, 200);

    //打印出来看看
    //FILE *fp = fopen("D://log.txt", "w");
    //int转为字符串
    char str[50];
    sprintf(str, "%d", random);
    fputs(str, fp);
    fclose(fp);
}

2)访问静态方法

//借用java api 产生一个UUID字符串,作为文件的名称
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_accessStaticMethod(JNIEnv * env, jobject cls){    
    //如果native方法为static,jobject为子类jclass的实例,也就是native方法所属的类的Class实例
    //jclass
    //jclass cls = (*env)->GetObjectClass(env, obj);

    //jmethodID
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;"); 

    //调用
    //规则:CallStatic<Type>Method
    jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);
    //jstring转为C字符串
    char *uuid_str = (*env)->GetStringUTFChars(env, uuid, NULL);
    char filename[100] = {0};
    sprintf(filename, "D://%s.txt", uuid_str);
    FILE *fp = fopen(filename, "w");
    fputs(uuid_str, fp);
    fclose(fp);
}

3)访问构造方法

//使用java.util.Date产生一个当前时间时间戳
JNIEXPORT jobject JNICALL Java_com_tz_jni_TestNative_accessConstructor(JNIEnv * env, jobject obj){
    //Date jclass
    jclass cls = (*env)->FindClass(env, "java/util/Date");
    //构造方法jmethodID
    jmethodID contructor_mid = (*env)->GetMethodID(env, cls, "<init>","()V");
    //实例化一个Date对象
    jobject date_obj = (*env)->NewObject(env, cls, contructor_mid);

    //调用getTime方法
    jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
    jlong time = (*env)->CallLongMethod(env, date_obj, mid);
    //jlong -> 字符串
    FILE *fp = fopen("D://log.txt", "w");
    char str[50];
    sprintf(str, "%lld", time);
    fputs(str, fp);
    fclose(fp);

    return date_obj;
}

4)调用父类的方法

JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_callNonvirtualMethod(JNIEnv * env, jobject obj) {
    //获取一个Man对象
    jclass cls = (*env)->GetObjectClass(env, obj);
    jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/tz/jni/Human;");
    jobject human_obj = (*env)->GetObjectField(env, obj, fid);

    //执行sayHi方法
    jclass human_cls = (*env)->FindClass(env, "com/tz/jni/Human");//注意:传父类的类名
    jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()Ljava/lang/String;");
    
    //执行子类覆盖的方法
    //jstring jstr = (*env)->CallObjectMethod(env, human_obj, mid);
    //执行父类的方法
    jstring jstr = (*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);

    //jstring->char*
    char * str = (*env)->GetStringUTFChars(env, jstr, NULL);
    FILE *fp = fopen("D://log.txt", "w");
    fputs(str, fp);
    fclose(fp);
}

7.3 实战:中文字符串乱码

JNIEXPORT jstring JNICALL Java_com_tz_jni_TestNative_chineseChars(JNIEnv * env, jobject obj,jstring jstr) {
    //java中传入的中文->C字符串
    /*char * str = (*env)->GetStringUTFChars(env, jstr, NULL);
    FILE *fp = fopen("D://log.txt", "w");
    fputs(str, fp);
    fclose(fp);*/

    //C字符串->jstring
    char *cstr = "我说中文";
    //jstring j_str = (*env)->NewStringUTF(env, cstr);
    //借用Java的转码API,返回GB2312中文编码字符串
    //使用这个构造方法,完成转码
    //public String(byte bytes[], String charsetName)
    //执行这个构造方法需要三个东西
    //1.jmethodID
    //2.byte数组(jbyteArray)参数
    //3.charsetName参数(jstring)

    //String类的jclass
    jclass str_cls = (*env)->FindClass(env, "java/lang/String");
    jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");
    
    //char * -> char[] ->jbyteArray -> jbyte * 
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(cstr));
    //bytes数组赋值
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(cstr), cstr);

    //charsetName="GB2312"
    jstring charsetName = (*env)->NewStringUTF(env, "GB2312");

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

推荐阅读更多精彩内容

  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 4,128评论 1 44
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,494评论 18 139
  • [拳头]坚持下去就会看见不一样的风景,收获更美更健康的自己! No.5 站桩第五天 2018.6.6早 站桩时间:...
    明媚坚持阅读 165评论 0 0
  • 在老师都不知道长什么样的情况下..... 在没有人监督的情况下..... 在上班的情况下..... 主动写作业.....
    yk___阅读 115评论 0 0
  • 我要去通渭 阳光穿过无边的苍凉有一个声音幸福安详清晨我挥动白云的翅膀夜晚我匍匐在你的胸膛。生命在干旱中那么倔强,时...
    一枚冰儿阅读 324评论 3 4