Android NDK基础11:JNI数据类型_C调用Java

JNI数据类型

jclass和jobject

//每个native函数,都至少有两个参数(JNIEnv*, jclass或者jobject)
//1)当native方法为静态方法时:
//jclass 代表native方法所属类的class对象(JniTest.class)
//2)当native方法为非静态方法时:
//jobject 代表native方法所属的对象

基本数据类型

Java基本数据类型与JNI数据类型的映射关系

Java 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

引用类型(对象)

//String jstring
//Object jobject
//Class jobject
//数组,基本数据类型的数组
//boolean[] jbooleanArray
//byte[] jbyteArray
//char[] jcharArray
//short[] jshortArray
//int[] jintArray
//long[] jlongArray
//float[] jfloatArray
//double[] jdoubleArray
//对象数组
//String[] jobjectArray
//object[] jobjectArray

C/C++访问Java的成员

类型签名

Type Signature Java Type
Z boolean
B byte
C char
S short
I int
J long
F float
D double
Ljava/lang/String String
L{fully-qualified-class}; fully-qualified-class
[{type} type[]

访问属性

#define _CRT_SECURE_NO_WARNINGS
#include "cn_appblog_jni_JniTest.h"
#include <string.h>

//修改属性key
JNIEXPORT jstring JNICALL Java_cn_appblog_jni_JniTest_accessField
  (JNIEnv *env, jobject jobj) {
    //jobj是t对象,JniTest.class
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jfieldID
    //属性名称,属性签名
    jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");    

    //jason >> super jason
    //获取key属性的值
    //Get<Type>Field
    jstring jstr = (*env)->GetObjectField(env, jobj, fid);    
    printf("jstr:%#x\n", &jstr);

    //jstring -> c字符串
    //第三个参数isCopy 是否复制(true代表复制,false不复制)
    char *c_str = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE);
    //拼接得到新的字符串
    char text[20] = "super ";
    strcat(text, c_str);

    //c字符串 ->jstring
    jstring new_jstr = (*env)->NewStringUTF(env, text);

    //修改key
    //Set<Type>Field
    (*env)->SetObjectField(env, jobj, fid, new_jstr);

    printf("new_jstr:%#x\n", &new_jstr);

    return new_jstr;
}

访问静态属性

JNIEXPORT void JNICALL Java_cn_appblog_jni_JniTest_accessStaticField
  (JNIEnv *env, jobject jobj) {
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jfieldID
    jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
    //GetStatic<Type>Field
    jint count = (*env)->GetStaticIntField(env, cls, fid);
    count++;
    //修改
    //SetStatic<Type>Field
    (*env)->SetStaticIntField(env, cls, fid, count);
}

访问对象方法

//获取指定类所有属性和方法的签名
> javap -s -p cn.appblog.jni.JniTest
JNIEXPORT void JNICALL Java_cn_appblog_jni_JniTest_accessMethod
  (JNIEnv *env, jobject jobj) {
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jmethodID
    jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
    //调用
    //Call<Type>Method
    jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
    printf("random num:%ld", random);

    //.....
}

访问静态方法

JNIEXPORT void JNICALL Java_cn_appblog_jni_JniTest_accessStaticMethod
  (JNIEnv *env, jobject jobj) {
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jmethodID    
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");

    //调用
    //CallStatic<Type>Method
    jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);

    //随机文件名称 uuid.txt
    //jstring -> char*
    //isCopy JNI_FALSE,代表java和c操作的是同一个字符串
    char *uuid_str = (*env)->GetStringUTFChars(env, uuid, JNI_FALSE);
    //拼接
    char filename[100];
    sprintf(filename, "D://%s.txt", uuid_str);
    FILE *fp = fopen(filename,"w");
    fputs("I love Joe", fp);
    close(fp);
}

访问构造方法

//使用java.util.Date产生一个当前的时间戳
JNIEXPORT jobject JNICALL Java_cn_appblog_jni_JniTest_accessConstructor
  (JNIEnv *env, jobject jobj) {
    jclass cls = (*env)->FindClass(env, "java/util/Date");
    //jmethodID
    jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
    //实例化一个Date对象
    jobject date_obj = (*env)->NewObject(env, cls, constructor_mid);
    //调用getTime方法
    jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
    jlong time = (*env)->CallLongMethod(env, date_obj, mid);

    printf("\ntime:%lld\n", time);

    return date_obj;
}

Java层调用

public class JniTest {

    public String key = "joe";

    public static int count = 9;

    //访问属性,返回修改之后的属性内容
    public native String accessField();

    public native void accessStaticField();

    public native void accessMethod();

    public native void accessStaticMethod();

    public native Date assessConstructor();

    public static void main(String[] args) {
        JniTest t = new JniTest();

        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();
        t.assessConstructor();
    }

    //产生指定范围的随机数
    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("JniTest");
    }

}

调用父类的方法

public class Human {

    @Override
    public void sayHi() {
        System.out.println("父类:sayHi");
    }

}

public class Man extends Human {

    @Override
    public void sayHi() {
        super.sayHi();
        System.out.println("子类:sayHi");
    }

}

public class JniTest {

    public Human human = new Man();

    public native Date assessNonvirtualMethod();

    public static void main(String[] args) {
        JniTest t = new JniTest();
        t.assessNonvirtualMethod();
    }

    //加载动态库
    static {    
        System.loadLibrary("JniTest");
    }

}
JNIEXPORT void JNICALL Java_cn_appblog_jni_JniTest_accessNonvirtualMethod
  (JNIEnv *env, jobject jobj) {
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //获取man属性(对象)
    jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcc/androidios/jni/Human;");
    //获取
    jobject human_obj = (*env)->GetObjectField(env, jobj, fid);

    //执行sayHi方法
    jclass human_cls = (*env)->FindClass(env, "cc/androidios/jni/Human"); //注意:传父类的名称
    jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()V");

    //执行
    //(*env)->CallObjectMethod(env, human_obj, mid);
    //调用的父类的方法
    (*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);
}

中文问题

public class JniTest {

    public native String chineseChars(String content);

    public static void main(String[] args) {
        JniTest t = new JniTest();
        t.chineseChars("中文");
    }

    //加载动态库
    static {    
        System.loadLibrary("JniTest");
    }

}

JNIEXPORT jstring JNICALL Java_cn_appblog_jni_JniTest_chineseChars
  (JNIEnv *env, jobject jobj, jstring content) {
    //输出
    //char *c_str = (*env)->GetStringUTFChars(env, content, JNI_FALSE);
    //printf("%s\n", c_str);

    //c -> jstring
    char *c_str = "马蓉与宋江";
    //char c_str[] = "马蓉与宋喆";
    //jstring jstr = (*env)->NewStringUTF(env, c_str);
    //执行String(byte bytes[], String charsetName)构造方法需要的条件
    //1.jmethodID
    //2.byte数组
    //3.字符编码jstring

    jclass str_cls = (*env)->FindClass(env, "java/lang/String");
    jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");

    //jbyte -> char 
    //jbyteArray -> char[]
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
    //byte数组赋值
    //0->strlen(c_str),从头到尾
    //对等于,从c_str这个字符数组,复制到bytes这个字符数组
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);

    //字符编码jstring
    jstring charsetName = (*env)->NewStringUTF(env, "GB2312");

    //调用构造函数,返回编码之后的jstring
    return (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/25/android-ndk-basic-jni-data-type-c-calls-java/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
Android NDK基础11:JNI数据类型_C调用Java
JNI数据类型 jclass和jobject //每个native函数,都至少有两个参数(JNIEnv*, jclass或者jobject) //1)当native方法为静态方法时: //jclass 代表native方法……
<<上一篇
下一篇>>
文章目录
关闭
目 录