NDK学习笔记-JNI数据类型和属性方法的访问
JNI实现了C/C++与Java的相互访问,那么这篇文章就从C/C++访问Java开始说起
native函数说明
每个native函数,都至少有两个参数(JNIEnv *
和jclass
或jobject
)
- 当native方法为静态方法时,采用
jclass
,此时jclass
代表native方法所属类的class对象 - 当native方法为非静态时,使用
jobject
,此时jobject
代表native方法所属对象
JNI数据类型
基本数据类型
Java的基本数据类型与JNI数据类型成映射关系
Java类型
<=> JNI类型
<=> C类型
Java Language | Type Native | Type Description |
boolean | jboolean | unsigned 8 bits |
byte | jbyte | signed 8 bits |
char | jchar | unsigned 16 bits |
short | jshort | signed 16 bits |
int | jint | signed 32 bits |
long | jlong | signed 64 bits |
float | jfloat | 32 bits |
double | jdouble | 64 bits |
void | void | N/A |
引用数据类型
Java的引用类型与JNI的对应关系
Java引用类型 | JNI类型 |
String | jstring |
Object | jobject |
byte[] | jByteArray |
int[] | jIntArray |
String[] | jobjectArray |
Object[] | jobjectArray |
值得注意的是:普通数据类型的数组,其在JNI中的表现类似,表格中列举出两个,字符串数组属于Object数组,其表现形式一样
签名
Java Type | Type Signature |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
fully-qualified-class | L fully-qualified-class; |
type[] | [ type |
method type | (arg-types) ret-type |
说明:
Object:L
开头,然后以/
分隔包的完整类型,后面再加;
,比如String签名就是Ljava/lang/String;
Array:以[
开头,再加上数组元素类型的签名,比如int[]
签名就是[I
,再比如int[][]
的签名就是[[I
,Object数组的签名就是[Ljava/lang/Object;
使用javap -s -p 完整类名
可得到所有签名,需要在bin目录下
调用Java属性
访问非静态属性
在Java中存在
private String key = "jack";
public native String accessFiled(); //触发Java访问C/C++使其在底层修改并返回
非静态属性先得到class,再对其进行操作
Get和Set都有规律可循,GetField和SetField
JNIEXPORT jstring JNICALL Java_com_cj5785_jni_JniTest_accessField
(JNIEnv *env, jobject jobj)
{
//获取到JniTest.class
jclass cls = (*env)->GetObjectClass(env, jobj);
//属性名称,属性签名
jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
//获取key属性的值
jstring jstr = (*env)->GetObjectField(env, jobj, fid);
//jstring转化为C的字符串
char *c_str = (char *)(*env)->GetStringUTFChars(env, jstr, NULL);
//C语言处理:字符串拼接
char text[20] = "super ";
strcat(text, c_str);
//将C的字符串转化为jstring
jstring new_string = (*env)->NewStringUTF(env, text);
//修改key
(*env)->SetObjectField(env, jobj, fid, new_string);
//释放资源
(*env)->ReleaseStringUTFChars(env, jstr, c_str);
return new_string;
}
在Java中调用
package com.cj5785.jni;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
private String key = "test";
public native String accessField();
public static void main(String[] args) {
JniTest t = new JniTest();
System.out.println("修改前:" + t.key);
t.accessField();
System.out.println("修改后:" + t.key);
}
}
访问静态属性
在Java中存在
public static int count = 1;
public native void accessStaticField();
在native函数中修改
JNIEXPORT void JNICALL Java_com_cj5785_jni_JniTest_accessStaticField
(JNIEnv *env, jobject jobj)
{
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
jint count = (*env)->GetStaticIntField(env, cls, fid);
count++;
(*env)->SetStaticIntField(env, cls, fid, count);
}
在Java中访问
package com.cj5785.jni;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
public static int count = 1;
public native void accessStaticField();
public static void main(String[] args) {
JniTest t = new JniTest();
System.out.println("修改前:" + JniTest.count);
t.accessStaticField();
System.out.println("修改后:" + JniTest.count);
}
}
访问属性总结
如果为非静态属性,经历以下步骤
GetObjectClass
GetFieldID
Get<Type>Field
- 中间处理过程
Set<Type>Field
如果为静态属性,经历以下步骤
GetObjectClass
GetStaticFieldID
GetStatic<Type>Field
- 中间处理过程
SetStatic<Type>Field
调用Java方法
访问非静态方法
Java中存在
public native void accessMethod();
public int getRandomInt(int max) {
System.out.println("···getRandomInt run···");
return new Random().nextInt(max);
}
在native中调用
JNIEXPORT void JNICALL Java_com_cj5785_jni_JniTest_accessMethod
(JNIEnv *env, jobject jobj)
{
//jclass
jclass cls = (*env)->GetObjectClass(env, jobj);
//jmethodID
jmethodID mid = (*env)->GetMethodID(env, cls, "getRandomInt", "(I)I");
//Call<Type>Method
jint random = (*env)->CallIntMethod(env, jobj, mid, 100);
printf("%ld\n", random);
}
在Java中触发
package com.cj5785.jni;
import java.util.Random;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
public native void accessMethod();
public int getRandomInt(int max) {
System.out.println("···getRandomInt run···");
return new Random().nextInt(max);
}
public static void main(String[] args) {
JniTest t = new JniTest();
t.accessMethod();
}
}
访问静态方法
Java中存在
public native void accessStaticMethod();
public static String getUUID() {
System.out.println("···getUUID run···");
return UUID.randomUUID().toString();
}
在native中调用
JNIEXPORT void JNICALL Java_com_cj5785_jni_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj)
{
jclass cls = (*env)->GetObjectClass(env, jobj);
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
jstring jstr = (*env)->CallStaticObjectMethod(env, cls, mid);
char *uuid_str = (*env)->GetStringUTFChars(env, jstr, NULL);
printf("%s\n", uuid_str);
(*env)->ReleaseStringUTFChars(env, jstr, uuid_str);
}
在Java中触发
package com.cj5785.jni;
import java.util.UUID;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
public native void accessStaticMethod();
public static String getUUID() {
System.out.println("···getUUID run···");
return UUID.randomUUID().toString();
}
public static void main(String[] args) {
JniTest t = new JniTest();
t.accessStaticMethod();
}
}
访问方法总结
如果为非静态方法,经历以下步骤
GetObjectClass
GetMethodID
Call<Type>Method
- 处理过程
如果为静态属性,经历以下步骤
GetObjectClass
GetStaticMethodID
GetStatic<Type>Method
- 处理过程
访问构造方法
使用Date类的getTime()方法,产生当前时间戳
在native中调用Date的getTime方法
JNIEXPORT jobject JNICALL Java_com_cj5785_jni_JniTest_accessConstructor
(JNIEnv *env, jobject jobj)
{
jclass cls = (*env)->FindClass(env, "java/util/Date");
jmethodID construcyor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
jobject date_obj = (*env)->NewObject(env, cls, construcyor_mid);
jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
jlong time = (*env)->CallLongMethod(env, date_obj, mid);
printf("%lld\n", time);
return date_obj;
}
在Java中触发
package com.cj5785.jni;
import java.util.Date;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
public native Date accessConstructor();
public static void main(String[] args) {
JniTest t = new JniTest();
t.accessConstructor();
}
}
访问构造方法,分成以下几个步骤
- FindClass
- GetMethodID:初始化
- NewObject
- GetMethodID
- CallMethod
调用父类方法
在Java中存在Person和Student两个类
public class Person {
public void say() {
System.out.println("Person Class");
}
}
public class Student extends Person {
@Override
public void say() {
System.out.println("Student Class");
}
}
在native中调用子类方法,获取父类方法
JNIEXPORT void JNICALL Java_com_cj5785_jni_JniTest_accessNonvirtualMethod
(JNIEnv *env, jobject jobj)
{
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "person", "Lcom/cj5785/jni/Person;");
jobject person_obj = (*env)->GetObjectField(env, jobj, fid);
jclass person_cls = (*env)->FindClass(env, "com/cj5785/jni/Person");
jmethodID mid = (*env)->GetMethodID(env, person_cls, "say", "()V");
//执行子类方法
(*env)->CallObjectMethod(env, person_obj, mid);
//执行父类方法
(*env)->CallNonvirtualObjectMethod(env, person_obj, person_cls, mid);
}
在Java中触发
package com.cj5785.jni;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
public Person person = new Student();
public native void accessNonvirtualMethod();
public static void main(String[] args) {
JniTest t = new JniTest();
t.accessNonvirtualMethod();
}
}
调用父类方法步骤
- GetObjectClass:获取class对象
- GetFieldID:获取属性(对象)
- GetField:获取
- FindClass:查找父类
- GetMethodID:获取方法
- CallMethod(子类方法)或CallNonvirtualMethod(父类方法)
字符串乱码问题
在Java存在
public native String chineseChar(String str);
在native中产生的字符串,当返回时可能会产生乱码问题,这是由于编码格式不同造成的
在Java中传入字符串,那么在native如果不处理,直接返回,那么不会出现乱码
JNIEXPORT jstring JNICALL Java_com_cj5785_jni_JniTest_chineseChar
(JNIEnv *env, jobject jobj, jstring jstr)
{
//使用GetStringUTFChars返回
char *c_str = (*env)->GetStringUTFChars(env, jstr, NULL);
return (*env)->NewStringUTF(env, c_str);
}
但如果对其进行过处理,那么返回的中文字符则会出现乱码问题
以下两例,一个是在输入的字符串做了追加字符,一个是做了新字符串返回,都存在中文,返回的结果都出现了乱码
JNIEXPORT jstring JNICALL Java_com_cj5785_jni_JniTest_chineseChar
(JNIEnv *env, jobject jobj, jstring jstr)
{
//使用GetStringUTFChars返回
//char *c_str = (*env)->GetStringUTFChars(env, jstr, NULL);
//strcat(c_str, "追加");
//return (*env)->NewStringUTF(env, c_str);
//使用C转为jstring,然后返回
char *c_str = "native:中文测试";
return (*env)->NewStringUTF(env, c_str);
}
这种情况,有两种解决办法,一种是在C中寻找字符串转码的工具,另一种是直接调用Java的转码工具,前者难度较大,在这里采用后者
JNIEXPORT jstring JNICALL Java_com_cj5785_jni_JniTest_chineseChar
(JNIEnv *env, jobject jobj, jstring jstr)
{
//使用Java的字符串转码工具
char *c_str = "native:中文测试";
//获取jmethod
jclass strcls = (*env)->FindClass(env, "java/lang/String");
jmethodID constructor_mid = (*env)->GetMethodID(env, strcls, "<init>", "([BLjava/lang/String;)V");
//C数组转JNI数组
jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
//数组赋值
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);
//设置字符编码
jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
//调用构造方法,返回编码后的jstring
return (*env)->NewObject(env, strcls, constructor_mid, bytes, charsetName);
}
在Java中触发
package com.cj5785.jni;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
public native String chineseChar(String str);
public static void main(String[] args) {
JniTest t = new JniTest();
System.out.println(t.chineseChar("中文测试"));
}
}
传入数组的处理
传入int数组,并对其排序
Java中存在native方法
public native void sortArray(int[] array);
在native函数中处理
int compare(int *a, int *b)
{
return (*a - *b);
}
JNIEXPORT void JNICALL Java_com_cj5785_jni_JniTest_sortArray
(JNIEnv *env, jobject jobj, jintArray array)
{
//jintArray转化为C int数组
jint *elems = (*env)->GetIntArrayElements(env, array, NULL);
//获取数组长度
int len = (*env)->GetArrayLength(env, array);
//排序
qsort(elems, len, sizeof(jint), compare);
//刷新数组
(*env)->ReleaseIntArrayElements(env, array, elems, JNI_COMMIT);
}
关于数组刷新的同步问题
mode | 更新Java数组 | 释放C/C++数组 |
`0` | `√` | `√` |
`JNI_ABORT` | `×` | `√` |
`JNI_COMMIT` | `√` | `×` |
在Java中调用
```java
package com.cj5785.jni;
import java.util.Arrays;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
public native void sortArray(int[] array);
public static void main(String[] args) {
JniTest t = new JniTest();
int[] arr = {5,12,3,6,9,25,1};
System.out.print("排序前:" + Arrays.toString(arr) + "\n");
t.sortArray(arr);
System.out.print("排序后:" + Arrays.toString(arr) + "\n");
}
}
#### 返回数组
Java中存在native方法
```java
public native int[] getArray(int len);
在native中生成数组
JNIEXPORT jintArray JNICALL Java_com_cj5785_jni_JniTest_getArray
(JNIEnv *env, jobject jobj, jint len)
{
//生成jint数组
jintArray jint_arr = (*env)->NewIntArray(env, len);
//获取数组元素
jint *elems = (*env)->GetIntArrayElements(env, jint_arr, NULL);
//为数组赋值
int i = 0;
for (; i < len; i++)
{
elems[i] = i;
}
//同步数组
(*env)->ReleaseIntArrayElements(env, jint_arr, elems, 0);
//返回生成的数组
return jint_arr;
}
在Java中调用生成数组的方法
package com.cj5785.jni;
import java.util.Arrays;
public class JniTest {
static {
System.loadLibrary("JNITest");
}
public native int[] getArray(int len);
public static void main(String[] args) {
JniTest t = new JniTest();
int newArr[] = t.getArray(10);
System.out.println(Arrays.toString(newArr));
}
}
NDK学习笔记-JNI数据类型和属性方法的访问的更多相关文章
- NDK学习笔记-JNI多线程
前面讲到记录到ffmpeg音视频解码的时候,采用的是在主线程中进行操作,这样是不行的,在学习了POSIX多线程操作以后,就可以实现其在子线程中解码了,也可以实现音视频同步了 简单示例 在native实 ...
- NDK学习笔记-JNI的异常处理与缓存策略
在使用JNI的时候,可能会产生异常,此时就需要对异常进行处理 异常处理 JNI抛出Throwable异常,在Java层可以用Throwable捕捉 而在C只有清空异常这种处理 但如果在JNI中通过Th ...
- NDK学习笔记-JNI开发流程
JNI(Java Native Interface)Java本地化接口,Java调用C/C++,C/C++调用Java的一套API接口 实现步骤 在Java源文件中编写native方法 public ...
- NDK学习笔记-JNI的引用
JNI中的引用意在告知虚拟机何时回收一个JNI变量 JNI引用变量分为局部引用和全局引用 局部引用 局部引用,通过DeletLocalRef手动释放对象 原因 访问一个很大的Java对象,使用之后还用 ...
- Android JNI和NDK学习(06)--JNI的数据类型(转)
本文转自:http://www.cnblogs.com/skywang12345/archive/2013/05/23/3094037.html 本文介绍JNI的数据类型.NDK中关于JNI数据类型的 ...
- SQL反模式学习笔记6 支持可变属性【实体-属性-值】
目标:支持可变属性 反模式:使用泛型属性表.这种设计成为实体-属性-值(EAV),也可叫做开放架构.名-值对. 优点:通过增加一张额外的表,可以有以下好处 (1)表中的列很少: (2)新增属性时,不需 ...
- Java程序猿的JavaScript学习笔记(9—— jQuery工具方法)
计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...
- 前端学习笔记汇总(之merge方法)
学习笔记 关于Jquery的merge方法 话不多说,先上图 使用jquery时,其智能提示如上,大概意思就是合并first和second两个数组,得到的结果是first+(second去重后的结果) ...
- 《深入Java虚拟机学习笔记》- 第19章 方法的调用与返回
<深入Java虚拟机学习笔记>- 第19章 方法的调用与返回
随机推荐
- setTimeout设置为0 为啥不能立马执行
setTimeout(function(){}, timer) 是指延时执行.第一个参数是回调函数,第二个参数是指延时多久执行回调函数. setTimeout(function(){console.l ...
- IDEA 安装与破解(亲测有效)
本文转载:https://blog.csdn.net/g_blue_wind/article/details/74380483 根据以下的流程,顺利安装了最新版本的idea企业版. IDEA 全称 I ...
- 查看nginx服务器状态
编译安装时使用--with-http_stub_status_module开启状态页面模块 [root@proxy ~]# yum -y install gcc pcre-devel openssl- ...
- nginx反向代理tomcat 时,出现https redirect后变成http的问题解决方法
需要修改两个配置 1.nginx配置 location / { proxy_pass http://test-server; proxy_set_header Host $host; proxy_se ...
- Luogu P1450 [HAOI2008]硬币购物 背包+容斥原理
考虑如果没有个数的限制,那么就是一个完全背包,所以先跑一个完全背包,求出没有个数限制的方案数即可. 因为有个数的限制,所以容斥一下:没有1个超过限制的方案=至少0个超过限制-至少1个超过限制+至少2个 ...
- NetMQ介绍
NetMQ 是 ZeroMQ的C#移植版本. 一.ZeroMQ ZeroMQ(Ø)是一个轻量级的消息内核,它是对标准socket接口的扩展.它提供了一种异步消息队列,多消息模式,消息过滤(订阅),对 ...
- 【java设计模式】-02工厂模式
工厂模式简述 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建对象时不会对客 ...
- Python之python简介
一.Python的优缺点 优点: 1.Python的定位是“优雅”.“明确”.“简单”,所以Python程序看上去总是简单易懂,初学者学Python,不但入门容易,而且将来深入下去,可以编写那些非常非 ...
- Docker容器常用命令汇总
Docker常用命令总结如下: # 查看docker详细信息 docker info # 获取当前节点所有容器 docker ps -a # 管理容器生命周期 docker [start|stop|r ...
- 使用 suspend 和 resume 暂停和恢复线程
suspend 和 resume 的使用 在 Thread 类中有这样两个方法:suspend 和 resume,这两个方法是成对出现的. suspend() 方法的作用是将一个线程挂起(暂停), r ...