Android JNI 动态注册
动态注册与静态注册
- 静态注册:平时默认的就是静态注册
例如:
静态注册:
- 优点:使用简单,方便在程序运行时才会初始化调用
- 缺点:类名长,与类名捆绑,一旦更改包名会比较麻烦
动态注册
- 优点:最开始运行的时候就会初始化,代码简洁
- 缺点:使用比较麻烦.
静态注册就不说了,本篇主要介绍动态注册
动态注册
动态注册像是调用类的构造器一样,每当最开始运行的时候就会初始化所有方法
在这个方法中,完成 Activity 中的所有初始化!
jint JNI_OnLoad(JavaVM *javaVm, void *) {
//返回 JNI 版本号
return JNI_VERSION_1_6;
}
JNI_OnLoad源码:
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
第一步:获取 JVM 中的 Env,如果返回结果!=0 说明获取Env失败
注:JVM 是唯一的
//获取 jVM 中的 Env
int result = javaVm->GetEnv(reinterpret_cast<void **>(&jniEnv), JNI_VERSION_1_6);
//result != 0 则失败
if (result != JNI_OK) {
//失败
return -1;
}
这段代码比较简单就不说了!
第二步:通过 Env 动态注册需要初始化的方法
//获取 jclass
jclass thread_class = jniEnv->FindClass(path);
/**
* 源码:
* jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods)
* 参数一:class
* 参数二:结构体数组
* 参数三:结构体大小
*/
jniEnv->RegisterNatives(thread_class, jniNativeMethod,
sizeof(jniNativeMethod) / sizeof(JNINativeMethod));
- 参数一:jclass
- 参数二:JNINativeMethod的结构体
typedef struct {
const char* name; //方法名
const char* signature; //签名
void* fnPtr; //回调函数
} JNINativeMethod;
- 参数三:JNINativeMethod数组长度
参数二对应代码:
void javaDynamicRegist(JNIEnv *jniEnv, jobject jobj) {
LOGE("javaDynamicRegist")
}
int javaDynamicRegist2(JNIEnv *jniEnv, jobject jobj, jstring name) {
const char *name2 = jniEnv->GetStringUTFChars(name, nullptr);
LOGE("javaDynamicRegist2%s\n", name2);
return 200;
}
/**
typedef struct {
const char* name; //调用的名字
const char* signature; //签名
void* fnPtr; //具体实现
} JNINativeMethod;
*/
static const JNINativeMethod jniNativeMethod[] = {
{"nativeDynamicRegist", "()V", (void *) javaDynamicRegist},
{"nativeDynamicRegist2", "(Ljava/lang/String;)I", (void *) javaDynamicRegist2},
};
辅助图:
使用:
这里 native 方法报错不用管,这是正常状态!
运行结果为:
2021-04-30 16:57:03.098 330-330/com.example.jni E/native 层:: javaDynamicRegist
2021-04-30 16:57:03.098 330-330/com.example.jni E/native 层:: javaDynamicRegist2李元霸
JNI线程
在 C/C++中,线程使用 pthread
| pthread函数 | 说明 |
|---|---|
| pthread_create() | 创建线程开始运行相关线程函数,运行结束则线程退出 |
| pthread_eixt() | 因为exit()是用来结束进程的,所以则需要使用特定结束线程的函数 |
| pthread_join() | 挂起当前线程,用于阻塞式地等待线程结束,如果线程已结束则立即返回,0=成功 |
| pthread_cancel() | 发送终止信号给thread线程,成功返回0,但是成功并不意味着thread会终止 |
辅助代码图:
记得导包哦:
#include <pthread.h>
pthread_create()解释:
- 参数一:线程 ID
- 参数二:线程属性(一般都是 0 或者 nullptr)
- 参数三:函数回调
- 参数四:传递的值
为什么要把jobject设置为全局变量?
答:jobject不能跨越线程,不能跨越函数 [解决思路:吧 jobject 提升为全局引用]
函数回调:
void *my_thread_action(void *pVoid) {
MyThread *thread = static_cast<MyThread *>(pVoid);
/**
* JVM 只有一个 jNIEnv
* jNIEnv解决方式:
*/
JNIEnv *newJniEnv = nullptr;
/**
* jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
* 得到全新的 JNIEnv
*/
jint result = ::jvm->AttachCurrentThread(&newJniEnv, nullptr);
//结果 == 0 表示成功
if (result != JNI_OK) {
return 0; // 附加失败,返回了
}
jclass j_c = newJniEnv->GetObjectClass(thread->jobj);
jmethodID j_id = newJniEnv->GetMethodID(j_c, "isThread", "()V");
//调用 java 层的 isThread 方法
newJniEnv->CallVoidMethod(thread->jobj, j_id);
//解除附加
::jvm->DetachCurrentThread();
//不能直接释放引用
return nullptr;
}
jvm是在JNI_OnLoad初始化的时候获取的
jint JNI_OnLoad(JavaVM *javaVm, void *) {
::jvm = javaVm;
}
::jvm 相当于 java 中的 this.jvm
最终调用java 层的方法:
效果图:
注意:
如果 so 库报错,把其他两个注释掉
同理,如果你想看 JNI 基本使用(native-simple-lib.cpp)的代码,那么吧 JNI 进阶和 QQ语音实战的代码注释掉!
现在只能有一个 cpp 文件存在
其他 JNI 文章:
第三篇:Android JNI QQ 搞怪语音实战 (含完整 Demo)
原创不易,您的点赞就是对我最大的支持~