-- C语言写JNI方法的过程中,不用编译JNI .h头文件,但是实现Java代码中定义的native方法时,还是得写全native方法的全路径,如JNIEXPORT void JNICALL Java_com_desaco_practiceknowing_native_1method_JniInvokeJava_reflectJava(JNIEnv *, jobject, jstring);
C++语言写JNI方法的过程中,要编译JNI .h头文件,并且引用头文件,写全native方法的全路径。
JNI接口与接口指针,JNI环境与指针,JNI二进制兼容,类加载器和JVM,JNI移位与反移位,JNI多线程与线程死锁?
> JNI 资源释放,获取上下文Context,加密
JNI 资源释放- https://blog.csdn.net/ccm_oliver/article/details/12781319
Android JNI获取上下文Context- https://blog.csdn.net/lb377463323/article/details/75315167Android学习JNI,使用JNI实现字符串加密- https://blog.csdn.net/longwang155069/article/details/47808209
JNI接口实现Java和C的交互- http://blog.csdn.net/linzhuowei0775/article/details/49965877
-- JNI 打印Log
#define LOGI(format, ...) __android_log_print(ANDROID_LOG_INFO, "(^_^)", format, ##__VA_ARGS__)
#define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "ProjectName", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , "ProjectName", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , "ProjectName", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , "ProjectName", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "ProjectName", __VA_ARGS__)
LOGI("user info----name:%s, age:%d, sex:%s.", "xxx", 18, "男");
LOGD("log.d 这是Jni中的log: Java_cn_hpc_cai_jni_JniLogDemo_test()");
LOGI("Log.i 这是Jni中的log: Java_cn_hpc_cai_jni_JniLogDemo_test()");
LOGD("########## i = %d", i);
-- 编写JNI需要注意的:
1. 错误检查
编写本地方法时,最常见的错误就是忘记检查是否发生了异常。所以我们需要注意在平时的编码中,注意检查和处理。
2. 不要向JNI函数传递非法参数
JNI函数不会检查参数是否正确,尽量保证函数的正确性。否则会发生未知的错误。
3. 区别 jclass 和 jobject
对象引用对应的是jobject,类引用对应的是jclass。像GetFieldID函数需要传入jclass作为参数,因为它是从一个类中获取某个字段的描述。而GetIntField函数则需要传入jobject,因为它从一个对象实例中获取字段的值。GetStaticXXXField传入的参数是jclass,顾名思义,static是静态的,属于类,而不属于对象。
4. jboolean数据截取问题
jboolean是一个8-bit unsigned的C类型。可以存储0~255的值。0对应JNI_FALSE,则1~255对应JNI_TRUE。但是32或者16位的值,如果低8位是0的话,就会被截取出来使用,即0。看如下代码:
5. 编程时,怎么用C
(1)尽量少写本地代码。
(2)让本地代码尽量独立。实际使用中,尽量让所有本地方法在同一个包甚至是同一个类。
6. ID唯一
本地代码可以创建多个引用并让其指向同一个对象。而字段和方法ID是唯一的。比如类A中定义个F方法,而B类从类A中继承了F方法。但通过GetMethodID方法获取的ID是相同的。
7. ID缓存
一般情况下,尽量使用全局缓存ID的方式来缓存。
8. 确保释放内存资源
在编写本地方法时,一定要记得在使用完毕资源后对其进行释放操作。尤其是在分支结构代码中。外部释放后,在分支代码中,如果代码返回,也需要立刻释放。
9. 过多的创建局部引用,需要及时手动释放
大量的创建局部引用会浪费不必要的内存。一个局部引用会导致它本身和它所指向的对象都得不到回收释放。尤其要注意长时间运行的方法,循环,工具函数。充分利用PushLocalFrame和PopLocalFrame方法来管理局部引用。
10. 不要使用失效的局部引用
局部引用只在一个本地方法的调用期间有效,方法执行完毕,局部引用就会被自动回收释放,本地代码不应该把局部引用存储到全局变量中,然后在其他地方使用。
11. 不能跨进程使用JNIEnv
JNIEnv这个指针只能在当前线程使用,不要再其它线程中跨进程使用。
-- Android开发实践:Java层与Jni层的数组传递- https://blog.csdn.net/xinchen200/article/details/25333047
JNI接口指针只在当前线程中有效。因此,本地方法不能将接口指针从一个线程传递到另一个线程中。实现JNI的虚拟机可将本地线程的数据分配和储存在JNI接口指针所指向的区域中。本地方法将JNI接口指针当作参数来接受。虚拟机在从相同的Java线程中对本地方法进行多次调用时,保证传递给该本地方法的接口指针是相同的。但是,一个本地方法可被不同的Java线程所调用,因此可以接受不同的JNI接口指针。
-- 在创建支持的C/C++程序中添加两个方法,分别是非静态和静态
public native String stringFromJNI1();
public static native String stringFromJNI2();
JNI代码:
extern "C"
JNIEXPORT jstring JNICALL
Java_fj_clover_ndktest_MainActivity_stringFromJNI1(JNIEnv *env, jobject instance) {
// TODO
return env->NewStringUTF(returnValue);
}
extern "C"
JNIEXPORT jstring JNICALL
Java_fj_clover_ndktest_MainActivity_stringFromJNI2(JNIEnv *env, jclass type) {
// TODO
return env->NewStringUTF(returnValue);
}