#1. c里面 JNIEnv是个二级指针,c++里直接是个指针,因为c++里有this指针表示结构体自己;
#2. JNIEnv代表java运行环境,在c中调用java代码
#3. java调用的native方法命名规则 Java_完整类名_方法名(JNIEnv * env,jobject thiz......)
#4. jni里的对象都是通过gc进行管理的;
每个native方法至少有两个参数JNIEnv ,jclass(静态)或者jobject(非静态)
静态方法:jclass代表native方法所在java类的class对象;
非静态方法:jobject代表native方法所属的对象;
#数据类型
java类型 ——>JNI类型——> c类型
String jstring
object jobject
byte[] jByteArray
object[](String[])jobjectArray
boolean jboolean(JNI_TRUE,JNI_FALSE)
写好java 的native方法之后,cmd到src目录下,用“
javah 完整类名” 生成头文件.h
#define _CRT_SECURE_NO_WARNINGS//必须放在最上面
#include <stdio.h>
#include <string.h>
#include "com_jack_TestJni.h"
#define log(args,...) printf(args,__VA_ARGS__);
JNIEXPORT void JNICALL Java_com_jack_TestJni_change
(JNIEnv *env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env, jobj);
//获取签名的方法:cmd, cd 到java工程所在的bin目录,javap -s -private 完整类名
jfieldID valueId = (*env)->GetFieldID(env, cls, "value", "I");
jint value = (*env)->GetIntField(env, jobj, valueId);
//基础的数据类型 jni的类型和c类型不用相互转化
printf("original value is : %d\n", value);
(*env)->SetIntField(env, jobj, valueId, 27);
//注意不要忘记最后的分号
jfieldID textId = (*env)->GetFieldID(env, cls, "text", "Ljava/lang/String;");
//获取对象类型
jstring jtext = (*env)->GetObjectField(env, jobj, textId);
//最后的isCopy参数如果为JNI_TRUE 的话,会报错
//isCopy 为指针类型,在函数执行过程中赋值,
//isCopy=JNI_TRUE 为复制; isCopy=JNI_FALSE 为不复制,c和java指向同一字符串,不能修改
//要在c函数中使用java字符串,必须转换String-> jstring -> char*
char *cOriginalText = (*env)->GetStringUTFChars(env, jtext, JNI_FALSE);
printf("original text is : %s\n", cOriginalText);
jmethodID methId = (*env)->GetMethodID(env, cls, "getText", "()Ljava/lang/String;");
jstring jstr = (*env)->CallObjectMethod(env, jobj, methId);
char *cOriginaljstr = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE);
printf("original jstr from method : %s\n", cOriginaljstr);
//设置新的对象值
char* cText = "gloria";
jstring newText = (*env)->NewStringUTF(env, cText);
(*env)->SetObjectField(env, jobj, textId, newText);
(*env)->ReleaseStringUTFChars(env, newText , cText);//释放字符串
//----------------------以下是操作java静态成员---------------------------
//获取静态属性id
jfieldID staticStrId = (*env)->GetStaticFieldID(env, cls, "staticText", "Ljava/lang/String;");
//获取静态属性
jstring staticStr = (*env)->GetStaticObjectField(env, cls, staticStrId);
char *cStr = (*env)->GetStringUTFChars(env, staticStr, JNI_FALSE);
printf("static text from java is : %s \n", cStr);
cStr=strcat(cStr, " is change 哈哈哈哈----------");
//-------------通过调用java的string构造方法解决中文乱码问题-----------
jclass strCls = (*env)->FindClass(env, "java/lang/String");
//获取构造方法
jmethodID cId = (*env)->GetMethodID(env, strCls, "<init>", "([BLjava/lang/String;)V");
int lenth = strlen(cStr);
jbyteArray bytes = (*env)->NewByteArray(env,lenth );
(*env)->SetByteArrayRegion(env, bytes, 0, lenth, cStr);
jstring char_set = (*env)->NewStringUTF(env, "gb2312");
jstring result = (*env)->NewObject(env, strCls, cId, bytes, char_set);
//设置静态属性的值
(*env)->SetStaticObjectField(env, cls, staticStrId, result);
//---------------调用静态方法------------------
/*jmethodID method= (*env)->GetStaticMethodID(env, cls, "setStaticText", "(Ljava/lang/String;)V");
char* c = "hello from c";
jstring njs = (*env)->NewStringUTF(env, c);
(*env)->CallStaticVoidMethod(env, cls, method, njs);*/
//---------------------创建java对象---------------------
jclass date = (*env)->FindClass(env, "java/util/Date");
//获取构造方法
jmethodID constructorId = (*env)->GetMethodID(env, date, "<init>", "()V");
jobject dateInstance = (*env)->NewObject(env, date, constructorId);
jmethodID methodId = (*env)->GetMethodID(env, date, "getTime", "()J");
jlong time = (*env)->CallLongMethod(env, dateInstance, methodId);
printf("current time is : %lld \n", time);
//----------------------访问父类的方法----------------------------
jclass childCls = (*env)->GetObjectClass(env, jobj);
jclass superCls = (*env)->FindClass(env, "com/jack/SuperClass");
jmethodID m = (*env)->GetMethodID(env, superCls, "sayHello", "()V");
(*env)->CallObjectMethod(env, jobj, m);
(*env)->CallNonvirtualObjectMethod(env, jobj, superCls, m);
}
//------------操作数组------------------
//必须用指针,否则不能交换位置
int compare(int* a, int* b){
return (*a) - (*b);
}
JNIEXPORT void JNICALL Java_com_jack_TestJni_sort
(JNIEnv *env, jobject obj,jintArray array){
int size = (*env)->GetArrayLength(env, array);
jint* arrayP = (*env)->GetIntArrayElements(env, array, NULL);
qsort(arrayP, size, sizeof(jint), compare);
//最后一个参数
//0,Java数组更新,并且释放c数组
//JNI_COMMIT ,java数组更新,不释放c数组,可继续使用
//JNI_ABORT(2) java数组不更新,释放c数组
(*env)->ReleaseIntArrayElements(env, array, arrayP, JNI_ABORT);
//-------------jni 的异常捕获-------------
//JNI自己抛出的异常,可以在java层被捕捉
//用户通过(*env)->ThrowNew(env);抛出的异常,在java层可以捕获;
jclass cls = (*env)->GetObjectClass(env, obj);
//获取静态属性id
//没有这个字段,java.lang.NoSuchFieldError可以用throwable捕获到
jfieldID staticStrId = (*env)->GetStaticFieldID(env, cls, "staticText22222", "Ljava/lang/String;");
/*int arr[] = { 1, 2, 3, 4, 5, 0 };
for (int var = 0; var < 6;var++)
{
int i = 100 / arr[var];
}*/
//检查是否出现java,jni异常,不能捕获c的异常(c 本来就没有异常捕获的措施),比如上面的除数为0的异常
jthrowable exc = (*env)->ExceptionOccurred(env);
if (exc != NULL){//清空异常,保证java代码继续执行
printf("--------jni catch java exception------------");
(*env)->ExceptionClear(env);
//抛出异常
jclass newExcCls = (*env)->FindClass(env, "java/lang/Exception");
(*env)->ThrowNew(env, newExcCls, "error");//这样抛出的异常可以被捕获
}
}
void (JNICALL *DeleteGlobalRef) (JNIEnv *env, jobject gref);//用于jni中 删除全局引用
void (JNICALL *DeleteLocalRef) (JNIEnv *env, jobject obj);//删除局部引用
弱全局引用WeakGlobalRef
static 声明在函数内的局部静态变量
static int ;//作用域只在函数内,生命周期直到程序退出结束。