目录
1.4.2 Class and Interface Operations
1.4.4 Global and Local References
1.4.12 Native Method Registration
3 Basic Types, Strings, and Arrays
3.2.1 Converting to Native Strings
3.2.4 Other JNI String Functions
3.3.2 Accessing Arrays of Primitive Types
3.3.5 Accessing Arrays of Objects
4.1.1 Procedure for Accessing an Instance Field 实例域
4.1.3 Accessing Static Fields 类域
4.2.2 Forming the Method Descriptor
5.1 Local and Global References
8.1.4 Obtaining a JNIEnv Pointer in Arbitrary Contexts
8.3 Registering Native Methods
10.3 Confusing jclass with jobject
10.4 Truncating(截断) jboolean Arguments
10.6 Confusing IDs with References
10.7 Caching Field and Method IDs 缓存域ID(Filed ID)和方法ID(Method ID)
10.8 Terminating(结束) Unicode Strings
10.11 Retaining Virtual Machine Resources 防止内存泄漏
12.1 Primitive and Reference Types
12.3.4 Method Descriptors 方法描述符
13.1 Summary of the JNI Functions
13.1.1 Directly-Exported Invocation Interface Functions(直接导出的调用接口函数)
13.1.2 The JavaVM Interface(JavaVM接口)
13.1.3 Functions Defined in Native Libraries(Native库中定义的函数)
13.1.4 The JNIEnv Interface(JNIEnv接口)
1 github仓
以下所有测试用例托管在如下github仓:
GitHub - hanyuhang-hz/jni-functions: https://blog.youkuaiyun.com/u012906122/article/details/86688136
https://download.youkuaiyun.com/download/u012906122/11614608
本章整理了JNI函数接口的常用用法。由参考资料[1]中第13章"JNI Functions",我们可以将jni functions大致分为如下四大类:
1 由VM直接导出的调用接口函数
2 JavaVM接口
3 Native库中定义的函数
4 JNIEnv接口
/**
* Table of methods associated with a single class.
*/
static JNINativeMethod g_NativeMethods[] = {
/* name, signature, funcPtr */
{ "native_startTicks", "()V", (void*) Native_StartTicks },
{ "native_stopTicks", "()V", (void*) Native_StopTicks },
{ "native_stringOperations", "()V", (void*) Native_StringOperations },
{ "native_arrayOperations", "()V", (void*) Native_ArrayOperations },
{ "native_objectOperations", "()V", (void*) Native_ObjectOperations },
{ "native_classAndInterfaceOperations", "()V", (void*) Native_ClassAndInterfaceOperations },
{ "native_exceptions", "()V", (void*) Native_Exceptions },
{ "native_globalAndLocalReferences", "()V", (void*) Native_GlobalAndLocalReferences },
{ "native_instanceField", "()V", (void*) Native_InstanceField },
{ "native_staticField", "()V", (void*) Native_StaticField },
{ "native_instanceMethod", "()V", (void*) Native_InstanceMethod },
{ "native_staticMethod", "()V", (void*) Native_StaticMethod },
{ "native_javaVMInterface", "()V", (void*) native_JavaVMInterface },
{ "native_monitorOperations", "()V", (void*) native_monitorOperations },
};
1.1 由VM直接导出的调用接口函数
注意:这三个接口只是由VM为JNI导出的三个符号,一般不用用户调用!
1 JNI_GetDefaultJavaVMInitArgs
jint JNI_GetDefaultJavaVMInitArgs(void*);
返回Java虚拟机实现的默认配置。
2 JNI_CreateJavaVM
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
加载并初始化一个虚拟机实例。
3 JNI_GetCreatedJavaVMs
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
1.2 JavaVM接口
1 DestroyJavaVM
jint DestroyJavaVM()
卸载JavaVM并释放它的资源。
2 AttachCurrentThread
jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
绑定当前线程(一般是子线程)到一个虚拟机实例。
3 DetachCurrentThread
jint DetachCurrentThread()
使得当前线程脱离一个虚拟机实例。
4 GetEnv
jint GetEnv(void** env, jint version)
(1)用来检查当前线程是否绑定到给定的虚拟机实例,env设置为NULL,返回错误JNI_EDETACHED.
(2)用来获取其他接口,如JNIEnv的interfaces.
代码:
void *UpdateTicks(void *context) {
FUNCTION_ENTER;
TickContext *pctx = (TickContext *) context;
JavaVM *javaVM = pctx->javaVM;
JNIEnv *env;
//(1)因为UpdateTicks属于子线程,子线程并没有绑定到javaVM,所以res返回一定是error
jint ret = javaVM->GetEnv((void **)&env, JNI_VERSION_1_6);
LOGD_HYH;
if (JNI_OK != ret) {
LOGD_HYH;
//(2)绑定子线程到虚拟机实例,即初始化env
ret = javaVM->AttachCurrentThread(&env, NULL);
if (JNI_OK != ret) {
LOGE("AttachCurrentThread error,ret = %d", ret);
return NULL;
}
}
struct timeval beginTime, curTime, usedTime, leftTime;
const struct timeval kOneSecond = {
(__kernel_time_t) 1,
(__kernel_suseconds_t) 0
};
jmethodID timerId = env->GetMethodID(pctx->jniManagerClz, "updateTimer", "()V");
while (1) {
gettimeofday(&beginTime, NULL);
pthread_mutex_lock(&pctx->lock);
int done = pctx->done;
if (pctx->done) {
pctx->done = 0;
}
pthread_mutex_unlock(&pctx->lock);
if (done) {
break;
}
env->CallVoidMethod(pctx->jniManagerObj, timerId);
gettimeofday(&curTime, NULL);
timersub(&curTime, &beginTime, &usedTime);
timersub(&kOneSecond, &usedTime, &leftTime);
struct timespec sleepTime;
sleepTime.tv_sec = leftTime.tv_sec;
sleepTime.tv_nsec = leftTime.tv_usec * 1000;
if (sleepTime.tv_sec <= 1) {
nanosleep(&sleepTime, NULL);
} else {
LOGE("Process too long!");
break;
}
}
//(3)使得子线程脱离虚拟机实例
javaVM->DetachCurrentThread();
FUNCTION_QUIT;
return context;
}
1.3 Native库中定义的函数
1 JNI_OnLoad
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
当虚拟机加载一个native库时,调用JNI_OnLoad接口。即System.loadLibrary会调用JNI_OnLoad接口。获取JavaVM对象。
2 JNI_OnUnload
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
当虚拟机卸载一个native库时,调用JNI_OnUnload接口。
代码:
/*
* Returns the JNI version on success, -1 on failure.
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
FUNCTION_ENTER;
JNIEnv* env = NULL;
jint ret;
g_tickctx.javaVM = vm;
g_tickctx.done = 0;
//todo:g_tickctx.javaVM和g_monitorctx.javaVM 同一个即可
g_monitorctx.javaVM = vm;
if (JNI_OK != vm->GetEnv((void **) &env, JNI_VERSION_1_6))
{
LOGE("GetEnv error!!!");
return JNI_ERR;
}
ret = env->GetVersion();
LOGD("GetVersion:%x",ret);
if (Register_Hellolibs_Functions(env) != 0)
{
LOGE("Register_Hellolibs_Functions error!!!");
return JNI_ERR;
}
//20190102:test crash debug methods
//WillCrash();
/* success -- return valid version number */
ret = JNI_VERSION_1_6;
FUNCTION_QUIT;
return ret;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
{
FUNCTION_ENTER;
JNIEnv *env = NULL;
if (JNI_OK != vm->GetEnv((void **) &env, JNI_VERSION_1_6)) {
return;
}
if (NULL != env)
{
Unregisternatives(env);
}
FUNCTION_QUIT;
}
1.4 JNIEnv接口
1.4.1 Version Information
1 GetVersion
jint GetVersion()
返回JNIEnv interface的版本号
1.4.2 Class and Interface Operations
1 DefineClass
jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen)
从一个jbyte* buffer里定义一个类
2 FindClass
jclass FindClass(const char* name)
返回一个类的引用
3 GetSuperclass
jclass GetSuperclass(jclass clazz)
返回基类,除了java.lang.Object类(因为java.lang.Object是最基类)
代码:
JNIEXPORT void JNICALL Native_ClassAndInterfaceOperations(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
//(1)DefineClass
//(2)FindClass
jclass clazz = env->FindClass(HELLOLIBS_JNI_CLASS_NAME);
jboolean bool_x = env->IsInstanceOf(obj,clazz);
LOGD("bool_x:%d",bool_x);
//(3)GetSuperclass
jclass clazz_super = env->GetSuperclass(clazz);
jboolean bool_y = env->IsInstanceOf(obj,clazz_super);
LOGD("bool_y:%d",bool_y);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(clazz_super);
FUNCTION_QUIT;
}
1.4.3 Exceptions
1 FatalError
void FatalError(const char* msg)
打印信息并终止当前虚拟机实例
2 ExceptionCheck
jboolean ExceptionCheck()
检查当前线程是否发生了异常,若有异常返回JNI_TRUE,否则返回JNI_FALSE
3 ExceptionDescribe
void ExceptionDescribe()
JNI层打印异常的堆栈信息!!!
4 ExceptionClear
void ExceptionClear()
清除异常堆栈信息
5 Throw and ThrowNew
jint Throw(jthrowable obj)
丢弃一个现有的异常对象,在当前线程触发一个新的异常
jint ThrowNew(jclass clazz, const char* message)
在当前线程触发一个异常,并自定义输出异常信息
6 ExceptionOccurred
jthrowable ExceptionOccurred()
检查当前线程是否发生了异常,若有异常返回该异常的引用,否则返回NULL
代码:
JNIEXPORT void JNICALL Native_Exceptions(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
//(1)FatalError
// env->FatalError("FatalError!!!"); //will crash
jclass jCls = env->GetObjectClass(obj);
jmethodID jMid = env->GetStaticMethodID(jCls,"ExcepitonCallback","()V"); //C层获取Java层静态方法ExcepitonCallback
if(NULL != jMid){
env->CallStaticVoidMethod(jCls,jMid);
}
LOGD("JNI:ExcepitonCallback");
//(2)ExceptionCheck
if(env->ExceptionCheck()){
LOGE("print exception stack information!!!");
//(3)ExceptionDescribe
env->ExceptionDescribe();
//(4)ExceptionClear
env->ExceptionClear();
//(5)ThrowNew
JNI_ThrowNew(env,"java/lang/Exception","JNI throw exception1!!!");
//注意:退出前释放相关资源!
//return ;
}
jMid = env->GetStaticMethodID(jCls,"NormalCallback","()V");
if(NULL != jMid){
env->CallStaticVoidMethod(jCls,jMid);
}
LOGD("JNI:NormalCallback");
//(6)ExceptionOccurred
jthrowable jExc = env->ExceptionOccurred();
if(jExc){
env->ExceptionDescribe();
env->ExceptionClear();
JNI_ThrowNew(env,"java/lang/Exception","JNI throw exception2!!!");
//注意:退出前释放相关资源!
//return ;
}
env->DeleteLocalRef(jCls);
env->DeleteLocalRef(jExc);
FUNCTION_QUIT;
}
1.4.4 Global and Local References
1 NewGlobalRef,DeleteGlobalRef
jobject NewGlobalRef(jobject obj)
void DeleteGlobalRef(jobject globalRef)
(1)创建:
调用NewGlobalRef,基于全局引用,弱全局引用,局部引用创建
(2)会阻止GC回收所引用的对象:
只有手动释放全局引用后,垃圾回收机制才可以回收
(3)能跨函数使用,能跨线程使用:
(4)释放:
(i)自动释放:无自动释放
(ii)手动释放:(*env)->DeleteGlobalRef(env,g_cls_string)
2 NewWeakGlobalRef,DeleteWeakGlobalRef
jweak NewWeakGlobalRef(jobject obj)
void DeleteWeakGlobalRef(jweak obj)
(1)创建:
调用NewWeakGlobalRef,基于局部引用或全局引用创建
(2)不会阻止GC回收所引用的对象:
GC认为应该回收它的时候(比如内存紧张的时候)会进行自动回收,从而释放全局弱引用
(3)能跨函数使用,能跨线程使用:
(4)释放:
(i)自动释放:在GC认为应该回收它的时候(比如内存紧张的时候)会进行自动回收,释放全局弱引用
(ii)手动释放:(*env)->DeleteWeakGlobalRef(env,g_cls_string)
3 NewLocalRef,DeleteLocalRef
jobject NewLocalRef(jobject ref)
void DeleteLocalRef(jobject localRef)
(1)创建
调用NewLocalRef或其他JNI接口(FindClass、NewObject、NewCharArray)创建。
(2)会阻止GC回收所引用的对象
只有释放局部引用后GC才可以回收
(3)不能跨函数使用,不能跨线程使用
(4)释放
(i)自动释放:函数返回后局部引用的当前对象会被GC自动释放,所以局部引用是有内存溢出的风险的!例如:在函数体内有循环,循环里New局部引用会出现内存溢出!!!
JNIEXPORT void JNICALL f(JNIEnv *env,jobject obj){
for(int i=0;i<len;i++){
jobject strLocalref = env->NewLocalRef(strObject);
...
//如果没有手动释放strLocalref ,只有最后一个strLocalref 会被GC自动释放,其余strLocalref会出现native内存泄漏
//env->DeleteLocalRef(strLocalref );
}
}
(ii)手动释放:(*env)->DeleteLocalRef(env,local_ref)
代码:
static jclass g_strCls1;
static jclass g_strCls2;
JNIEXPORT void JNICALL Native_GlobalAndLocalReferences(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
//(1)全局引用
jclass strCls = env->FindClass("java/lang/String");
g_strCls1 = static_cast<jclass >(env->NewGlobalRef(strCls));
//(2)全局弱引用
g_strCls2 = static_cast<jclass >(env->NewWeakGlobalRef(strCls));
//(3)局部引用
jmethodID mid = env->GetMethodID(strCls,"<init>","([C)V");
jstring strUnicode = env->NewStringUTF("hanyuhang"); //(i)create jstring
const jchar *charUnicode = env->GetStringChars(strUnicode, nullptr); //(ii)jstring-->jchar*
jint len = env->GetStringUTFLength(strUnicode);
LOGD("GetStringUTFLength:%d",len);
jint len1 = env->GetStringLength(strUnicode);
LOGD("GetStringLength:%d",len1);
jcharArray charArr = env->NewCharArray(len);
env->SetCharArrayRegion(charArr,0,len,charUnicode); //(iii)jchar*-->jcharArray
jobject strObject = env->NewObject(strCls,mid,charArr); //(iv)jcharArray-->jobject(通过String构造函数)
jobject strLocalref = env->NewLocalRef(strObject); //(v)NewLocalRef
const char *charUtf = env->GetStringUTFChars(static_cast<jstring >(strLocalref),nullptr); //(vi)jobject-->char*
LOGD("charUtf:%s",charUtf);
//GetStringUTFChars/ReleaseStringUTFChars
env->ReleaseStringUTFChars(static_cast<jstring>(strLocalref),charUtf);
//(1)删除全局引用
env->DeleteGlobalRef(g_strCls1);
//(2)删除全局弱引用
env->DeleteWeakGlobalRef(g_strCls2);
//(3)删除局部引用
env->DeleteLocalRef(strCls);
env->DeleteLocalRef(strUnicode);
env->DeleteLocalRef(charArr);
env->DeleteLocalRef(strObject);
env->DeleteLocalRef(strLocalref);
FUNCTION_QUIT;
}
1.4.5 Object Operations
1 GetObjectClass
jclass GetObjectClass(jobject obj)
返回一个对象对应的类
2 IsInstanceOf
jboolean IsInstanceOf(jobject obj, jclass clazz)
检查一个对象是否是一个类的实例
3 NewObject
jobject NewObject(jclass clazz, jmethodID methodID, ...)
分配对象空间并构造该对象
4 AllocObject
jobject AllocObject(jclass clazz)
分配一个未初始化的对象
代码:
JNIEXPORT void JNICALL Native_ObjectOperations(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
//(1)GetObjectClass
jclass clz = env->GetObjectClass(obj);
//(2)IsInstanceOf
jboolean bool_x = env->IsInstanceOf(obj,clz);
LOGD("bool_x:%d",bool_x);
jclass stringCls = env->FindClass("java/lang/String");
if(NULL == stringCls){
LOGE("NULL == stringCls");
return ;
}
jmethodID methodId = env->GetMethodID(stringCls,"<init>","([C)V");
jstring strUnicode= env->NewStringUTF("testhellolibs!"); //(i)create jstring
jboolean isCopy = false;
const jchar *chars = env->GetStringChars(strUnicode,&isCopy); //(ii)jstring-->jchar*
int len = env->GetStringLength(strUnicode);
jcharArray charArray = env->NewCharArray(len); //(iii)create jcharArray
env->SetCharArrayRegion(charArray,0,len,chars); //(iv)jchar*-->jcharArray
//(3)NewObject
jstring strUnicode1 = (jstring)env->NewObject(stringCls,methodId,charArray); //jcharArray-->jstring
const char *charUTF = env->GetStringUTFChars(strUnicode1,&isCopy); //jstring-->char*
LOGD("charUTF:%s",charUTF);
env->DeleteLocalRef(clz);
env->DeleteLocalRef(stringCls);
env->DeleteLocalRef(charArray);
env->DeleteLocalRef(strUnicode);
env->DeleteLocalRef(strUnicode1);
FUNCTION_QUIT;
}
1.4.6 Instance Field Access
1 GetFieldID
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
获取类的成员域ID
2 Get<Type>Field,Set<Type>Field
jint GetIntField(jobject obj, jfieldID fieldID)
根据成员域ID获取类成员
void SetIntField(jobject obj, jfieldID fieldID, jint value)
根据成员域ID设置类成员
代码:
JNIEXPORT void JNICALL Native_InstanceField(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
//Jni局部变量命名习惯:j开头!!!
jclass jCls = env->GetObjectClass(obj);
jfieldID jWidthFid = env->GetFieldID(jCls,"mWidth","I");
int width = env->GetIntField(obj,jWidthFid);
LOGD("get width:%d",width);
env->SetIntField(obj,jWidthFid,8);
width = env->GetIntField(obj,jWidthFid);
LOGD("set width:%d",width);
//todo:GetObjectFiled
FUNCTION_QUIT;
}
1.4.7 Static Field Access
1 GetStaticFieldID
jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
获取类的静态成员域ID
2 GetStatic<Type>Field,SetStatic<Type>Field
jint GetStaticIntField(jclass clazz, jfieldID fieldID)
根据成员域ID获取类静态成员
void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
根据成员域ID设置类静态成员
代码:
void Native_StaticField(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
jclass jCls = env->FindClass(HELLOLIBS_JNI_CLASS_NAME);
if(jCls == nullptr){
return ;
}
jfieldID jWidthFid = env->GetStaticFieldID(jCls,"mStaicWidth","I");
if(jWidthFid == nullptr){
return ;
}
jint jWidth = env->GetStaticIntField(jCls,jWidthFid);
LOGD("get jNum:%d",jWidth);
env->SetStaticIntField(jCls,jWidthFid,88);
jWidth = env->GetStaticIntField(jCls,jWidthFid);
LOGD("set jNum:%d",jWidth);
env->DeleteLocalRef(jCls);
FUNCTION_QUIT;
}
1.4.8 Instance Method Calls
1 GetMethodID
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
获取一个类方法的方法ID
2 Call<Type>Method
jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...)
调用类方法
代码:
JNIEXPORT void JNICALL Native_InstanceMethod(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
jclass jCls = env->GetObjectClass(obj);
jmethodID jmid = env->GetMethodID(jCls,"InstanceMethodCallback","()V");
env->CallVoidMethod(obj,jmid);
FUNCTION_QUIT;
}
1.4.9 Static Method Calls
1 GetStaticMethodID
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
获取一个类静态方法的方法ID
2 CallStatic<Type>Method
jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
调用类静态方法
代码:
void Native_StaticMethod(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
jclass jCls = env->FindClass(HELLOLIBS_JNI_CLASS_NAME);
if(jCls == nullptr){
return ;
}
jmethodID static_mid = env->GetStaticMethodID(jCls,"StaticMethodCallback","()V");
if(static_mid == nullptr){
return ;
}
env->CallStaticVoidMethod(jCls,static_mid);
env->DeleteLocalRef(jCls); //删除jCls局部引用
FUNCTION_QUIT;
}
1.4.10 String Operations
char*(1字节)~UTF-8字符串
jchar*(2字节)~Unicode字符串
1 NewString
jstring NewString(const jchar* unicodeChars, jsize len)
通过jchar*创建一个jstring对象
2 NewStringUTF
jstring NewStringUTF(const char* bytes)
通过char*创建一个jstring对象
3 GetStringLength
jsize GetStringLength(jstring string)
返回jstring的长度
4 GetStringUTFLength
jsize GetStringUTFLength(jstring string)
返回jstring的长度
5 GetStringChars/ReleaseStringChars
const jchar* GetStringChars(jstring string, jboolean* isCopy)
通过jstring获取jchar*指针
void ReleaseStringChars(jstring string, const jchar* chars)
释放jstring对应的jchar*指针
6 GetStringUTFChars/ReleaseStringUTFChars
const char* GetStringUTFChars(jstring string, jboolean* isCopy)
通过jstring获取char*指针
void ReleaseStringUTFChars(jstring string, const char* utf)
释放jstring对应的char*指针
7 GetStringCritical/ReleaseStringCritical
const jchar* GetStringCritical(jstring string, jboolean* isCopy)
通过jstring获取jchar*指针
void ReleaseStringCritical(jstring string, const jchar* carray)
释放jstring对应的jchar*指针
8 GetStringRegion/GetStringUTFRegion
void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)
将jstring对象拷贝到jchar*内存
void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf)
将jstring对象拷贝到char*内存
代码:
JNIEXPORT void JNICALL Native_StringOperations(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
//(1)Unicode String Operations
jstring str = env->NewStringUTF("testhellolibs1");
jint len = env->GetStringUTFLength(str);
LOGD("len:%d",len);
const jchar *strchar = env->GetStringChars(str, nullptr);
for (jint i = 0; i < len; i++) {
LOGD("strchar:%c", strchar[i]);
}
//GetStringChars/ReleaseStringChars
env->ReleaseStringChars(str,strchar);
//虽然函数结束后,LocalRef str会自动释放,还是要养成手动释放的习惯
env->DeleteLocalRef(str);
//(2)UTF String Operations
jstring strutf = env->NewStringUTF("testhellolibs2");
jint lenutf = env->GetStringUTFLength(strutf);
LOGD("lenutf:%d",lenutf);
const char *strcharutf = env->GetStringUTFChars(strutf, nullptr);
for (int i = 0; i < lenutf; i++) {
LOGD("strcharutf:%c", strcharutf[i]);
}
//GetStringUTFChars/ReleaseStringUTFChars
env->ReleaseStringUTFChars(strutf,strcharutf);
env->DeleteLocalRef(strutf);
//(3)GetStringCritical
jstring strcritical = env->NewStringUTF("testhellolibs3");
jint lencritical = env->GetStringUTFLength(strcritical);
LOGD("lencritical:%d",lencritical);
const jchar* jcharscritical = env->GetStringCritical(strcritical,NULL);
for(int i = 0;i < lencritical;i++){
LOGD("jcharscritical:%c", jcharscritical[i]);
}
//GetStringCritical/ReleaseStringCritical
env->ReleaseStringCritical(strcritical,jcharscritical);
env->DeleteLocalRef(strcritical);
//(4)GetStringUTFRegion
jstring strregion = env->NewStringUTF("testhellolibs4");
jint lenregion = env->GetStringUTFLength(strregion);
LOGD("lenregion:%d",lenregion);
char buff1[128];
char* pBuff1 = buff1;
env->GetStringUTFRegion(strregion,0,lenregion,pBuff1);
for(int i=0;i < lenregion;i++){
LOGD("buff1:%c",buff1[i]);
}
env->DeleteLocalRef(strregion);
FUNCTION_QUIT;
}
1.4.11 Array Operations
1 GetArrayLength
jsize GetArrayLength(jarray array)
返回数组元素个数
2 NewObjectArray
jobjectArray NewObjectArray(jsize length, jclass elementClass,jobject initialElement)
创建对象数组
3 New<Type>Array
jintArray NewIntArray(jsize length)
创建基本类型数组(int,char...)
4 GetObjectArrayElement/SetObjectArrayElement
jobject GetObjectArrayElement(jobjectArray array, jsize index)
获取对象数组元素
void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
设置对象数组元素
5 Get<Type>ArrayElements/Release<Type>ArrayElements
jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
获取基本类型数组内存指针
void ReleaseIntArrayElements(jintArray array, jint* elems,jint mode)
释放基本类型数组内存
6 Get<Type>ArrayRegion/Set<Type>ArrayRegion
void GetIntArrayRegion(jintArray array, jsize start, jsize len,jint* buf)
拷贝jintArray到jint*
void SetIntArrayRegion(jintArray array, jsize start, jsize len,const jint* buf)
拷贝const jint*到jintArray
代码:
JNIEXPORT void JNICALL Native_ArrayOperations(JNIEnv *env, jobject obj) {
FUNCTION_ENTER;
//(1)基本类型
//(i)NewIntArray
int len = 3;
jintArray intArray = env->NewIntArray(len);
if (NULL == intArray) {
LOGD("NULL == ret");
return ;
}
//(ii)GetArrayLength
jint arrayLen = env->GetArrayLength(intArray);
LOGD("arrayLen:%d", arrayLen);
//(iii)GetIntArrayRegion/SetIntArrayRegion
int tmp[3]={7,8,9};
env->SetIntArrayRegion(intArray,0,arrayLen,tmp);
int array[3];
env->GetIntArrayRegion(intArray,0,arrayLen,array);
for(int i=0;i<arrayLen;i++){
LOGD("tmp1:%d ",array[i]);
}
//(iv)GetIntArrayElements/ReleaseIntArrayElements
jboolean isCopy;
jint *c_array = env->GetIntArrayElements(intArray,&isCopy);
LOGD("isCopy:%d ",isCopy);
if(c_array == nullptr){
LOGE("GetIntArrayElements error!!!");
return ;
}
for(int i = 0;i<arrayLen;i++){
LOGD("get:%d ",c_array[i]);
}
env->ReleaseIntArrayElements(intArray,c_array,0);
env->DeleteLocalRef(intArray);
//(2)Object类型
int lenObject = 5;
jclass stringArrCls = env->FindClass("java/lang/String");
if (NULL == stringArrCls) {
LOGD("NULL == stringArrCls");
return ;
}
//(i)NewObjectArray
jobjectArray objectArray = env->NewObjectArray(lenObject, stringArrCls, 0); //0:jobject initialElement
if (NULL == objectArray) {
LOGD("NULL == objectArray");
return ;
}
//(ii)GetArrayLength
jint arrayLenObject = env->GetArrayLength(objectArray);
LOGD("arrayLenObject:%d", arrayLenObject);
//(iii)GetObjectArrayElement/SetObjectArrayElement
char *sa[]={"Hello","Han","yu","hang","!!!"};
for(int i=0;i<arrayLenObject;i++){
jstring str = env->NewStringUTF(sa[i]);
env->SetObjectArrayElement(objectArray,i,str);
env->DeleteLocalRef(str);
}
for(int i=0;i<arrayLenObject;i++){
jstring str = (jstring)env->GetObjectArrayElement(objectArray,i);
jboolean isCopy;
const char *chars = env->GetStringUTFChars(str, &isCopy);
LOGD("isCopy:%d,chars:%s",isCopy,chars);
env->ReleaseStringUTFChars(str,chars);
}
env->DeleteLocalRef(objectArray);
FUNCTION_QUIT;
}
1.4.12 Native Method Registration
1 RegisterNatives/UnregisterNatives
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)
注册native方法
jint UnregisterNatives(jclass clazz)
注销native方法
代码:
jint Register_Hellolibs_Functions(JNIEnv * env)
{
FUNCTION_ENTER;
jclass clazz = env->FindClass(HELLOLIBS_JNI_CLASS_NAME);
if (clazz == 0)
{
return -1;
}
/**
* Table of methods associated with a single class.
*/
static JNINativeMethod g_NativeMethods[] = {
/* name, signature, funcPtr */
{ "native_startTicks", "()V", (void*) Native_StartTicks },
{ "native_stopTicks", "()V", (void*) Native_StopTicks },
{ "native_stringOperations", "()V", (void*) Native_StringOperations },
{ "native_arrayOperations", "()V", (void*) Native_ArrayOperations },
{ "native_objectOperations", "()V", (void*) Native_ObjectOperations },
{ "native_classAndInterfaceOperations", "()V", (void*) Native_ClassAndInterfaceOperations },
{ "native_exceptions", "()V", (void*) Native_Exceptions },
{ "native_globalAndLocalReferences", "()V", (void*) Native_GlobalAndLocalReferences },
{ "native_instanceField", "()V", (void*) Native_InstanceField },
{ "native_staticField", "()V", (void*) Native_StaticField },
{ "native_instanceMethod", "()V", (void*) Native_InstanceMethod },
{ "native_staticMethod", "()V", (void*) Native_StaticMethod },
{ "native_javaVMInterface", "()V", (void*) native_JavaVMInterface },
{ "native_monitorOperations", "()V", (void*) native_monitorOperations },
};
jint nMethods = sizeof(g_NativeMethods) / sizeof(JNINativeMethod);
jint res = env->RegisterNatives(clazz, g_NativeMethods, nMethods);
if (res < 0)
{
return -1;
}
env->DeleteLocalRef(clazz);
FUNCTION_QUIT;
return 0;
}
static void UnregisterNativeMethods(JNIEnv* env, const char* className)
{
FUNCTION_ENTER;
jclass clazz;
clazz = env->FindClass(className);
if (NULL == clazz) return;
if (NULL != env)
{
env->UnregisterNatives(clazz);
}
FUNCTION_QUIT;
}
1.4.13 Monitor Operations
1 MonitorEnter
jint MonitorEnter(jobject obj)
进入临界区
2 MonitorExit
jint MonitorExit(jobject obj)
退出临界区
代码:
void native_monitorOperations(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
//(1)创建线程monitorThread2
pthread_t threadInfo_;
pthread_attr_t threadAttr_;
pthread_attr_init(&threadAttr_);
pthread_attr_setdetachstate(&threadAttr_, PTHREAD_CREATE_DETACHED);
jclass clz = env->GetObjectClass(obj);
g_monitorctx.jniManagerClz = static_cast<jclass>(env->NewGlobalRef(clz)); //static_cast<jclass>:jobject-->jclass
g_monitorctx.jniManagerObj = env->NewGlobalRef(obj);
int res = pthread_create(&threadInfo_, &threadAttr_,monitorThread2, &g_monitorctx);
assert(res == 0);
//(2)MonitorEnter/MonitorExit
if(env->MonitorEnter(obj)!=JNI_OK){
LOGE("MonitorEnter error!!!");
return ;
}
//进入临界区
for(int i=0;i<20;i++){
globle_monitor++;
LOGD("globle_monitor:%d thread:%lu",globle_monitor,pthread_self());
}
if(env->MonitorExit(obj)!=JNI_OK){
LOGE("MonitorExit error!!!");
return ;
}
//(3)pthread_attr_destroy
pthread_attr_destroy(&threadAttr_);
FUNCTION_QUIT;
}
1.4.14 JavaVM Interface
1 GetJavaVM
jint GetJavaVM(JavaVM** vm)
获取当前虚拟机JavaVM指针
代码:
void native_JavaVMInterface(JNIEnv *env,jobject obj){
FUNCTION_ENTER;
static JavaVM *pJavaVM = nullptr;
env->GetJavaVM(&pJavaVM);
LOGD("pJavaVM:%p",pJavaVM);
FUNCTION_QUIT;
}
1.5 参考资料
[1]The Java™ Native Interface Programmer’s Guide and Specification.pdf
[2]JNI/NDK开发指南
https://blog.youkuaiyun.com/xyang81/article/category/2759987
3 Basic Types, Strings, and Arrays
3.1 A Simple Native Method
JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt){
}
JNIEXPORT和JNICALL宏(jni.h头文件定义)确保这个函数会从native库中导出.
3.2 Accessing Strings
3.2.1 Converting to Native Strings
JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const char *str;
str = (*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str); //Freeing Native String Resources
/* We assume here that the user does not type more than 127 characters */
scanf("%s", buf);
return (*env)->NewStringUTF(env, buf); //Constructing New Strings
}
3.2.4 Other JNI String Functions
UTF-8格式的字符串以'\0'结束,Unicode格式的字符串不以'\0'结束.
(1)UTF-8 char 1字节
GetStringUTFChars/ReleaseStringUTFChars
(2)Unicode jchar 2字节
GetStringChars/ReleaseStringChars
3.3 Accessing Arrays
分两种情况:
(1)基本元素数组
(2)对象数组.
3.3.1 Accessing Arrays in C
拷贝基本元素数组:
JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jint buf[10];
jint i, sum = 0;
(*env)->GetIntArrayRegion(env, arr, 0, 10, buf); //arr-->buf
for (i = 0; i < 10; i++) {
sum += buf[i];
}
return sum;
}
GetIntArrayRegion的第三个参数0表示arr的起始索引,第四个参数10表示要拷贝的元素个数.
3.3.2 Accessing Arrays of Primitive Types
获取基本元素数组的指针:
GetIntArrayElements/ReleaseIntArrayElements
JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jint *carr;
jint i, sum = 0;
carr = (*env)->GetIntArrayElements(env, arr, NULL);
if (carr == NULL) {
return 0; /* exception occurred */
}
for (i=0; i<10; i++) {
sum += carr[i];
}
(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
return sum;
}
3.3.5 Accessing Arrays of Objects
获取/设置对象数组:
GetObjectArrayElement/SetObjectArrayElement
JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env,jclass cls,int size)
{
jobjectArray result;
int i;
jclass intArrCls = (*env)->FindClass(env, "[I"); //int[]!!!
if (intArrCls == NULL) {
return NULL; /* exception thrown */
}
result = (*env)->NewObjectArray(env, size, intArrCls,NULL);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
for (i = 0; i < size; i++) {
jint tmp[256]; /* make sure it is large enough! */
int j;
jintArray iarr = (*env)->NewIntArray(env, size);
if (iarr == NULL) {
return NULL; /* out of memory error thrown */
}
for (j = 0; j < size; j++) {
tmp[j] = i + j;
}
(*env)->SetIntArrayRegion(env, iarr, 0, size, tmp);
(*env)->SetObjectArrayElement(env, result, i, iarr);
(*env)->DeleteLocalRef(env, iarr);
}
return result;
}
如上代码创建一个二维数组,如果size设置为3,二维数组如下:
0 1 2
1 2 3
2 3 4
扩充:根据row和column创建二维数组
JNIEXPORT jobjectArray JNICALL createInt2DArray(JNIEnv *env, jobject obj, int size_row,
int size_column)
{
int i,j;
//(1)int[]
jclass intArrCls = (*env)->FindClass(env, "[I"); //可以这么用!!!
if (intArrCls == NULL) {
return NULL; /* exception thrown */
}
//(2)int[size_row][]
jobjectArray result = (*env)->NewObjectArray(env, size_row, intArrCls,NULL);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
//(3)int[size_row][size_column]
for (i = 0; i < size_row; i++) {
jint line[256]; /* make sure it is large enough! */
jintArray iarr = (*env)->NewIntArray(env, size_column);
if (iarr == NULL) {
return NULL; /* out of memory error thrown */
}
for (j = 0; j < size_row; j++) {
line[j] = i + j;
}
(*env)->SetIntArrayRegion(env, iarr, 0, size_column, line);
(*env)->SetObjectArrayElement(env, result, i, iarr);
(*env)->DeleteLocalRef(env, iarr);
}
//不能在函数返回前删除,应该在外部用完后删除
//(*env)->DeleteLocalRef(env,result); //错
return result;
}
4 Fields and Methods
4.1 Accessing Fields 域
域有两种:实例域和类域.
4.1.1 Procedure for Accessing an Instance Field 实例域
实例域:每个类的实例有各自的变量.
java层:
class Test{
private String s_test;
}
jni层:
jfieldID fid = (*env)->GetFieldID(env, cls, "s_test", "Ljava/lang/String;");
jstring jstr = (*env)->GetObjectField(env, obj, fid);
4.1.3 Accessing Static Fields 类域
类域:每个类的实例有相同的变量,因为对于类来说,static唯一.
class StaticFielcdAccess {
private static int si;
private native void accessField();
public static void main(String args[]) {
StaticFieldAccess c = new StaticFieldAccess();
StaticFieldAccess.si = 100;
c.accessField();
System.out.println("In Java:");
System.out.println(" StaticFieldAccess.si = " + si);
}
static {
System.loadLibrary("StaticFieldAccess");
}
}
JNIEXPORT void JNICALL Java_StaticFieldAccess_accessField(JNIEnv *env, jobject obj)
{
jfieldID fid; /* store the field ID */
jint si;
/* Get a reference to obj’s class */
jclass cls = (*env)->GetObjectClass(env, obj);
printf("In C:\n");
/* Look for the static field si in cls */
fid = (*env)->GetStaticFieldID(env, cls, "si", "I");
if (fid == NULL) {
return; /* field not found */
}
/* Access the static field si */
si = (*env)->GetStaticIntField(env, cls, fid);
printf(" StaticFieldAccess.si = %d\n", si);
(*env)->SetStaticIntField(env, cls, fid, 200);
}
实例域和类域的区别:
(1)调用函数不同:
GetStaticFieldID and GetFieldID
SetStaticIntField and SetIntField
(2)类域对应的是Static函数,对于类来说static变量唯一。
4.2 Calling Methods
class InstanceMethodCall {
private native void nativeMethod();
private void callback() {
System.out.println("In Java");
}
public static void main(String args[]) {
InstanceMethodCall c = new InstanceMethodCall();
c.nativeMethod();
}
static {
System.loadLibrary("InstanceMethodCall");
}
}
JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj)
{
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V");
if (mid == NULL) {
return; /* method not found */
}
printf("In C\n");
(*env)->CallVoidMethod(env, obj, mid);
}
4.2.2 Forming the Method Descriptor
Do not let C function prototypes such as “int f(void)” mislead you to thinking that "(V)I" is a valid method descriptor. Use "()I" instead.
4.2.3 Calling Static Methods
static方法和local方法的不同点:
static方法形参是cls,local方法的形参是obj.
class StaticMethodCall {
private native void nativeMethod();
private static void callback() {
System.out.println("In Java");
}
public static void main(String args[]) {
StaticMethodCall c = new StaticMethodCall();
c.nativeMethod();
}
static {
System.loadLibrary("StaticMethodCall");
}
}
JNIEXPORT void JNICALL Java_StaticMethodCall_nativeMethod(JNIEnv *env, jobject obj)
{
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "callback", "()V");
if (mid == NULL) {
return; /* method not found */
}
printf("In C\n");
(*env)->CallStaticVoidMethod(env, cls, mid);
}
4.3 Invoking Constructors
构造函数"<init>"
jstring MyNewString(JNIEnv *env, jchar *chars, jint len)
{
jclass stringClass;
jmethodID cid;
jcharArray elemArr;
jstring result;
stringClass = (*env)->FindClass(env, "java/lang/String");
if (stringClass == NULL) {
return NULL; /* exception thrown */
}
/* Get the method ID for the String(char[]) constructor */
cid = (*env)->GetMethodID(env, stringClass,"<init>", "([C)V");
if (cid == NULL) {
return NULL; /* exception thrown */
}
/* Create a char[] that holds the string characters */
elemArr = (*env)->NewCharArray(env, len);
if (elemArr == NULL) {
return NULL; /* exception thrown */
}
(*env)->SetCharArrayRegion(env, elemArr, 0, len, chars);
/* Construct a java.lang.String object */
result = (*env)->NewObject(env, stringClass, cid, elemArr);
/* Free local references */
(*env)->DeleteLocalRef(env, elemArr);
(*env)->DeleteLocalRef(env, stringClass);
return result;
}
5 Local and Global References
local references, global references, and weak global references.
5.1 Local and Global References
5.1.1 Local References
注意:不要将JNI的Local Reference存入到一个static变量里.
来看下如下代码的stringClass:
/* This code is illegal */
jstring MyNewString(JNIEnv *env, jchar *chars, jint len)
{
static jclass stringClass = NULL;
jmethodID cid;
jcharArray elemArr;
jstring result;
if (stringClass == NULL) {
stringClass = (*env)->FindClass(env,"java/lang/String");
if (stringClass == NULL) {
return NULL;
}
}
/* It is wrong to use the cached stringClass here, because it may be invalid. */
cid = (*env)->GetMethodID(env, stringClass,"<init>", "([C)V");
elemArr = (*env)->NewCharArray(env, len);
result = (*env)->NewObject(env, stringClass, cid, elemArr);
(*env)->DeleteLocalRef(env, elemArr);
return result;
}
JNIEXPORT jstring JNICALL Java_C_f(JNIEnv *env, jobject this)
{
char *c_str = ...;
...
return MyNewString(c_str,len);
}
//Java层调用两次Java_C_f接口:
...
... = C.f(); // The first call is perhaps OK.
... = C.f(); // This would use an invalid local reference.
...
解释:
FindClass返回了java.lang.String的localRef(stringClass),在第一次C.f()方法return后,虚拟机释放了C.f()期间创建的所有localRef,包括stringClass.
但stringClass!=NULL,第二次调用C.f()时,MyNewString()不会再调用FindClass,不会生成新的localRef.此时stringClass是无效的localRef,可能导致系统crash.
两种方式invalidate localRef:
(1)虚拟机自动释放(函数return)
(2)DeleteLocalRef
5.1.2 Global References
Unlike local references, which are created by most JNI functions, global references are created by just one JNI function, NewGlobalRef.
/* This code is OK */
jstring MyNewString(JNIEnv *env, jchar *chars, jint len)
{
static jclass stringClass = NULL;
...
if (stringClass == NULL) {
jclass localRefCls = (*env)->FindClass(env, "java/lang/String");
if (localRefCls == NULL) {
return NULL; /* exception thrown */
}
/* Create a global reference */
stringClass = (*env)->NewGlobalRef(env, localRefCls);
if (stringClass == NULL) {
return NULL; /* out of memory exception thrown */
}
/* The local reference is no longer useful */
(*env)->DeleteLocalRef(env, localRefCls);
}
...
}
5.1.3 Weak Global References
They are created using NewGlobalWeakRef and freed using DeleteGlobalWeakRef.
全局引用保证对象不被垃圾回收,弱全局引用不会保证对象不被垃圾回收.
Caching the class in a weak global reference allows the mypkg.MyCls2 class to still be unloaded:
JNIEXPORT void JNICALL Java_mypkg_MyCls_f(JNIEnv *env, jobject self)
{
static jclass myCls2 = NULL;
if (myCls2 == NULL) {
jclass myCls2Local =
(*env)->FindClass(env, "mypkg/MyCls2");
if (myCls2Local == NULL) {
return; /* can’t find class */
}
myCls2 = NewWeakGlobalRef(env, myCls2Local);
if (myCls2 == NULL) {
return; /* out of memory */
}
}
... /* use myCls2 */
}
We would have to check whether the cached weak reference still points to a live class object or points to a class object that has already been garbage collected. The next section will explain how to perform such checks on weak global references.
5.1.4 Comparing References
(*env)->IsSameObject(env, obj1, obj2)
The rules for weak global references are somewhat different.
检查一个弱全局引用是否还指向一个Object:
You can use IsSameObject to determine whether a non-NULL weak global reference still points to a live object. Suppose wobj is a non-NULL weak global reference. The following call:
(*env)->IsSameObject(env, wobj, NULL)
returns JNI_TRUE if wobj refers to an object that has already been collected,
and returns JNI_FALSE if wobj still refers to a live object
8 Additional JNI Features
8.1 JNI and Threads
8.1.1 Constraints 约束
(1)JNIEnv只在单线程里有效.不能在线程间传递JNIEnv.
(2)局部引用(Local Reference)只在创建它们的线程里有效。如果是多线程,需要将局部引用(Local Reference)转换为全局引用(Global Reference).
8.1.2 Monitor Entry and Exit
同java层synchronized,jni使用MonitorEnter和MonitorExit.
if ((*env)->MonitorEnter(env, obj) != JNI_OK) {
... /* error handling */
}
... /* synchronized block */
if ((*env)->MonitorExit(env, obj) != JNI_OK) {
... /* error handling */
};
MonitorEnter and MonitorExit作用在jobject上。
8.1.3 Monitor Wait and Notify
java层对应的线程同步方法有Object.wait, Object.notify, and Object.notifyAll.
/* precomputed method IDs */
static jmethodID MID_Object_wait;
static jmethodID MID_Object_notify;
static jmethodID MID_Object_notifyAll;
void JNU_MonitorWait(JNIEnv *env, jobject object, jlong timeout)
{
(*env)->CallVoidMethod(env, object, MID_Object_wait, timeout);
}
void JNU_MonitorNotify(JNIEnv *env, jobject object)
{
(*env)->CallVoidMethod(env, object, MID_Object_notify);
}
void JNU_MonitorNotifyAll(JNIEnv *env, jobject object)
{
(*env)->CallVoidMethod(env, object, MID_Object_notifyAll);
}
8.1.4 Obtaining a JNIEnv Pointer in Arbitrary Contexts
JavaVM *jvm; /* already set */
f()
{
JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
... /* use env */
}
当前线程如果已经连接到java虚拟机,AttachCurrentThread便会返回属于当前线程的JNIEnv.
8.3 Registering Native Methods
JNI函数的两种注册方式:动态注册和静态注册.
(1)静态注册:
虚拟机在已经加载的native库中寻找native方法。
(2)动态注册:
程序员通过注册函数指针动态链接native方法。
例如:
static JNINativeMethod g_NativeMethods[] = {
/* name, signature, funcPtr */
{"native_faceId_init", "()I", (void *) native_faceId_init},
{"native_setStringArray", "(JLjava/lang/String;[Ljava/lang/String;)I", (void *) native_setStringArray},
{"native_faceId_register", "(JII[BLjava/lang/String;I)I", (void *) native_faceId_register},
{"native_faceId_recognize", "(JII[BZ)I", (void *) native_faceId_recognize},
{"native_faceId_uninit", "(J)I", (void *) native_faceId_uninit},
};
jint nMethods = sizeof(g_NativeMethods) / sizeof(JNINativeMethod);
jint res = env->RegisterNatives(clazz, g_NativeMethods, nMethods);
if (res < 0) {
return -1;
}
8.6 JNI Programming in C++
(1)形参:
C++:
jclass cls = env->FindClass("java/lang/String");
C:
jclass cls = (*env)->FindClass(env, "java/lang/String");
(2)强制转换:
C++:
jstring jstr = (jstring)env->GetObjectArrayElement(arr, i);
C:
jstring jstr = (*env)->GetObjectArrayElement(env, arr, i);
C++:
jclass cls = static_cast<jclass>(jobject);
jobject转换为jclass需要static_cast<jclass>(jobject),即基类转换为子类需要static_cast<>.
10 Traps and Pitfalls
10.3 Confusing jclass with jobject
注意:不要混淆jclass和jobject
Class references(类引用) correspond to java.lang.Class instances, which represent class types.
Instance references(实例引用) correspond to arrays and instances of java.lang.Object or one of its subclasses.
An operation such as GetFieldID, which takes a jclass, is a class operation because it gets the field descriptor from a class.
In contrast, GetIntField, which takes a jobject, is an instance operation because it gets the value of a field from an instance.
10.4 Truncating(截断) jboolean Arguments
注意:截断jboolean类型
jboolean是一个8位的unsigned类型,值范围为0~255.0对应JNI_FALSE,1~255对应JNI_TRUE.
对于32位或16位的变量a赋值给jboolean时,如果a的低8位为0会出现逻辑问题.
代码:
void print(jboolean condition)
{
if (condition) {
printf("true\n");
} else {
printf("false\n");
}
}
//错误
int n = 256;
print (n); //==>JNI_FALSE
对于32位变量n(256的低8位为0),本应该判断为JNI_TRUE,实际判断为JNI_FLASE.
改为如下后逻辑正确:
//正确
int n = 256;
print(n ? JNI_TRUE : JNI_FALSE);
10.6 Confusing IDs with References
注意:不要混淆ID和引用
The JNI exposes objects as references. Classes, Strings, and Arrays are special types of references.
The JNI exposes methods and fields as IDs. An ID is not a reference.
Native code may create multiple references that refer to the same object.
In contrast,a unique field or method ID is derived for the same definition of a field or a method.
If class A defines method f and class B inherits f from A, the two GetMethodID calls in the following code always return the same result:
jmethodID MID_A_f = (*env)->GetMethodID(env, A, "f", "()V");
jmethodID MID_B_f = (*env)->GetMethodID(env, B, "f", "()V");
10.7 Caching Field and Method IDs 缓存域ID(Filed ID)和方法ID(Method ID)
下面举例说明了没有缓存域ID可能带来的bug:
class C {
private int i;
native void f();
}
(1)不缓存域ID:
1) 获取对象的类,GetObjectClass.
2) 找出变量i的域ID,GetFieldID.
3) 基于域ID获取域值,GetIntField.
// No field IDs cached.
JNIEXPORT void JNICALL Java_C_f(JNIEnv *env, jobject this) {
jclass cls = (*env)->GetObjectClass(env, this);
jfieldID fid = (*env)->GetFieldID(env, cls, "i", "I");
int ival = (*env)->GetIntField(env, this, fid);
}
如果我们定义C类的子类D,D类里也声明一个变量i,会出现问题:
// Trouble in the absence of ID caching
class D extends C {
private int i;
D() {
f(); // inherited from C
}
}
当D的构造函数调用C的接口f时,native接口Java_C_f里的jobject参数是D的对象,cls指向D类,fid代表的是D.i。所以ival里的值是D.i而不是C.i。这可能不是我们想要的。
如何解决?
(2)缓存域ID:
解决办法是当你确定要使用哪个类时,缓存对应的域ID。见如下代码:
// Version that caches IDs in static initializers
class C {
private int i;
native void f();
private static native void initIDs();
static {
initIDs(); // Call an initializing native method
}
}
static jfieldID FID_C_i; //缓存域ID
JNIEXPORT void JNICALL Java_C_initIDs(JNIEnv *env, jclass cls) {
/* Get IDs to all fields/methods of C that native methods will need. */
FID_C_i = (*env)->GetFieldID(env, cls, "i", "I");
}
JNIEXPORT void JNICALL Java_C_f(JNIEnv *env, jobject this) {
int ival = (*env)->GetIntField(env, this, FID_C_i);
/* ival is always C.i, not D.i */
}
域ID缓存在C类的静态方法initIDs里。这就保证了FID_C_i缓存的是C类变量i的域.
10.8 Terminating(结束) Unicode Strings
用GetStringChars或GetStringCritical接口获取的Unicode字符串不是以null结尾的。一般用GetStringLength来获取Unicode字符串的长度。
10.11 Retaining Virtual Machine Resources 防止内存泄漏
A common mistake in native methods is forgetting to free virtual machine resources.
JNIEXPORT void JNICALL
Java_pkg_Cls_f(JNIEnv *env, jclass cls, jstring jstr)
{
const jchar *cstr =
(*env)->GetStringChars(env, jstr, NULL);
if (cstr == NULL) {
return;
}
...
if (...) { /* exception occurred */
/* misses a ReleaseStringChars call */
return;
}
...
/* normal return */
(*env)->ReleaseStringChars(env, jstr, cstr);
}
12 JNI Types
12.1 Primitive and Reference Types
12.1.1 Primitive Types 基本类型
注意:
Java的char和Jni的jchar是Unicode格式,占两个字节.
C|C++中的char对应的是Native Type的jbyte,不是Native Type的jchar.
12.1.2 Reference Types 引用类型
JNI reference types are organized in the hierarchy shown below.
12.3 String Formats
12.3.2 Class Descriptors 类描述符
It can be derived from a fully qualified class or interface name as defined in The Java™ Language Specification by substituting(替换) the “.” character with the “/” character.
For example, the class descriptor for java.lang.String is:"java/lang/String"
Array classes are formed using the “[” character followed by the field descriptor (§12.3.3) of the element type. The class descriptor for “int[]” is:"[I"
and the class descriptor for “double[][][]” is:"[[[D"
12.3.3 Field Descriptors 域描述符
Field descriptors of reference types begin with the “L” character, followed by the class descriptor, and terminated by the “;” character.
12.3.4 Method Descriptors 方法描述符
"V" is used to denote the void method return type.
Constructors use "V" as their return type, and use "<init>" as their name.
//构造函数String(char[] chars);
jclass strCls = env->FindClass("java/lang/String");
jmethodID mid = env->GetMethodID(strCls,"<init>","([C)V");
13 JNI Functions
13.1 Summary of the JNI Functions
13.1.1 Directly-Exported Invocation Interface Functions(直接导出的调用接口函数)
A virtual machine implementation directly exports the following three functions as part of the invocation interface:
Note these are the only symbols exported for JNI by the VM.
//注意,这三个接口只是由VM为JNI导出的三个符号!!!不用用户调用!!!
1 JNI_GetDefaultJavaVMInitArgs
2 JNI_CreateJavaVM
3 JNI_GetCreatedJavaVMs
13.1.2 The JavaVM Interface(JavaVM接口)
The JavaVM interface is a pointer to a pointer to a function table.The first three entries in the function table are reserved,The remaining four entries are part of the invocation interface:
1 DestroyJavaVM
2 AttachCurrentThread
3 DetachCurrentThread
4 GetEnv
13.1.3 Functions Defined in Native Libraries(Native库中定义的函数)
When the virtual machine implementation loads a native library, it searches for and invokes the exported function entry JNI_OnLoad.
When the virtual machine implementation unloads a native library, it searches for and invokes the exported function entry JNI_OnUnload.
1 JNI_OnLoad
2 JNI_OnUnload
13.1.4 The JNIEnv Interface(JNIEnv接口)
A JNIEnv interface pointer is valid only in a particular thread,
the JavaVM interface pointer is valid for all threads in a virtual machine instance.
A JNIEnv interface pointer is a pointer to a pointer to a function table.
Seven entries are set NULL.The remainder of this chapter will cover all the entries in the JNIEnv interface in detail. For now we give a high-level overview.
来做个总结:
(1)Version Information
1 GetVersion:返回JNIEnv interface的版本号
(2)Class and Interface Operations
1 DefineClass:从一个buffer里定义一个类
2 FindClass:返回一个类的引用
类名:"java/lang/String" //class name
签名:"Ljava/lang/Object" //signature
env->FindClass("java/lang/String");
env->FindClass(HELLOLIBS_JNI_CLASS_NAME);
#define HELLOLIBS_JNI_CLASS_NAME "com/test/hello_libs/jni/JNIManager"
env->FindClass("java/lang/Integer");
env->FindClass("[I") //int[]
3 GetSuperclass:返回基类,除了java.lang.Object类(因为java.lang.Object是最基类)
(3)Exceptions
1 Throw and ThrowNew:
Throw:丢弃一个现有的异常对象,在当前线程触发一个新的异常
ThrowNew:在当前线程触发一个异常,并自定义输出异常信息
2 FatalError:打印信息并终止当前虚拟机实例
3 ExceptionCheck:检查是否发生了异常,若有异常返回JNI_TRUE,否则返回JNI_FALSE
4 ExceptionClear:清除异常堆栈信息
5 ExceptionDescribe:打印异常的堆栈信息
6 ExceptionOccurred:检查是否发生了异常,若有异常返回该异常的引用,否则返回NULL
(4)Global and Local References
1 NewGlobalRef,DeleteGlobalRef:创建/删除一个全局引用
输入参数可以是全局引用,弱全局引用,局部引用
2 NewWeakGlobalRef,DeleteWeakGlobalRef:弱全局引用
3 NewLocalRef,DeleteLocalRef:局部引用
笔记[2019.08.26]:在 JNI 编程中避免内存泄漏
(5)Object Operations
1 AllocObject:分配一个未初始化的对象
2 NewObject:分配一个对象并运行构造函数
3 GetObjectClass:返回某实例对应的类
4 IsInstanceOf:检查某对象是否为某类的实例
5 IsSameObject:检查两个引用是否指向同一个对象
(6)Instance Field Access
1 GetFieldID:返回一个实例的域ID
2 Get<Type>Field,Set<Type>Field:获取一个实例的域/设置一个实例的域