1. GetStringUTFChars与ReleaseStringUTFChars函数简单说明
通过Jstring返回char *指针,包含结束符
JNI支持Unicode/UTF-8字符编码互转。Unicode以16-bits值编码;UTF-8是一种以字节为单位变长格式的字符编码,并与7-bitsASCII码兼容。UTF-8字串与C字串一样,以NULL('\0')做结束符, 当UTF-8包含非ASCII码字符时,以'\0'做结束符的规则不变--。7-bit ASCII字符的取值范围在1-127之间,这些字符的值域与UTF-8中相同。当最高位被设置时,表示多字节编码。
- Java_com_conowen_test_testActivity_test(JNIEnv *env, jobject obj, jstring str)
- {
- char buf[128];
- const jbyte *cbyte;
- cbyte= (*env)->GetStringUTFChars(env, str, NULL);
- if (cbyte== NULL) {
- return NULL;
- }
- printf("%s", cbyte);
- (*env)->ReleaseStringUTFChars(env, str, cbyte);
- scanf("%127s", buf);
- return (*env)->NewStringUTF(env, buf);
- //或者return (*env)->NewStringUTF(env, "hello world");
- }
//调用GetStringUTFChars,把一个Unicode字串转成UTF-8格式字串 Java_com_conowen_test_testActivity_test(JNIEnv *env, jobject obj, jstring str) { char buf[128]; const jbyte *cbyte; cbyte= (*env)->GetStringUTFChars(env, str, NULL); if (cbyte== NULL) { return NULL; } printf("%s", cbyte); (*env)->ReleaseStringUTFChars(env, str, cbyte); scanf("%127s", buf); return (*env)->NewStringUTF(env, buf); //或者return (*env)->NewStringUTF(env, "hello world"); }
上述函数中,有isCopy参数,当该值为JNI_TRUE,将返回str的一个拷贝;为JNI_FALSE将直接指向str的内容。 注意:当isCopy为JNI_FALSE,不要修改返回值,不然将改变java.lang.String的不可变语义。一般会把isCopy设为NULL,不关心Java VM对返回的指针是否直接指向java.lang.String的内容。
注意:在调用GetStringChars之后,一定要调用ReleaseStringChars做释放,(Unicode -> UTF-8转换的原因)。不管在调用GetStringChars时为isCopy赋值JNI_TRUE还是JNI_FALSE,因不同JavaVM实现的原因,ReleaseStringChars可能释放内存,也可能释放一个内存占用标记。
2. 回调java非静态函数实例
JNIEXPORT jint JNICALL Java_com_kedacom_kdkkdevmgr_commonStatck_CommonStatckSDK_RegAppChannelStatusChangeCBFun
(JNIEnv *pJv, jclass Jc, jobject statusCB, jint nStatusContext)
{
printf("begin to reg status cb\n");
if (g_cbData.m_bInit == false)
{
g_cbData.m_pEnv = pJv;
g_cbData.m_pEnv->GetJavaVM(&g_cbData.m_pJavaVm);
g_cbData.m_bInit = true;
}
g_cbData.m_objStatusCallBack = pJv->NewGlobalRef(statusCB);
g_cbData.m_nStatusContext = nStatusContext;
jclass cls;
cls = g_cbData.m_pEnv->GetObjectClass(statusCB);
if (0 == cls)
{
printf("class 0\n");
}
g_cbData.m_jStatusCBId = g_cbData.m_pEnv->GetMethodID(cls, "apply", "(IIILjava/lang/String;II)V"); //函数名为apply
if (g_cbData.m_jStatusCBId == NULL)
{
printf("jmidProcess = NULL\n");
return -2;
}
else
{
printf("get jmidProcess apply sucess.\n");
}
//int i = 10000; //测试回调
//jstring jstrChannelID = pJv->NewStringUTF("10086");
//jstring jstrChannelID = pJv->NewStringUTF("");//不能根据NULL来构造jstring对象,jstring作为java参数的时候不能填入NULL,"",只能填入jstring对象
//g_cbData.m_pEnv->CallVoidMethod(g_cbData.m_objStatusCallBack, g_cbData.m_jStatusCBId, i, 111, 1, jstrChannelID, 7, 0);
回调时jstring参数不能填NULL,不能根据NULL来构造jstring对象,可以使用""
回调时jbyteArray参数可以填为NULL
3.JNI内存泄露释放
3.1.jstring转换为C风格字符串char* test = (char*)(*env)->GetStringUTFChars(env,jstring,NULL);使用完毕后,应调用:(*env)->ReleaseStringUTFChars(env,jstring, test);释放资源。
4. 在C++中多线程回调java函数
JNIEnv指针只在它所在的线程中有效,不能跨线程传递和使用。不同线程调用一个本地方法时,传入的JNIEnv指针是不同的。
局部引用只在创建它们的线程中有效,同样不能跨线程传递。但可以把局部引用转化成全局引用来供多线程使用。
例:
JNIEnv *pJniEnv;
g_cbData.m_pJavaVm->GetEnv((void **)&pJniEnv, 0x00010006); //JavaVM* m_pJavaVm; //在其他有JNIEnv指针的地方调用 pEnv->GetJavaVM(&g_cbData.m_pJavaVm);初始化JavaVM
g_cbData.m_pJavaVm->AttachCurrentThread((void**)&pJniEnv, NULL); //AttachCurrentThread并不是加锁的功能
jstring jstrDeviceID = pJniEnv->NewStringUTF(pszDeviceID);
jbyte *cXml = (jbyte*)pXmlData;
jbyteArray chXmlData = pJniEnv->NewByteArray(iXmlDataLen);
pJniEnv->SetByteArrayRegion(chXmlData, 0, iXmlDataLen, cXml);
pJniEnv->CallVoidMethod(g_cbData.m_objDataCallBack,
g_cbData.m_jDatasCBId,
lAppChannelID,
jstrDeviceID,
iMsgType,
iSeqID,
chXmlData,
iXmlDataLen,
NULL,
0,
iConnIndex);
pJniEnv->DeleteLocalRef(jstrDeviceID);
pJniEnv->DeleteLocalRef(chXmlData);
g_cbData.m_pJavaVm->DetachCurrentThread();
在c++中new的对象,如果不返回java,必须用release掉,否则内存泄露。包括NewStringUTF,NewObject。如果返回java不必release,java会自己回收。
-
jstring jstr = env->NewStringUTF((*p).sess_id);
-
...
-
env->DeleteLocalRef( jstr);
-
jobject jobj = env->NewObject(clazz,midInit);
-
return jobj;
内存泄露可以先从windows资源管理器中,看到随程序运行,内存不断增长的趋势,具体可以用hp jmeter检测。在运行程序时,加jvm参数 -Xrunhprof:heap=all,cutoff=0 ,生成java.hprof.txt,用jmeter打开,Metric -> Residual Objects (Count),可以看到未回收的对象,选中要查看的对象,点Mark记录下要查看的对象,Window -> New Window 打开新窗口,用Metric -> Reference Graph Tree,然后点Find Immediately可以看到对象被哪里引用。
总体原则:释放所有对object的引用
1.FindClass
-
jclass ref= (env)->FindClass("java/lang/String");
-
env->DeleteLocalRef(ref);
2.NewString / NewStringUTF / NewObject / NewByteArray
例如,
-
jstring (*NewString)(JNIEnv*, const jchar*, jsize);
-
const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
-
void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
-
jstring (*NewStringUTF)(JNIEnv*, const char*);
-
const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
-
void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
env->DeleteLocalRef(ref);
3.GetObjectField/GetObjectClass/GetObjectArrayElement
-
jclass ref = env->GetObjectClass(robj);
-
env->DeleteLocalRef(ref);
4.GetByteArrayElements和GetStringUTFChars
-
jbyte* array= (*env)->GetByteArrayElements(env,jarray,&isCopy);
-
(*env)->ReleaseByteArrayElements(env,jarray,array,0);
-
const char* input =(*env)->GetStringUTFChars(env,jinput, &isCopy);
-
(*env)->ReleaseStringUTFChars(env,jinput,input);
5.NewGlobalRef/DeleteGlobalRef
-
jobject (*NewGlobalRef)(JNIEnv*, jobject);
-
void (*DeleteGlobalRef)(JNIEnv*, jobject);
例如,
-
jobject ref= env->NewGlobalRef(customObj);
-
env->DeleteGlobalRef(customObj);