1 Studying《jni spec》

目录

1 github仓

1.1 由VM直接导出的调用接口函数

1.2 JavaVM接口

1.3 Native库中定义的函数

1.4 JNIEnv接口

1.4.1 Version Information

1.4.2 Class and Interface Operations

1.4.3 Exceptions

1.4.4 Global and Local References

1.4.5 Object Operations

1.4.6 Instance Field Access

1.4.7 Static Field Access

1.4.8 Instance Method Calls

1.4.9 Static Method Calls

1.4.10 String Operations

1.4.11 Array Operations

1.4.12 Native Method Registration

1.4.13 Monitor Operations

1.4.14 JavaVM Interface

1.5 参考资料

3 Basic Types, Strings, and Arrays

3.1 A Simple Native Method

3.2 Accessing Strings

3.2.1 Converting to Native Strings

3.2.4 Other JNI String Functions

3.3 Accessing Arrays

3.3.1 Accessing Arrays in C

3.3.2 Accessing Arrays of Primitive Types

3.3.5 Accessing Arrays of Objects

4 Fields and Methods

4.1 Accessing Fields 域

4.1.1 Procedure for Accessing an Instance Field 实例域

4.1.3 Accessing Static Fields 类域

4.2 Calling Methods

4.2.2 Forming the Method Descriptor

4.2.3 Calling Static Methods

4.3 Invoking Constructors

5 Local and Global References

5.1 Local and Global References

5.1.1 Local References

5.1.2 Global References

5.1.3 Weak Global References

5.1.4 Comparing References

8 Additional JNI Features

8.1 JNI and Threads

8.1.1 Constraints 约束

8.1.2 Monitor Entry and Exit

8.1.3 Monitor Wait and Notify

8.1.4 Obtaining a JNIEnv Pointer in Arbitrary Contexts

8.3 Registering Native Methods

8.6 JNI Programming in C++

10 Traps and Pitfalls

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 JNI Types

12.1 Primitive and Reference Types

12.1.1 Primitive Types 基本类型

12.1.2 Reference Types 引用类型

12.3 String Formats

12.3.2 Class Descriptors 类描述符

12.3.3 Field Descriptors 域描述符

12.3.4 Method Descriptors 方法描述符

13 JNI Functions

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:获取一个实例的域/设置一个实例的域

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值