LOG引入
#include <android/log.h>
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "com.droider.jnimethods", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "com.droider.jnimethods", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "com.droider.jnimethods", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "com.droider.jnimethods", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "com.droider.jnimethods", __VA_ARGS__)
最简单的Jni方法
/*
* Class: com_xiaomakj_hellogcc_TestJniMethods
* Method: nativeMethod
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_xiaomakj_hellogcc_TestJniMethods_nativeMethod
(JNIEnv * env , jobject object){
const char * chs = "你好!NativeMethod";
return (*env)->NewStringUTF(env, chs);
}
或者
jstring stoJstring( JNIEnv* env, const char* pat )
{
jclass strClass = (*env)->FindClass(env, "java/lang/String");
jmethodID ctorID = (*env)->GetMethodID(env,
strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), pat);
jstring encoding = (*env)->NewStringUTF(env, "GB2312");
return (jstring)(*env)->NewObject(env,
strClass, ctorID, bytes, encoding);
}
对应native方法
public native String nativeMethod();
加载一个本地类
int version = (*env)->GetVersion(env);//获JNI版本
LOGE("GetVersion() --> jni version:%2x", version);
jclass build_class = (*env)->FindClass(env, "android/os/Build"); //加载一个本地类
jfieldID brand_id = (*env)->GetStaticFieldID(env, //获取类的静态字段ID
build_class, "MODEL", "Ljava/lang/String;");
jstring brand_obj = (jstring)(*env)->GetStaticObjectField(env, //获取类的静态字段的值ֵ
build_class, brand_id);
//__asm__ ("bkpt");
//raise(SIGTRAP);
const char *nativeString = (*env)->GetStringUTFChars(env, brand_obj, 0); //通过jstring生成char*
LOGE("GetStringUTFChars() --> MODEL:%s", nativeString);
LOGE("GetStaticFieldID() --> %s", nativeString);
LOGE("ReleaseStringUTFChars() --> %s", nativeString);
(*env)->ReleaseStringUTFChars(env, brand_obj, nativeString); //释放GetStringUTFChars()生成的char*
GetVersion() --> jni version:10006
GetStringUTFChars() --> MODEL:vivo X9i
GetStaticFieldID() --> vivo X9i
ReleaseStringUTFChars() --> vivo X9i
初始化本地对象
jclass test_class = (*env)->FindClass(env, "com/droider/jnimethods/TestClass");
//空参构造
jmethodID constructor = (*env)->GetMethodID(env, test_class, "<init>", "()V"); //获取构造函数
jobject obj = (*env)->NewObject(env, test_class, constructor); //创建一个对象
操纵成员变量
//String
jfieldID stringfieldID = (*env)->GetFieldID(env, //获取String类型的字段
test_class, "aStringField", "Ljava/lang/String;");
jstring stringfieldValue = (jstring)(*env)->GetObjectField(env, //获取String字段的值ֵ
obj, stringfieldID);
const char *stringValue = (*env)->GetStringUTFChars(env,
stringfieldValue, 0);
LOGE("GetObjectField() --> aStringField:%s", stringValue);
//int
jfieldID intfieldID = (*env)->GetFieldID(env, //获取int类型的字段
test_class, "aIntField", "I");
jint fieldValue = (*env)->GetIntField(env, obj, intfieldID); //获取int字段的值ֵ
LOGV("GetIntField() --> aField:%d", fieldValue);
(*env)->SetIntField(env, obj, intfieldID, (jint)123); // 设置int字段的值ֵ
fieldValue = (*env)->GetIntField(env, obj, intfieldID);
LOGV("SetIntField() --> aField:%d", fieldValue);
获取父类
jclass parent_class = (*env)->GetSuperclass(env, test_class);
LOGV("GetSuperclass() --> ");
JNI常量 定义在jni.h当中
#define JNI_FALSE 0
#define JNI_TRUE 1
#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006
#define JNI_OK (0) /* no error */
#define JNI_ERR (-1) /* generic error */
#define JNI_EDETACHED (-2) /* thread detached from the VM */
#define JNI_EVERSION (-3) /* JNI version error */
#define JNI_COMMIT 1 /* copy content, do not free buffer */
#define JNI_ABORT 2 /* free buffer w/o copying back */
EnsureLocalCapacity 检测是否还可以创建5个局部引用 https://blog.csdn.net/xyang81/article/details/44873769)
// 保证至少可以创建3个局部引用(str_array,cls_string,obj_str)
if ((*env)->EnsureLocalCapacity(env, 3) != JNI_OK) {
return NULL;
}
操作本地方法
if(JNI_OK == (*env)->EnsureLocalCapacity(env, 5)){ //检测是否还可以创建5个局部引用
LOGV("EnsureLocalCapacity() --> ensure 5 locals");
jmethodID obj2_voidmethod = (*env)->GetMethodID(env, //获取一个void方法
test_class, "aVoidMethod", "()V");
(*env)->CallVoidMethod(env, obj, obj2_voidmethod);
LOGV("CallVoidMethod()");
jmethodID obj2_Staticmethod = (*env)->GetStaticMethodID(env, //获取static方法
test_class, "aStaticMethod", "(Ljava/lang/String;)V");
LOGV("GetStaticMethodID()");
const char *fromJni = "this string from jni";
jstring jstr_static = (*env)->NewStringUTF(env, fromJni);
(*env)->CallStaticVoidMethod(env, test_class, obj2_Staticmethod, jstr_static); //调用一个静态方法
}
MonitorEnter类似于JAVA的synchronized成对存在
(*env)->MonitorEnter(env, obj); //同步操作
(*env)->MonitorExit(env, obj);
操作String
//获取String
jmethodID obj2_chinesemethod = (*env)->GetMethodID(env, //传递中文字符串
test_class, "getChineseString", "()Ljava/lang/String;");
jstring obj2_chinesejstring = (jstring)(*env)->CallObjectMethod(env, obj, obj2_chinesemethod);
jsize chinese_size = (*env)->GetStringLength(env, obj2_chinesejstring);
LOGV("GetStringLength() --> %d", chinese_size);
//GetStringRegion http://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/string.html
//为这个字符串分配空间 0:statr,3:len,buff_jchar:end
jchar buff_jchar[4] = {0};
(*env)->GetStringRegion(env, obj2_chinesejstring, 0, 3, buff_jchar);
LOGV("GetStringRegion() -->");
jStrinfg to jchar
const jchar * obj2_chinesechars = (*env)->GetStringChars(env, obj2_chinesejstring, NULL);
jstring new_chinesestring = (*env)->NewString(env, obj2_chinesechars, chinese_size);
调用本地方法 并释放String
jmethodID obj2_Staticmethod = (*env)->GetStaticMethodID(env, //获取static方法
test_class, "aStaticMethod", "(Ljava/lang/String;)V");
LOGE("GetStaticMethodID()");
(*env)->CallStaticVoidMethod(env, test_class, obj2_Staticmethod, new_chinesestring); //调用一个静态方法
(*env)->ReleaseStringChars(env, obj2_chinesejstring, obj2_chinesechars); //释放GetStringChars获取的jchar*
LOGV("CallStaticVoidMethod()");
Jni调用JAVA返回计算结果
jmethodID obj2_sqrtmethod = (*env)->GetStaticMethodID(env, //获取一个static方法
test_class, "sqrt", "(I)I");
jint int_sqrt = (*env)->CallStaticIntMethod(env, //计算5的平方
test_class, obj2_sqrtmethod, (jint)5);
LOGE("CallStaticIntMethod() -->5 sqrt is:%d", int_sqrt);
java代码
public static int sqrt(int x){
return x * x;
}
IsAssignableFrom:
判断该类是否是本类或子类 是则返回true
https://www.jb51.net/article/103114.htm
抛出一个异常
if(JNI_TRUE == (*env)->IsAssignableFrom(env, test_class, parent_class)){
LOGV("IsAssignableFrom() --> yes");
} else {
jclass newExceptionClazz = (*env)->FindClass(env,"java/lang/RuntimeException"); //实例化一个异常
if(newExceptionClazz != NULL)
(*env)->ThrowNew(env, newExceptionClazz,"这里永远不会被执行的!");
LOGE("ThrowNew()");
}
获取对象的类
jclass obj_clazz = (*env)->GetObjectClass(env, obj); //获取对象的类
if(JNI_TRUE == (*env)->IsInstanceOf(env, obj, obj_clazz)){
LOGE("IsInstanceOf() --> Yes");
} else {
(*env)->FatalError(env, "fatal error!"); //抛出致命错误
LOGE("FatalError()");
}
FatalError致命错误
(*env)->FatalError(env, "fatal error!");
创建全局和局部引用 相当于 new
(*env)->PushLocalFrame(env, 2); //申请局部引用空间,增加局部引用的管理
jobject obj_localref = (*env)->NewLocalRef(env, obj); //创建一个局部引用
jobject obj_globalref = (*env)->NewGlobalRef(env, obj); //创建一个全局引用
LOGV("PushLocalFrame()");
LOGV("NewLocalRef()");
LOGV("NewGlobalRef()");
if (JNI_TRUE == (*env)->IsSameObject(env, obj_localref, obj_globalref)){
LOGV("IsSameObject() --> Yes");
}
(*env)->DeleteLocalRef(env, obj_localref); //删除一个局部引用
(*env)->DeleteGlobalRef(env, obj_globalref); //删除一个全局引用
(*env)->PopLocalFrame(env, NULL);
LOGV("DeleteLocalRef()");
LOGV("DeleteGlobalRef()");
LOGV("PopLocalFrame()");
调用子类成员和方法
注意:CallNonvirtualVoidMethod方法 第三个参数为指定类类型
jclass sub_class = (*env)->FindClass(env,
"com/droider/jnimethods/TestSubClass"); //查询子类
jobject sub_obj = (*env)->AllocObject(env, sub_class);
jmethodID sub_methodID = (*env)->GetMethodID(env,
sub_class, "aVoidMethod", "()V");
(*env)->CallNonvirtualVoidMethod(env,
sub_obj, sub_class, sub_methodID); //调用子类的方法
(*env)->CallNonvirtualVoidMethod(env,
sub_obj, test_class, sub_methodID); //根据调用类的不同调用父类的方法
jfieldID sub_fieldID = (*env)->GetStaticFieldID(env, //获取子类静态字段
sub_class, "subFloatField", "F");
(*env)->SetStaticFloatField(env, sub_class, sub_fieldID, (jfloat)33.88f);
LOGV("SetStaticFloatField() --> %.2f",
(*env)->GetStaticFloatField(env, sub_class, sub_fieldID));
异常抓取
https://blog.csdn.net/arui319/article/details/2178619
jthrowable throwable = (*env)->ExceptionOccurred(env);
//判断是否包含异常 有则清除和打印异常
if (throwable){ //有异常发生,还可以使用ExceptionCheck()函数来判断
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
LOGE("ExceptionOccurred()");
}
int 数组操作
jintArray int_array = (*env)->NewIntArray(env, 5); //创建int数组
LOGV("NewIntArray() --> %d", (*env)->GetArrayLength(env, int_array)); //获取数组长度
const jint ints[] = {11, 12, 13, 14, 15};
(*env)->SetIntArrayRegion(env, int_array, 0, 5, ints); //设置数组一个范围的值ֵ
LOGV("SetIntArrayRegion() --> %d,%d,%d,%d,%d",
ints[0], ints[1], ints[2], ints[3], ints[4]);
jint ints2[2] = {0, 0};
(*env)->GetIntArrayRegion(env, int_array, 1, 2, ints2); //获取数组一个范围的值
LOGV("GetIntArrayRegion() --> %d,%d", ints2[0], ints2[1]);
jint* array_ints = (*env)->GetIntArrayElements(env, int_array, NULL); //获取指向所有元素的指针
LOGV("GetIntArrayElements() --> %d,%d,%d,%d,%d",
array_ints[0], array_ints[1], array_ints[2], array_ints[3], array_ints[4]);
(*env)->ReleaseIntArrayElements(env, int_array, array_ints, 0); //释放指向所有元素的指针
LOGV("ReleaseIntArrayElements()");
String 数组操作
jclass class_string = (*env)->FindClass(env, "java/lang/String");
jarray string_array = (*env)->NewObjectArray(env, 3, class_string, 0); //创建String数组
LOGV("NewObjectArray()");
jsize array_size = (*env)->GetArrayLength(env, string_array);
LOGV("GetArrayLength() --> %d", array_size);
jstring array_string1 = (*env)->NewStringUTF(env, "one");
char buff_char[4] = {0};
(*env)->GetStringUTFRegion(env, array_string1, 0, 3, buff_char);
LOGV("GetStringUTFRegion() --> %s", buff_char);
(*env)->SetObjectArrayElement(env, string_array, 0, array_string1); //设置数组元素的值
LOGV("SetObjectArrayElement() --> one");
array_string1 = (jstring)(*env)->GetObjectArrayElement(env, string_array, 0); //获取数组元素的值
const char* array_elemchars = (*env)->GetStringUTFChars(env, array_string1, NULL);
LOGV("GetObjectArrayElement() --> %s", array_elemchars);
(*env)->ReleaseStringUTFChars(env, array_string1, array_elemchars);
JNI_OnLoad和JNI_OnUnLoad:
JNI的内部方法,RegisterNatives加载本地方法,动态引入native方法,也就是在.h文件中并未实现申明的函数在JNI中动态添加
JNIEnv *g_env;
jclass native_class;
#ifndef NELEM //计算结构元素个数
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif
static JNINativeMethod methods[] = {
{"newJniThreads", "(I)V", (void*)newJniThreads}
};
jint JNI_OnLoad(JavaVM* vm, void* reserved){
if(JNI_OK != (*vm)->GetEnv(vm, (void**)&g_env, JNI_VERSION_1_6)){ //加载指定版本的JNI 对应 int version = (*env)->GetVersion(env);
return -1;
}
LOGV("JNI_OnLoad()");
native_class = (*g_env)->FindClass(g_env, "com/xiaomakj/hellogcc/TestJniMethods");
if (JNI_OK ==(*g_env)->RegisterNatives(g_env, //注册未声明的本地方法
native_class, methods, NELEM(methods))){
LOGV("RegisterNatives() --> nativeMethod() ok");
} else {
LOGE("RegisterNatives() --> nativeMethod() failed");
return -1;
}
return JNI_VERSION_1_6;
}
void JNI_OnUnLoad(JavaVM* vm, void* reserved){
LOGV("JNI_OnUnLoad()");
(*g_env)->UnregisterNatives(g_env, native_class);
LOGV("UnregisterNatives()");
}
newJniThreads方法 注意:
1.方法名较 Java_com_droider_jnimethods_TestJniMethods_test短得多
2.这里方法需要事先申明 否则将该方法放在JNI_OnLoad之前
#include <pthread.h>//引入线程头文件
JNIEXPORT void newJniThreads(JNIEnv* env, jobject obj, jint nums){}
线程操作
注意:
1.代码的顺序 thread_func在上面
2.GetJavaVM方法获取全集JVM对象,控制线程的创建
#include <pthread.h> //导入线程.h
JavaVM *g_vm; //下面多线程程序用到
pthread_mutex_t thread_mutex;
void *thread_func(void *arg){
JNIEnv *env;
pthread_mutex_lock(&thread_mutex);
if (JNI_OK != (*g_vm)->AttachCurrentThread(g_vm, &env, NULL)){
LOGE("AttachCurrentThread() failed");
return NULL;
}
LOGE("AttachCurrentThread() --> thread:%d", (jint)arg);
(*g_vm)->DetachCurrentThread(g_vm);
LOGE("DetachCurrentThread() --> thread:%d", (jint)arg);
pthread_mutex_unlock(&thread_mutex);
pthread_exit(0);
return NULL;
}
JNIEXPORT void newJniThreads(JNIEnv* env, jobject obj, jint nums){
(*env)->GetJavaVM(env, &g_vm); //保存全局JavaVM
LOGE("GetJavaVM()");
pthread_t* pt = (pthread_t*)malloc(sizeof(pthread_t)* nums);
pthread_mutex_init(&thread_mutex, NULL);
int i;
for (i = 0 ; i < nums; i++){
pthread_create(&pt[i], NULL, &thread_func, (void*)i); //创建线程
}
free(pt);
}
分析一下 pthread_create:
pthread_create(&pt[i], NULL, &thread_func, (void*)i); //创建线程
第一个参数pt代表了数组pt的首地址指正,pt[i]也就是申请的第i个指针
&pt[i]即取地址值
参考
https://blog.csdn.net/fx677588/article/details/74857473
试验可参考
https://blog.csdn.net/oyhb_1992/article/details/77162326?locationNum=9&fps=1
帮助理解int a[5];
a的类型是int[5] 数组
&a的类型是int(*)[5] 指针——指向int[5]数组的指针
&a[0]的类型是int* 指针——指向int类型的指针
malloc和free成对存在:动态分配和释放内存
malloc返回的是分配内存的地址
pthread_mutex_lock 锁
pthread_mutex_lock(&thread_mutex); pthread_mutex_unlock(&thread_mutex);
pthread_exit(0)
字节缓冲区
JNIEXPORT jobject allocNativeBuffer(JNIEnv* env, jobject obj, jlong size){
void* buffer = malloc(size);
jobject directBuffer = (*env)->NewDirectByteBuffer(env, buffer, size);
LOGE("NewDirectByteBuffer() --> %d", (int)size);
return directBuffer;
}
JNIEXPORT void freeNativeBuffer(JNIEnv* env, jobject obj, jobject bufferRef)
{
void *buffer = (*env)->GetDirectBufferAddress(env, bufferRef);
strcpy(buffer, "123");
LOGE("GetDirectBufferAddress() --> %s", buffer);
free(buffer);
}
JAVA调用
Object obj = methods.allocNativeBuffer(16L); //分配字节缓冲区
methods.freeNativeBuffer(obj); //释放字节缓冲区
源码链接:https://pan.baidu.com/s/1_EhtJ2RoLCmtIu1pV5CdAQ 密码:sywq
文件路径:
++源代码(韦生强)\源代码\chapter7\7.6\7.6.2\jnimethods\jni++