java 与jni转码_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)->GetField();

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的属性

//注意规则:SetField

(*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");

//获取静态属性的值

//规则:GetStaticField

jint count = (*env)->GetStaticIntField(env, cls, fid);

count += 10;

//修改属性的值

//规则:SetStaticField

(*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");

//调用方法,产生了一个随机数

//规则:CallMethod 返回值类型

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;");

//调用

//规则:CallStaticMethod

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, "","()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, "", "([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);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值