一.C++中子线程操作JNIEnv环境指针
先声明一个native方法去启动线程:
csharp
public native void executePthread();
在C++中进行实现:
arduino
extern "C"
JNIEXPORT void JNICALL
Java_com_carey_myndk_MainActivity_executePthread(JNIEnv *env, jobject thiz) {
// 线程
pthread_t thread;
// 创建线程
// 参数1 指向线程标识符的指针
// 参数2 用来设置线程属性
// 参数3 线程运行函数的起始地址
// 参数4 运行函数的参数
pthread_create(&thread, NULL, get_min, NULL);
}
我们创建get_min运行函数:
arduino
void* get_min(void* arg) {
for (int i = 0; i < 5; i ++) {
__android_log_print(ANDROID_LOG_INFO, "carey====get_min", "%d", i);
}
JNIEnv *env = NULL;
// 正确调用 AttachCurrentThread
int result = jvm->AttachCurrentThread(&env, NULL);
if (result!= 0) {
// 处理 AttachCurrentThread 失败的情况
__android_log_print(ANDROID_LOG_ERROR, "carey====", "Failed to attach current thread.");
return NULL;
}
__android_log_print(ANDROID_LOG_INFO, "carey====", "%s", "my name is carey.");
// 分离线程,解除关联JVM虚拟机
jvm->DetachCurrentThread();
return NULL;
}
里面我们有一个循环打印,完了通过jvm的AttachCurrentThread方法获取环境变量JNIEnv指针,这里的jvm是JavaVM,JavaVM可以通过重写JNI_OnLoad方法得到:
javascript
JavaVM* jvm;
// 当我们应用程序加载完毕之后,虚拟机立马调用该方法
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// 通过JavaVM获取环境指针
jvm = vm;
return JNI_VERSION_1_6;
}
通过上面方法获取到JavaVM指针,并返回当前JNI的版本号。
当Java虚拟机卸载包含本地代码(通常是用C或C++编写的动态库)的库时,会调用JNI_OnUnload
方法:
arduino
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
最后Java中调用executePthread方法,查看日志打印:
二. C++中的常量
新建一个java类,并声明一个native方法:
csharp
public class NDKCppInterface {
// C++中常量
public native void executeCppConst();
}
在Terminal中执行命令,生成相应的头部.h文件:
如果javah命令找不到,可以在环境变量中添加jdk/bin目录。这时在当前java类同级目录下生成了.h文件:
arduino
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_carey_myndk_NDKCppInterface */
#ifndef _Included_com_carey_myndk_NDKCppInterface
#define _Included_com_carey_myndk_NDKCppInterface
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_carey_myndk_NDKCppInterface
* Method: executeCppConst
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_carey_myndk_NDKCppInterface_executeCppConst
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
我们把这个.h文件放到cpp/目录下:
同时,我们在cpp/目录下创建对应的.cpp文件com_carey_myndk_NDKCppInterface.cpp:
arduino
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include "com_carey_myndk_NDKCppInterface.h"
#include <android/log.h>
/*
* Class: com_carey_myndk_NDKCppInterface
* Method: executeCppConst
* Signature: ()V
*/
extern "C"
JNIEXPORT void JNICALL Java_com_carey_myndk_NDKCppInterface_executeCppConst
(JNIEnv *, jobject) {
const int a = 100; // 常量
int *p = (int*)&a; // 指针指向a的地址
*p = 200; // 改为200
__android_log_print(ANDROID_LOG_INFO, "carey====", "C语言: %d", a);
}
同时更改CMakeLists.txt文件中添加新增加的cpp文件:
在Java中调用该方法,查看日志:
ini
NDKCppInterface ndkCppInterface = new NDKCppInterface();
ndkCppInterface.executeCppConst();
这里我们看到输出的a值还是100,就是说明这个常量值是不能修改的。
三.指针的引用
我们新增一个方法:
csharp
public class NDKCppInterface {
// C++中常量
public native void executeCppConst();
// 指针的引用
public native void executeCppPointer();
}
在.h文件和.cpp文件中分别添加该方法是声明和实现:
arduino
JNIEXPORT void JNICALL
Java_com_carey_myndk_NDKCppInterface_executeCppPointer(JNIEnv *env, jobject thiz);
ini
// 定义一个结构体
struct User {
char *name;
int age;
};
// 更新user
void update_user(User **u) { // 二级指针,`u` 是一个指向 `User*` 类型指针的指针。要修改 `u` 所指向的指针(即 `*u`),需要通过解引用 `u` 来实现。
User *user = (User*) malloc(sizeof(User));
user->name = "Carey";
user->age = 100;
*u = user;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_carey_myndk_NDKCppInterface_executeCppPointer(JNIEnv *env, jobject thiz) {
User *user = nullptr;
update_user(&user);
__android_log_print(ANDROID_LOG_INFO, "carey====", "名字: %s, 年龄:%d", user->name, user->age);
delete user;
user = nullptr;
}
我们先用二级指针的方式去更改结构体对象User的数据,查看打印:
下面通过指针的引用去更改User信息,我们改下update_user方法代码:
arduino
// 指针引用
void update_user(User* &u) { // `u` 是一个对 `User*` 类型指针的引用。可以直接修改 `u` 所指向的地址。
// 使用new分配内存并初始化
u = new User(); // 调用默认构造函数
strcpy(u->name, "Carey2"); // 使用strcpy复制字符串
u->age = 28;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_carey_myndk_NDKCppInterface_executeCppPointer(JNIEnv *env, jobject thiz) {
User *user = nullptr;
update_user(user); // 传入指针
__android_log_print(ANDROID_LOG_INFO, "carey====", "名字: %s, 年龄:%d", user->name, user->age);
delete user; // 释放内存
user = nullptr;
}
这样我们查看打印:
四.总结
今天学习了C++中创建线程、获取JavaVM指针,通过JavaVM指针的AttachCurrentThread方法得到JNIEnv指针;还学习了C++中指针引用和常量。喜欢的可以点赞和收藏,感谢!