JNI开发基础(二)

来源:互联网 发布:人工智能的现状论文 编辑:程序博客网 时间:2024/05/29 04:35

上一篇文章讲到了C/C++端调用Android端的属性,下面接着上篇文章。

1、C/C++访问java方法:
在Android端写一个产生随机方位的随机数的方法

    public int genRandomInt(int max){        Log.e("TAG","getRandomInt被调用了");        return new Random().nextInt(max);    }

函数声明为:
public native void accessMethod();

JNI中函数:

extern "C"JNIEXPORT void JNICALLJava_com_sportmoo_jni01_MainActivity_accessMethod(JNIEnv *env, jobject jobj) {    // jclass    jclass  cls = env->GetObjectClass(jobj);    //jmethodId    jmethodID mid = env->GetMethodID(cls, "getRandomInt", "(I)I");    //调用    jint random = env->CallIntMethod(jobj, mid, 200);}

其中,在上面得到函数id的时候,三个参数,一个是jclass,一个是Android中方法名,一个是数据类型的签名,详情见上一篇文章的最后表格,也可以利用javap函数查找。
这里写图片描述
列出所有的属性已经方法,找到需要的那个签名类型。

访问静态方法与访问方法类似,不同的是:

1、GetStaticMethodID vs GetMethodId2、CallStatic<Type>Method vs Call<Type>Method

2、C/C++访问构造函数

很多初始化工作是在构造函数函数中完成的,如果能拿到构造函数中的数据,基本上也就能拿到类中的所有方法和函数。

以Date()函数生成当前时间戳为例。
这里写图片描述
Date的构造方法就有6个,我们需要的是第一个。

extern "C"JNIEXPORT jobject JNICALLJava_com_sportmoo_jni01_MainActivity_accessConstructor(JNIEnv *env, jobject jobj) {    // jclass    jclass cls = env->FindClass("java/util/Date");    //jmethodId    jmethodID  consructor_id = env->GetMethodID(cls, "<init>", "()V");    //实例化一个Date对象    jobject  date_obj = env->NewObject(cls,consructor_id);    //调用getTime方法    jmethodID  mid = env->GetMethodID(cls,"getTime","()J");    env->CallLongMethod(date_obj,mid);    return date_obj;}

在Android中:

 Date date = accessConstructor(); Log.e("TAG",date.toString());

输出结果为:
这里写图片描述

3、中文字符的处理
这里写图片描述
String中就存在这样一个转换字符格式的构造函数,C/C++中存在中文乱码的情况下就利用这个函数来进行处理。

char *c_str = "马蓉与宋江";//char c_str[] = "马蓉与宋喆";//jstring jstr = env->NewStringUTF(c_str);//执行String(byte bytes[], String charsetName)构造方法需要的条件//1.jmethodID//2.byte数组//3.字符编码jstringjclass str_cls = env->FindClass( "java/lang/String");jmethodID constructor_mid = env->GetMethodID(str_cls, "<init>", "([BLjava/lang/String;)V");//jbyte -> char //jbyteArray -> char[]jbyteArray bytes = env->NewByteArray(strlen(c_str));//byte数组赋值//0->strlen(c_str),从头到尾//对等于,从c_str这个字符数组,复制到bytes这个字符数组env->SetByteArrayRegion(bytes, 0, strlen(c_str), c_str);//字符编码jstringjstring charsetName = env->NewStringUTF("GB2312");//调用构造函数,返回编码之后的jstringreturn env->NewObject(env,str_cls,constructor_mid,bytes,charsetName);

4、传递数组
这里写图片描述

5、返回数组
创建一个指定大小的数组

jintArray jint_arr = (*env)->NewIntArray(env, len);jint *elems = (*env)->GetIntArrayElements(env, jint_arr, NULL); int i = 0;for (; i < len; i++){    elems[i] = i;}//同步(*env)->ReleaseIntArrayElements(env, jint_arr, elems, 0);   return jint_arr;

6、JNI引用变量
引用变量分为局部引用和全局引用。

局部引用能够通过DeleteLocalRef手动释放对象。1、访问一个很大的java对象,使用完之后,还要进行复杂的耗时操作。2、创建了大量的局部引用,占用了太多的内存,而且这些局部引用跟后面的操作没有连续性。env->DeleteLocalRef(obj);

全局引用

共享(可以跨多个线程),手动控制内存使用。创建:函数体外:    jstring global_str;函数体内:    jstring obj = env->NewStringUTF("dfadfadfa");    global_str = env->NewGlobalRef(obj);可以通过不同的函数去获得或者释放:return global_str;//获得env->DeleteGlobal_str;//释放

7、弱全局引用
节省内存,在内存不足时可以释放所引用的对象。
创建NewWeakGlobalRef,销毁DeleteGlobalWeakRef。

8、异常处理
1、保证Java代码可以运行
2、补救措施保证C代码继续运行
JNI自己抛出的异常,在Java层无法被捕捉,只能在C层清空,用户通过ThrowNew抛出的异常,可以在Java层捕捉。

jclass cls = (*env)->GetObjectClass(env, jobj);jfieldID fid = (*env)->GetFieldID(env, cls, "key2", "Ljava/lang/String;");//检测是否发生Java异常jthrowable exception = (*env)->ExceptionOccurred(env);if (exception != NULL){    //让Java代码可以继续运行    //清空异常信息    (*env)->ExceptionClear(env);    //补救措施    fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");}//获取属性的值jstring jstr = (*env)->GetObjectField(env, jobj, fid);char *str = (*env)->GetStringUTFChars(env, jstr, NULL);//对比属性值是否合法if (_stricmp(str, "super jason") != 0){    //认为抛出异常,给Java层处理    jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");    (*env)->ThrowNew(env,newExcCls,"key's value is invalid!");}

9、缓存策略
局部静态变量。

10、初始化全局变量

jfieldID key_fid;jmethodID random_mid;JNIEXPORT void JNICALL Java_com_example_jni_JniTest_initIds(JNIEnv *env, jclass jcls){      key_fid = (*env)->GetFieldID(env, jcls, "key", "Ljava/lang/String;");    random_mid = (*env)->GetMethodID(env, jcls, "genRandomInt", "(I)I");}

Android加载动态库的时候,就进行初始化

    static{         System.loadLibrary("jni_study");        initIds();    }
0 0