env->ReleaseIntArrayElements(arg6, j_ints, 0);
}
简单代码就不说了,只说一下陌生的;
String类型解释:
- java -> String
- JNI -> jstring
- C++ -> char*
想要吧 java 的 String 转换为 C++认识的 char*就需要用到:
char * j_string = const_cast<char *>(env->GetStringUTFChars(arg4, NULL));
GetObjectArrayElement解释:
在JNI 中只有基本类型和引用类型,String 是引用类型,所以遍历 String[]的时候,采用GetObjectArrayElement()来接收
- 参数一:jobjectArray:所有的元素
- 参数二:jsize:当前的位置
ReleaseIntArrayElements解释:
ReleaseIntArrayElements()方法是 C++通知 JNI 改变 java 的值,在代码中 java层 传递过来一个数组,然后修改完成后再返回给 java 层
- 参数一:jintArray:java 传递过来的数组
- 参数二:修改后的数组
- 参数三:修改的模式,填 0 就好
ReleaseStringChars解释:
ReleaseStringChars是用来释放 JNI 内存的,虽然说不释放也可以,因为在方法结束的时候会弹栈,一旦弹栈就会自动释放内存,但是如果说一个方法中有 5000 行代码,在一直不释放,等待到 JNI 弹栈自动释放,那么就会引起 JNI 在调用该方法的时候占用空间略大(养成好习惯)
运行结果:
native 运行结果:
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数一为42
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数二为24.420000
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数三为1
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数四为李元霸
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数五为成龙
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数五为李小龙
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数五为松井宝
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数五为迪丽热巴
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 参数五为ohh
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 第六个参数为:101
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 第六个参数为:104
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 第六个参数为:102
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 第六个参数为:162
2021-04-28 17:36:22.217 6501-6501/com.example.jni E/szj: 第六个参数为:161
java 运行结果:
2021-04-28 17:36:22.217 6501-6501/com.example.jni I/szjjava 层打印: 101
2021-04-28 17:36:22.217 6501-6501/com.example.jni I/szjjava 层打印: 104
2021-04-28 17:36:22.217 6501-6501/com.example.jni I/szjjava 层打印: 102
2021-04-28 17:36:22.217 6501-6501/com.example.jni I/szjjava 层打印: 162
2021-04-28 17:36:22.217 6501-6501/com.example.jni I/szjjava 层打印: 161
引用类型 静态/动态 方法调用
java 代码:
public native void nativeMethod(Persion persion);
辅助图:
右侧的红框是需要调用的方法;
- void setName()
- void setAge()
- static void show(String name)
native 代码:
extern “C”
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeMethod(JNIEnv *env, jobject thiz,
jobject persion) {
/**
- 获取 Persion 的 class
- 源码: jclass FindClass(const char* name)
*/
jclass j_class = env->FindClass(“com/example/jni/bean/Persion”);
/**
- 获取方法 ID:
- jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
*/
jmethodID j_method_id = env->GetMethodID(j_class, “getName”, “()Ljava/lang/String;”);
//获取 Persion getName方法返回值
jstring j_strName = static_cast(env->CallObjectMethod(persion, j_method_id));
//修改为 C++认识的 char*类型
char *j_char_name = const_cast<char *>(env->GetStringUTFChars(j_strName, NULL));
LOGE(“通过 getName获取值为:%s”, j_char_name)
//获取 setName的 ID
jmethodID j_setName_id = env->GetMethodID(j_class, “setName”, “(Ljava/lang/String;)V”);
//获取静态的 show 方法
jmethodID j_show_id = env->GetStaticMethodID(j_class, “show”, “(Ljava/lang/String;)V”);
env->CallVoidMethod(persion, j_setName_id, env->NewStringUTF(“糖果超甜”));
env->CallStaticVoidMethod(j_class, j_show_id, env->NewStringUTF(“糖果超甜”));
}
这个方法中没有陌生方法,大家应该都认识,看不懂记得评论区留言哦~
运行结果为:
native运行结果:
2021-04-28 17:44:29.775 6501-6501/com.example.jni E/szj: 通过 getName获取值为:洪静宝
java 运行结果:
2021-04-28 17:44:29.775 6501-6501/com.example.jni I/szjjava 层 setName方法:: 糖果超甜
2021-04-28 17:44:29.775 6501-6501/com.example.jni I/szjjava 层show方法:: 糖果超甜
引用类型进阶
java 定义native方法:
public native void nativeMethod2(Dog dog);
辅助图:
native 层代码:
extern “C”
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeMethod2(JNIEnv *env, jobject thiz,
jobject dog_jobj) {
//获取 Dog 类的 Class
jclass j_dog_class = env->GetObjectClass(dog_jobj);
//获取 Dog 类中的 DogShow 方法 ID
jmethodID jmethodId = env->GetMethodID(j_dog_class, “DogShow”,
“(Lcom/example/jni/bean/Persion;)V”);
//获取 Persion 的 jclass
jclass j_persion_class = env->FindClass(“com/example/jni/bean/Persion”);
//获取 Persion 的 jobj //AllocObject只实例化对象,不会调用构造函数
jobject j_persion_job = env->AllocObject(j_persion_class);
//给 Persion 赋值
jmethodID j_setName_id = env->GetMethodID(j_persion_class, “setName”, “(Ljava/lang/String;)V”);
jmethodID j_setAge_id = env->GetMethodID(j_persion_class, “setAge”, “(I)V”);
//调用 Persion 的 setName 和 setAge 给 name 和 age 赋值
env->CallVoidMethod(j_persion_job, j_setName_id, env->NewStringUTF(“蔡徐坤”));
env->CallVoidMethod(j_persion_job, j_setAge_id, 52);
//调用 Dog 的 DogShow(Persion)方法
env->CallVoidMethod(dog_jobj, jmethodId, j_persion_job);
//释放引用类型 class 和 job 都建议释放
env->DeleteLocalRef(j_dog_class);
env->DeleteLocalRef(j_persion_class);
env->DeleteLocalRef(j_persion_job);
}
思路:
- 创建 Persion 对象
- 调用 Persion 的 setName() / setAge() 方法给 name/age 赋值
- 创建 Dog 对象
- 调用 Dog 对象中的 DogShow(Persion) 方法,把 Persion 传递进去即可
AllocObject解释:
AllocObject 只实例化对象,不会调用构造函数,在新创建对象(不是传递过来的)的时候,必须用这个来实例化对象,否则调用 实例化的对象不起作用
构造函数调用
java 定义 native 方法:
public native void nativeStructure(Dog dog);
native 代码:
extern “C”
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeStructure(JNIEnv *env, jobject thiz,
jobject dog) {
//获取 Dog 的 jclass
jclass j_dog_class = env->GetObjectClass(dog);
//获取构造方法
jmethodID id1 = env->GetMethodID(j_dog_class, “”, “()V”);
jmethodID id2 = env->GetMethodID(j_dog_class, “”, “(II)V”);
jmethodID id3 = env->GetMethodID(j_dog_class, “”, “(III)V”);
//调用构造方法
env->CallVoidMethod(dog, id1);
env->CallVoidMethod(dog, id2, 100, 200);
env->CallVoidMethod(dog, id3, 300, 400, 500);
}
这里非常好理解:
构造方法用表示即可,其余的都没变
全局引用
java 定义 native 代码:
//全局引用测试
public native void nativeAllQuote();
native代码(局部引用):
发现错误找到错误
jclass dogClass;//注意这里定义到外边也是局部引用(和 java 不一样)
extern “C”
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeAllQuote(JNIEnv *env, jobject thiz) {
if (NULL == dogClass) {
//这个是局部引用,(和 java 不同)
dogClass = env->FindClass(“com/example/jni/bean/Dog”);
}
//获取 Dog 的 jclass
jclass j_dog_class = env->GetObjectClass(dogClass);
//获取构造方法
jmethodID id1 = env->GetMethodID(dogClass, “”, “()V”);
jmethodID id2 = env->GetMethodID(dogClass, “”, “(II)V”);
jmethodID id3 = env->GetMethodID(dogClass, “”, “(III)V”);
//调用构造方法
env->CallVoidMethod(j_dog_class, id1);
env->CallVoidMethod(j_dog_class, id2, 100, 200);
env->CallVoidMethod(j_dog_class, id3, 300, 400, 500);
// dogClass = NULL;//专门注释掉的!
}//因为当方法结束的时候,dogClass 会弹栈,但是dogClass还是不等于 NULL,此时只是内存地址消失,但是指针还指向内一个地址,是悬空指针状态
运行效果为:
可以看出,第二次点击按钮的时候会崩溃;
因为当方法结束的时候,dogClass 会弹栈,但是dogClass还是不等于 NULL,此时只是内存地址消失,但是指针还指向内一个地址,是悬空指针状态
解决思路:
将dogClass 转变成全局引用;
const char *a = “com/example/jni/bean/Dog”;
jclass temp = env->FindClass(a);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
给大家分享一份移动架构大纲,包含了移动架构师需要掌握的所有的技术体系,大家可以对比一下自己不足或者欠缺的地方有方向的去学习提升;
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
获取!!(备注:Android)**
[外链图片转存中…(img-exOxjAfm-1713717901638)]
最后
给大家分享一份移动架构大纲,包含了移动架构师需要掌握的所有的技术体系,大家可以对比一下自己不足或者欠缺的地方有方向的去学习提升;
[外链图片转存中…(img-6zSBr2nJ-1713717901639)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!