02.Jni开发流程_C/C++调用java

(创建于2017/11/18)

提示:
生成一个类中所有属性成员签名的方法如下:
1.进入这个类class文件所在的路径:如JniUtils这个类在d盘下 D:\application\java\eclipse-workspace\TestJni\bin\com\renzhenming\bsdiff>
2.执行命令D:\application\java\eclipse-workspace\TestJni\bin\com\renzhenming\bsdiff>javap -p -s JniUtils
3.会得到结果:descriptor所对应的就是签名

Compiled from "JniUtils.java"
public class com.renzhenming.bsdiff.JniUtils {
  public static int key;
    descriptor: I
  static {};
    descriptor: ()V

  public com.renzhenming.bsdiff.JniUtils();
    descriptor: ()V

  public native void changeIntKey();
    descriptor: ()V

  public native void accessMethod();
    descriptor: ()V

  public native void accessStaticMethod();
    descriptor: ()V

  public int getRandomInt(int);
    descriptor: (I)I

  public static java.lang.String getUUId();
    descriptor: ()Ljava/lang/String;
}

生成系统类签名的方法,以java.util.Date为例

直接进入命令行,输入javap -s -p 完整类名

C:\Users\renzhenming>javap -s -p java.util.Date
Compiled from "Date.java"
public class java.util.Date implements java.io.Serializable, java.lang.Cloneable, java.lang.Comparable {
  private static final sun.util.calendar.BaseCalendar gcal;
    descriptor: Lsun/util/calendar/BaseCalendar;
  private static sun.util.calendar.BaseCalendar jcal;
    descriptor: Lsun/util/calendar/BaseCalendar;
  private transient long fastTime;
    descriptor: J
  private transient sun.util.calendar.BaseCalendar$Date cdate;
    descriptor: Lsun/util/calendar/BaseCalendar$Date;
  private static int defaultCenturyStart;
    descriptor: I
  private static final long serialVersionUID;
    descriptor: J
  private static final java.lang.String[] wtb;
    descriptor: [Ljava/lang/String;
  private static final int[] ttb;
    descriptor: [I
  public java.util.Date();
    descriptor: ()V

  public java.util.Date(long);
    descriptor: (J)V

  public java.util.Date(int, int, int);
    descriptor: (III)V

  public java.util.Date(int, int, int, int, int);
    descriptor: (IIIII)V

  public java.util.Date(int, int, int, int, int, int);
    descriptor: (IIIIII)V

  public java.util.Date(java.lang.String);
    descriptor: (Ljava/lang/String;)V

  public java.lang.Object clone();
    descriptor: ()Ljava/lang/Object;

  public static long UTC(int, int, int, int, int, int);
    descriptor: (IIIIII)J

  public static long parse(java.lang.String);
    descriptor: (Ljava/lang/String;)J

  public int getYear();
    descriptor: ()I

  public void setYear(int);
    descriptor: (I)V

  public int getMonth();
    descriptor: ()I

  public void setMonth(int);
    descriptor: (I)V

  public int getDate();
    descriptor: ()I

  public void setDate(int);
    descriptor: (I)V

  public int getDay();
    descriptor: ()I

  public int getHours();
    descriptor: ()I

  public void setHours(int);
    descriptor: (I)V

  public int getMinutes();
    descriptor: ()I

  public void setMinutes(int);
    descriptor: (I)V

  public int getSeconds();
    descriptor: ()I

  public void setSeconds(int);
    descriptor: (I)V

  public long getTime();
    descriptor: ()J

  private final long getTimeImpl();
    descriptor: ()J

  public void setTime(long);
    descriptor: (J)V

  public boolean before(java.util.Date);
    descriptor: (Ljava/util/Date;)Z

  public boolean after(java.util.Date);
    descriptor: (Ljava/util/Date;)Z

  public boolean equals(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Z

  static final long getMillisOf(java.util.Date);
    descriptor: (Ljava/util/Date;)J

  public int compareTo(java.util.Date);
    descriptor: (Ljava/util/Date;)I

  public int hashCode();
    descriptor: ()I

  public java.lang.String toString();
    descriptor: ()Ljava/lang/String;

  private static final java.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String);
    descriptor: (Ljava/lang/StringBuilder;Ljava/lang/String;)Ljava/lang/StringBuilder;

  public java.lang.String toLocaleString();
    descriptor: ()Ljava/lang/String;

  public java.lang.String toGMTString();
    descriptor: ()Ljava/lang/String;

  public int getTimezoneOffset();
    descriptor: ()I

  private final sun.util.calendar.BaseCalendar$Date getCalendarDate();
    descriptor: ()Lsun/util/calendar/BaseCalendar$Date;

  private final sun.util.calendar.BaseCalendar$Date normalize();
    descriptor: ()Lsun/util/calendar/BaseCalendar$Date;

  private final sun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date);
    descriptor: (Lsun/util/calendar/BaseCalendar$Date;)Lsun/util/calendar/BaseCalendar$Date;

  private static final sun.util.calendar.BaseCalendar getCalendarSystem(int);
    descriptor: (I)Lsun/util/calendar/BaseCalendar;

  private static final sun.util.calendar.BaseCalendar getCalendarSystem(long);
    descriptor: (J)Lsun/util/calendar/BaseCalendar;

  private static final sun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date);
    descriptor: (Lsun/util/calendar/BaseCalendar$Date;)Lsun/util/calendar/BaseCalendar;

  private static final synchronized sun.util.calendar.BaseCalendar getJulianCalendar();
    descriptor: ()Lsun/util/calendar/BaseCalendar;

  private void writeObject(java.io.ObjectOutputStream) throws java.io.IOException;
    descriptor: (Ljava/io/ObjectOutputStream;)V

  private void readObject(java.io.ObjectInputStream) throws java.io.IOException, java.lang.ClassNotFoundException;
    descriptor: (Ljava/io/ObjectInputStream;)V

  public static java.util.Date from(java.time.Instant);
    descriptor: (Ljava/time/Instant;)Ljava/util/Date;

  public java.time.Instant toInstant();
    descriptor: ()Ljava/time/Instant;

  public int compareTo(java.lang.Object);
    descriptor: (Ljava/lang/Object;)I

  static {};
    descriptor: ()V
}

C:\Users\renzhenming>

1.在Java的一个类中,如下,定义各种类型的属性和方法

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

public class JniUtils {

    public String key = "renzhenming";
    
    private Human human = new Man(); 
    
    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 native void accessNonVirtualMethod();

    public native String toChineseString(String value);
    
    public native void accessConstructor();
    
    public native void setArray(int [] arr);
    
    public native int[] getArray();
    
    public native void cached();
    
    public native static void initIds();
    
    public static void main(String[] args) {
        String text = getStringFromC();
        System.out.println(text);
        JniUtils t = new JniUtils();
        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();
        
        System.out.println("访问构造方法");
        new JniUtils().accessConstructor();
        
        System.out.println("访问被子类重写后的父类方法");    
        new JniUtils().accessNonVirtualMethod();
        
        System.out.println(new JniUtils().toChineseString("想乱码的日子里"));
        
        int arr [] = {12,3,43,55,44,1,566};
        new JniUtils().setArray(arr);
        for(int i = 0 ; i < arr.length;i++ ) {
            System.out.println(arr[i]); 
        }
        
        System.out.println("-------------------------------");  
        int [] jarr = new JniUtils().getArray();
        for(int i = 0 ; i < jarr.length;i++ ) {
            System.out.println(jarr[i]);    
        }
        
        System.out.println("-------------------------------");  
        new JniUtils().createGlobalRef();
        System.out.println(new JniUtils().getGlobalRef());
        new JniUtils().deleteGlobalRef();
        //释放了再获取就会空指针
        //System.out.println(new JniUtils().getGlobalRef());
        
        try {
            new JniUtils().exception();
        } catch (Exception e) {
            System.out.println("发生异常:"+e.getMessage());
        }
        //不断调用cached方法
        for (int i = 0; i < 100; i++) {
            t.cached();
        }
    }
    
    //产生指定范围的随机数
    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");//这是生成的dll动态库的名字
        initIds();
    }

}

//类Human和类Man代码如下(测试jni调用父类方法而设置):
public class Human {
    public void sayHi() {
        System.out.println("人打招呼");
    }
}
public class Man extends Human{
    public void sayHi() {
        System.out.println("男。。。。。人打招呼");
    }
}

2.创建native方法,使用javah生成头文件,eclipse是进入到项目的src目录 javah com.xxx.xxx,如果提示错误: 编码GBK的不可映射字符,加上encoding javah -encoding URF-8 com.xxx.xxx

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_dongnaoedu_jni_JniTest */

#ifndef _Included_com_renzhenming_bsdiff_JniUtils
#define _Included_com_renzhenming_bsdiff_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     _com_renzhenming_bsdiff_JniUtils
 * Method:    getStringFromC
 * Signature: ()V
 */
JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_getStringFromC
  (JNIEnv *, jclass);

JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_getString2FromC
(JNIEnv *, jobject, jint);

JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessField
(JNIEnv *, jobject);

JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessStaticField
(JNIEnv *, jobject);

JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessMethod
(JNIEnv *, jobject);

JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessStaticMethod
(JNIEnv *, jobject);
    
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessConstructor
(JNIEnv *, jobject);
    
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessNonVirtualMethod
(JNIEnv *, jobject);
    
JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_toChineseString
(JNIEnv *, jobject, jstring);
    
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_setArray
(JNIEnv *,jobject,jintArray);
    
JNIEXPORT jintArray JNICALL Java_com_renzhenming_bsdiff_JniUtils_getArray
(JNIEnv *, jobject);
    
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_localRef
(JNIEnv *, jobject);

JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_createGlobalRef
(JNIEnv *,jobject);

JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_getGlobalRef
(JNIEnv *, jobject);

JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_deleteGlobalRef
(JNIEnv *, jobject);
    
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_exception
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

3.将头文件引入as中(jni.h和jni_md.h引入同上次笔记),编写c代码,如下

#define _CRT_SECURE_NO_WARNINGS
#include "com_renzhenming_bsdiff_JniUtils.h"
#include 

//功能:获取到指定类中一字符串类型成员变量,修改返回得到新的字符串
JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_changeKey
(JNIEnv *env, jobject jobj) {
    //我们编写的native方法是非静态方法,所以这里是jobject,而我们需要jclass来获取fieldid,所以这样转换
    jclass clazz  = (*env)->GetObjectClass(env, jobj);
    //参数分别为class对象,属性名,属性签名(签名得到的方法查看文档),获取到我们需要修改的属性的id值
    jfieldID fieldId = (*env)->GetFieldID(env, clazz, "key", "Ljava/lang/String;");
    //从这个对象jobj中获取这个id fieldid的jstring 对象
    jstring jstr = (*env)->GetObjectField(env, jobj, fieldId);
    //将jni类型jstring 转换为c对象char ,从而我们可以调用c方法修改这个对象的值
    char *c_str = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE);
    //将这个对象的值和text这个字符串连接得到我们需要的新值
    char text[30] = "handsome ";
    strcat(text, c_str);
    //再次将这个新值对应的c对象char转换为jni类型jstring,
    jstring new_jstr = (*env)->NewStringUTF(env, text);
    //将这个jstring类型的新对象设置给这个jobj这个对象中id值为fieldId的这个对象,至此对象值已经被我们修改
    (*env)->SetObjectField(env, jobj, fieldId, new_jstr);
    
    //删除
    (*env)->DeleteLocalRef(env,new_jstr);
    //返回这个新的字符串,这里没有用到
    return new_jstr;
}

#define _CRT_SECURE_NO_WARNINGS
#include "com_renzhenming_bsdiff_JniUtils.h"
#include 

//函数实现
JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_getStringFromC
(JNIEnv *env, jclass jcls){
    return (*env)->NewStringUTF(env,"C String");
}

JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_getString2FromC
(JNIEnv *env, jobject jobj, jint num){
    return (*env)->NewStringUTF(env,"C String2");
}

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

//基本数据
//Java基本数据类型与JNI数据类型的映射关系
//Java类型->JNI类型->C类型

/*
boolean jboolean
byte jbyte;
char jchar;
short jshort;
int jint;
long jlong;
float jfloat;
double jdouble;
void void
*/

//引用类型(对象)
//String jstring
//object jobject
//数组,基本数据类型的数组
//byte[] jByteArray
//对象数组
//object[](String[]) jobjectArray

//C/C++访问Java的成员

//1.访问属性
//修改属性key
JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessField
(JNIEnv *env, jobject jobj){
     //我们编写的native方法是非静态方法,所以这里是jobject,而我们需要jclass来获取fieldid,所以这样转换
    jclass clazz  = (*env)->GetObjectClass(env, jobj);
    //参数分别为class对象,属性名,属性签名(签名得到的方法查看文档),获取到我们需要修改的属性的id值
    jfieldID fieldId = (*env)->GetFieldID(env, clazz, "key", "Ljava/lang/String;");
    //从这个对象jobj中获取这个id fieldid的jstring 对象
    jstring jstr = (*env)->GetObjectField(env, jobj, fieldId);
    //将jni类型jstring 转换为c对象char ,从而我们可以调用c方法修改这个对象的值
    char *c_str = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE);
    //将这个对象的值和text这个字符串连接得到我们需要的新值
    char text[30] = "handsome ";
    strcat(text, c_str);
    //再次将这个新值对应的c对象char转换为jni类型jstring,
    jstring new_jstr = (*env)->NewStringUTF(env, text);
    //将这个jstring类型的新对象设置给这个jobj这个对象中id值为fieldId的这个对象,至此对象值已经被我们修改
    (*env)->SetObjectField(env, jobj, fieldId, new_jstr);
    (*env)->DeleteLocalRef(env,new_jstr);
    //返回这个新的字符串,这里没有用到
    return new_jstr;
}

//访问静态属性
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessStaticField
(JNIEnv *env, jobject jobj){
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jfieldID
    jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
    //GetStaticField
    jint count = (*env)->GetStaticIntField(env, cls, fid);
    count++;
    //修改
    //SetStaticField
    (*env)->SetStaticIntField(env,cls,fid,count);
}

//2.访问java方法
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessMethod
(JNIEnv *env, jobject jobj){
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jmethodID
    jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
    //调用
    //CallMethod
    jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
    printf("random num:%ld",random);

    //.....
}

//静态方法
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessStaticMethod
(JNIEnv *env, jobject jobj){
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jmethodID 
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
    
    //调用
    //CallStaticMethod
    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);
    
    //w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
    FILE *fp = fopen(filename,"w");
    fputs("i love jason", fp);
    fclose(fp);
}

//访问构造方法(Date 生成一个时间戳)
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessConstructor
(JNIEnv *env, jobject jobj) {
    //根据包名得到类,类似反射
    jclass cls = (*env)->FindClass(env, "java/util/Date");
    //获取类中要获取的方法id(获取构造方法,方法名传"")
    jmethodID methodid = (*env)->GetMethodID(env, cls, "", "()V");
    //实例化一个对象,类似反射
    jobject date_obj = (*env)->NewObject(env, cls, methodid);
    //通过这个对象调用getTime方法,需要获取到这个方法的id
    jmethodID g_methodid = (*env)->GetMethodID(env, cls, "getTime", "()J");
    //执行这个方法,返回值是long类型,所以CallLong
    jlong time = (*env)->CallLongMethod(env, date_obj, g_methodid);
    //占位符用的lld,是long long类型的,如果直接用ld占位符打印,打出的是负数
    printf("\ntime:%lld\n", time);
    (*env)->DeleteLocalRef(env,date_obj);
}

//访问父类被子类重写的方法
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_accessNonVirtualMethod
(JNIEnv *env, jobject jobj) {
    //在java中,父类指向子类对象,调用被子类重写的方法,执行的是子类的方法,在jni中
    //我们可以调用到父类的方法
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //获取类中human属性(先获取到id)  对象的签名
    jfieldID fid  = (*env)->GetFieldID(env, cls, "human", "Lcom/renzhenming/bsdiff/Human;");
    jobject human_obj = (*env)->GetObjectField(env, jobj, fid);
    //获取父类class
    jclass human_class = (*env)->FindClass(env, "com/renzhenming/bsdiff/Human");
    //获取父类中需要调用的方法的methodid
    jmethodID s_mid = (*env)->GetMethodID(env,human_class,"sayHi","()V");
    //执行指定方法

    //子类方法
    (*env)->CallObjectMethod(env, human_obj, s_mid);
    //父类方法(c++中要重写父类方法,用的是virtual关键字)(调用父类方法传入的class不能通过
    //GetObjectClass从human_obj中获取,因为这样获取到的是子类的class)
    (*env)->CallNonvirtualObjectMethod(env, human_obj, human_class, s_mid);
}
//解决jni中文乱码的问题
//java 内部是使用的 16 bit 的 unicode 编码(utf-16)来表示字符串的,无论英文还是中文都是 2 字节。
//JNI 内部是使用 utf-8 编码来表示字符串的,utf-8 是变长编码的 unicode,一般 ascii 字符是 1 字节,中文是 3 字节。
//C/C++ 使用的是原始数据,ascii 就是一个字节,中文一般是 GB2312 编码,用 2 个字节表示一个汉字。
//GetStringUTFChars,这个函数将得到一个 UTF-8 编码的字符串;
//另一个是 GetStringChars 这个将得到 UTF-16 编码的字符串。无论那个函数,得到的字符串如果含有中文,
//都需要进一步转化成 GB2312 的编码。

//两种解决方式,第一,就是下边这种,Java传递字符串到c中,c中通过java中的String构造方法,将接收到的字符串转成GB2312的编码格式

//第二,是直接在Java中将字符串转成GB2312编码的byte数组,然后传递到c中
//byte[] byteArry = strToJNI.getBytes("GB2312");
JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_toChineseString_
(JNIEnv *env, jobject jobj, jstring jstr) {
    //输出
    //char *c_str = (*env)->GetStringUTFChars(env, in, 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, "", "([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);
}

//-----------------------操作数组涉及到数组的同步问题(ReleaseIntArrayElements)-----------------------------//

//操作传入的数组
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_setArray
(JNIEnv *env, jobject  jobj, jintArray array) {
    //jintArray ->jint指针->c int数组
    jint *elems = (*env)->GetIntArrayElements(env, array, JNI_FALSE);
    //数组的长度
    int len = (*env)->GetArrayLength(env, array);
    //排序(传入的compare类似Java中的比较器)
    qsort(elems, len, sizeof(jint), compare);
    //将排序结果同步到Java中的数组
    //最后一个参数 mode
    //0  表示Java数组进行更新,并且释放c/c++数组
    //JNI_ABORT  表示Java数组不更新,释放c/c++数组
    //JNI_COMMIT 表示Java数组进行更新,但不释放c/c++数组
    (*env)->ReleaseIntArrayElements(env, array, elems, 0);
}

//返回一个数组
JNIEXPORT jintArray JNICALL Java_com_renzhenming_bsdiff_JniUtils_getArray
(JNIEnv *env, jobject jobj) {
    //创建一个指定大小的数组
    jintArray jint_arr = (*env)->NewIntArray(env, 10);
    //获取到数组指针
    jint *elems = (*env)->GetIntArrayElements(env, jint_arr, NULL);
    int i = 0;
    for ( ;  i< 10; i++)
    {
        elems[i] = i;
    }

    //同步
    (*env)->ReleaseIntArrayElements(env, jint_arr, elems, 0);
    return jint_arr;
}

//局部引用
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_localRef
(JNIEnv *env, jobject jobj) {
    int i = 0;
    for (; i < 5; i++) {
        //创建对象,以Date为例
        jclass cls = (*env)->FindClass(env, "java/util/Date");
        jmethodID constructor_id = (*env)->GetMethodID(env, cls, "init", "()V");
        jobject obj = (*env)->NewObject(env, cls, constructor_id);
        //....
        //不在使用这个对象了需要回收
        (*env)->DeleteLocalRef(env, obj);
        //....
    }
}

//创建全局引用(共享,可以跨多个线程)
jstring global_str;
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_createGlobalRef
(JNIEnv *env, jobject jobj) {
    jstring jstr = (*env)->NewStringUTF(env,"jni is a bitch");
    global_str = (*env)->NewGlobalRef(env, jstr);
}
//获取全局引用
JNIEXPORT jstring JNICALL Java_com_renzhenming_bsdiff_JniUtils_getGlobalRef
(JNIEnv *env, jobject jobj) {
    return global_str;
}
//删除全局引用
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_deleteGlobalRef
(JNIEnv *env, jobject jobj) {
    (*env)->DeleteGlobalRef(env, global_str);
}

//Java捕获c异常(如果不做抛异常处理,直接再Java层try是抓不到异常信息的)
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_exception
(JNIEnv *env, jobject jobj) {
    jclass cls = (*env)->GetObjectClass(env, jobj);
    jfieldID fid = (*env)->GetFieldID(env, cls, "value2", "Ljava/lang/String;");
    //检测是否发生Java异常
    jthrowable exception = (*env)->ExceptionOccurred(env);
    if (exception != NULL) {
        //让Java代码可以继续运行
        //清空异常信息
        (*env)->ExceptionClear(env);
        //补救措施
        fid = (*env)->GetFieldID(env, cls, "value", "Ljava/lang/String;");
    }

    //获取属性值
    jstring jstr = (*env)->GetObjectField(env, jobj, fid);
    char *str = (*env)->GetStringUTFChars(env, jstr, NULL);

    //对比属性是否合法
    if (stricmp(str, "renzhenming") != 0) {
        //抛出异常,给Java处理
        jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
        (*env)->ThrowNew(env, newExcCls, "key's value is invalid!");
    }
}

//静态缓存

//static jfieldID key_id 
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_cached(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->GetObjectClass(env, jobj); 
    //»ñÈ¡jfieldIDÖ»»ñÈ¡Ò»´Î
    //¾Ö²¿¾²Ì¬±äÁ¿
    static jfieldID key_id = NULL;
    if (key_id == NULL){
        key_id = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
        printf("--------GetFieldID-------\n");
    }
}

//加载动态库的时候就初始化全局变量,再loadLibrary后调用
jfieldID key_fid;
jmethodID random_mid;
JNIEXPORT void JNICALL Java_com_renzhenming_bsdiff_JniUtils_initIds(JNIEnv *env, jclass jcls){  
    key_fid = (*env)->GetFieldID(env, jcls, "key", "Ljava/lang/String;");
    random_mid = (*env)->GetMethodID(env, jcls, "genRandomInt", "(I)I");
}

4.生成dll动态库,放到eclipse中调用

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();
    
        System.out.println("访问构造方法");
        new JniUtils().accessConstructor();
    
        System.out.println("访问被子类重写后的父类方法");    
        new JniUtils().accessNonVirtualMethod();
    
        System.out.println(new JniUtils().toChineseString("想乱码的日子里"));
    
        int arr [] = {12,3,43,55,44,1,566};
        new JniUtils().setArray(arr);
        for(int i = 0 ; i < arr.length;i++ ) {
            System.out.println(arr[i]); 
        }
    
        System.out.println("-------------------------------");  
        int [] jarr = new JniUtils().getArray();
        for(int i = 0 ; i < jarr.length;i++ ) {
            System.out.println(jarr[i]);    
        }
    
        System.out.println("-------------------------------");  
        new JniUtils().createGlobalRef();
        System.out.println(new JniUtils().getGlobalRef());
        new JniUtils().deleteGlobalRef();

        //释放了再获取就会空指针
        //System.out.println(new JniUtils().getGlobalRef());
        
        try {
            new JniUtils().exception();
        } catch (Exception e) {
            System.out.println("发生异常:"+e.getMessage());
        }
        
    }

你可能感兴趣的:(02.Jni开发流程_C/C++调用java)