JNI自用手册1 - 实用篇
文章目录
JNI使用手册,其一,实用篇,先用起来。
来战!
- 简而言之,先抛几个例子上手,以后就算忘了,大概只要参考这几个例子就行了。
- JNI手册系列,主要在Android平台上运行。
Java层和Native层的传递调用
传递基本类型
- 基本传递方式,Java层声明native方法,Native层实现;
public class NativeTransferAdapter { public native int getIntData(int param); }
extern "C" JNIEXPORT jint JNICALL Java_com_sslyxhz_ndkcourse_NativeTransferAdapter_getIntData(JNIEnv *env, jobject instance, jint param) { LOGI("getIntData, param:%d", param); jint result = param + 1; return result; }
- 基本传递方式,Java层声明native方法,Native层实现;
传递数组类型
- 需要注意的是,在Native层创建的对象要记得删除;
public class NativeTransferAdapter { public native int[][] getIntByteArraysData(int[][] param); }
extern "C" JNIEXPORT jobjectArray JNICALL Java_com_sslyxhz_ndkcourse_NativeTransferAdapter_getIntByteArraysData(JNIEnv *env, jobject instance, jobjectArray param) { jint rows = env->GetArrayLength(param); for(int i=0;i<rows; ++i) { jintArray rowArray = (jintArray) env->GetObjectArrayElement(param, i); int cols = env->GetArrayLength(rowArray); for(int j=0; j<cols; ++j){ jint value = env->GetIntArrayElements(rowArray, 0)[j]; LOGI("getIntByteArraysData, param : <%d, %d> = %d", i, j, value); } } int size = 3; jclass intArrayClass = env->FindClass("[I"); jobjectArray result = env->NewObjectArray(size,intArrayClass, NULL); for(int i=0;i<size;i++){ jintArray rowArray = env->NewIntArray(size); jint tempArray[size]; for(int j=0;j<size;j++){ tempArray[j]=i+j; } env->SetIntArrayRegion(rowArray,0,size,tempArray); env->SetObjectArrayElement(result,i,rowArray); env->DeleteLocalRef(rowArray); } env->DeleteLocalRef(intArrayClass); return result; }
传递Set类型
- 此处已经涉及到如何从Java层中定位到类,并调用类中的方法;
public class NativeTransferAdapter { public native Set<String> getSetData(Set<String> param); }
extern "C" JNIEXPORT jobject JNICALL Java_com_sslyxhz_ndkcourse_NativeTransferAdapter_getSetData(JNIEnv *env, jobject instance, jobject param) { jclass hashSetClass = env->FindClass("java/util/HashSet"); jclass iteratorClass = env->FindClass("java/util/Iterator"); if(hashSetClass == NULL || iteratorClass == NULL){ return NULL; } jmethodID init_set_mid = env->GetMethodID(hashSetClass, "<init>", "()V"); jmethodID add_set_mid = env->GetMethodID(hashSetClass, "add", "(Ljava/lang/Object;)Z"); jmethodID iterator_set_mid = env->GetMethodID(hashSetClass, "iterator", "()Ljava/util/Iterator;"); jmethodID hasNext_iter_mid = env->GetMethodID(iteratorClass, "hasNext", "()Z"); jmethodID next_iter_mid = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); jobject iter_obj = env->CallObjectMethod(param, iterator_set_mid); while(env->CallBooleanMethod(iter_obj, hasNext_iter_mid)) { jstring jvalue = (jstring) env->CallObjectMethod(iter_obj, next_iter_mid); const char* cvalue = env->GetStringUTFChars(jvalue, 0); LOGI("getSetData, param item:%s", cvalue); env->DeleteLocalRef(jvalue); } jobject result = env->NewObject(hashSetClass, init_set_mid); int size = 3; for(int i=0;i<size;++i){ jstring testName = env->NewStringUTF("TestNameFromC++"); env->CallBooleanMethod(result, add_set_mid, testName); env->DeleteLocalRef(testName); } env->DeleteLocalRef(hashSetClass); env->DeleteLocalRef(iteratorClass); return result; }
传递Map类型
- 照本宣科,与传递Set非常相似
public class NativeTransferAdapter { public native Map<String, String> getMapData(Map<String, String> param); }
extern "C" JNIEXPORT jobject JNICALL Java_com_sslyxhz_ndkcourse_NativeTransferAdapter_getMapData(JNIEnv *env, jobject instance, jobject param) { jclass hashMapClass = env->FindClass("java/util/HashMap"); jclass setClass = env->FindClass("java/util/Set"); jclass iteratorClass = env->FindClass("java/util/Iterator"); if(hashMapClass == NULL || iteratorClass == NULL){ return NULL; } jmethodID init_map_mid = env->GetMethodID(hashMapClass, "<init>", "()V"); jmethodID put_map_mid = env->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); jmethodID get_map_mid = env->GetMethodID(hashMapClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); jmethodID keySet_map_mid = env->GetMethodID(hashMapClass, "keySet", "()Ljava/util/Set;"); jmethodID iterator_set_mid = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); jmethodID hasNext_iter_mid = env->GetMethodID(iteratorClass, "hasNext", "()Z"); jmethodID next_iter_mid = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); jobject jKeySet = env->CallObjectMethod(param, keySet_map_mid); jobject jIterarot = env->CallObjectMethod(jKeySet, iterator_set_mid); while(env->CallBooleanMethod(jIterarot, hasNext_iter_mid)) { jstring jHashMapKey = (jstring) env->CallObjectMethod(jIterarot, next_iter_mid); jstring jHashMapValue = (jstring) env->CallObjectMethod(param, get_map_mid, jHashMapKey); const char *strKey = env->GetStringUTFChars(jHashMapKey, JNI_FALSE); const char *strValue = env->GetStringUTFChars(jHashMapValue, JNI_FALSE); LOGI("getMapData, param <%s, %s>", strKey, strValue); env->DeleteLocalRef(jHashMapKey); env->DeleteLocalRef(jHashMapValue); } env->DeleteLocalRef(jKeySet); env->DeleteLocalRef(jIterarot); env->DeleteLocalRef(setClass); env->DeleteLocalRef(iteratorClass); jobject result = env->NewObject(hashMapClass, init_map_mid); for(int i=0;i<3;++i){ jstring testKey = env->NewStringUTF("TestKey"); jstring testValue = env->NewStringUTF("TestValue"); env->CallObjectMethod(result, put_map_mid, testKey, testValue); env->DeleteLocalRef(testKey); env->DeleteLocalRef(testValue); } env->DeleteLocalRef(hashMapClass); return result; }
指针传递
- 通常将指针转成jlong进行传递
- 暂不考虑128位CPU
内存块传递
- GetPrimitiveArrayCritical,访问Java底层数据
- ByteBuffer,但操作接口较少;
动态注册
动态注册优势;
- 缩短方法名;
- 提高建立映射关系时的效率;
示例
public class NativeRegisterAdapter {
public native void testRegisterMethod();
}
jclass g_registerAdapter;
static JNINativeMethod gNatvieMethods[] = {
{
"testRegisterMethod",
"()V",
(void *) native_testRegisterMethod
},
};
extern "C"
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
LOGE("**** OnLoad GetEnv failed!");
return result;
}
jclass jAdapterClz = env->FindClass("com/sslyxhz/ndkcourse/NativeRegisterAdapter");
g_registerAdapter = (jclass) env->NewGlobalRef((jobject)jAdapterClz);
env->RegisterNatives(g_registerAdapter, gNatvieMethods, sizeof(gNatvieMethods)/ sizeof(JNINativeMethod));
env->DeleteLocalRef(jAdapterClz);
return JNI_VERSION_1_6;
}
extern "C"
JNIEXPORT jint JNICALL
JNI_UnLoad(JavaVM *vm, void *reserved)
{
// 实际上在Android平台,JNI_UnLoad不会被调用!!
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
{
LOGE("**** UnLoad GetEnv failed!");
return result;
}
env->UnregisterNatives(g_registerAdapter);
env->DeleteGlobalRef(g_registerAdapter);
return JNI_VERSION_1_6;
}
void native_testRegisterMethod(JNIEnv *env, jobject instance)
{
// do something
}
异常处理
JNI不对空指针、非法参数类型等错误进行检测:
- 检查所有可能的错误会降低性能;
- 没有足够的RunTime信息支持;
当发生异常时,JNI允许Native方法处理抛出的异常,一般检测形式:
- 发生异常时直接返回,检查JNI函数的返回值;
- 调用ExceptionOccurred(),检测是否发生异常;
- 异常发生后,需要清楚异常后才能继续调用其他JNI接口;
- 未被处理的异常由JavaVM处理;
C++处理Java异常
jint floor = env->CallIntMethod(obj, test_mid); if (env->ExceptionCheck()) { jclass newExcCls; env->ExceptionDescribe(); // 输出异常信息 env->ExceptionClear(); // 清除异常信息 newExcCls = env->FindClass("java/lang/IllegalArgumentException"); if (newExcCls == NULL) { return; } env->ThrowNew(newExcCls, "thrown from C code"); }
在可能导致异常的JNI调用后保持检测,方便定位问题
jclass objectClass = env->GetObjectClass(obj); jfieldID fieldID = env->GetFieldID(objectClass, "charField", "C"); if(env->ExceptionOccurred()) { return; // 检测到异常返回Java代码,在常规的Java异常流程处理 } jchar result = env->GetCharField(obj, fieldID);
格式转换
- Native层部分常用的类型格式转换
// jbyteArray to byte array
jbyte * arrayBody = env->GetByteArrayElements(data,0);
jsize theArrayLengthJ = env->GetArrayLength(data);
BYTE * starter = (BYTE *)arrayBody;
// jbyteArray to BYTE[]
jbyte * olddata = (jbyte*)env->GetByteArrayElements(strIn, 0);
jsize oldsize = env->GetArrayLength(strIn);
BYTE* bytearr = (BYTE*)olddata;
int len = (int)oldsize;
// BYTE[] to jbyteArray
jbyte *by = (jbyte*)pData;
jbyteArray jarray = env->NewByteArray(nOutSize);
env->SetByteArrayRegin(jarray, 0, nOutSize, by);
// jbyteArray to char*
char* data = (char*)env->GetByteArrayElements(strIn, 0);
// char* to jstring
jstring WindowsTojstring(JNIEnv* env, char* str_tmp)
{
jstring rtn=0;
int slen = (int)strlen(str_tmp);
unsigned short* buffer=0;
if(slen == 0)
{
rtn = env->NewStringUTF(str_tmp);
}
else
{
int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str_tmp, slen, NULL, 0);
buffer = (unsigned short*)malloc(length*2+1);
if(MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str_tmp, slen, (LPWSTR)buffer, length) > 0)
{
rtn = env->NewString((jchar*)buffer, length);
}
}
if(buffer)
{
free(buffer);
}
return rtn;
}
// char* to jstring
JNIEXPORT jstring JNICALL Java_com_explorer_jni_SambaTreeNative_getDetailsBy
(JNIEnv *env, jobject jobj, jstring pc_server, jstring server_user, jstring server_passwd)
{
const char *pc = env->GetStringUTFChars(pc_server, NULL);
const char *user = env->GetStringUTFChars(server_user, NULL);
const char *passwd = env->GetStringUTFChars(server_passwd, NULL);
const char *details = smbtree::getPara(pc, user, passwd);
jstring jDetails = env->NewStringUTF(details);
return jDetails;
}