【综合】HarmonyOS 鸿蒙Next Native侧跨线程调用Java侧回调函数
【综合】HarmonyOS 鸿蒙Next Native侧跨线程调用Java侧回调函数
作者:HW-LC
在Android开发过程中,我们经常会遇到native调用Java的接口,这个网上资料也很多,我这里不再赘述了,我要讲的一种场景是native跨线程调用Java侧注册的回调函数。
我们的场景如下:
Java侧注册一个callback到native,native侧其他线程当触发某些条件之后(比如设备入网,设备离网),回调设备相关信息到Java侧。
具体结构图如下:
对应的代码如下:
(1)Java侧主线程注册Java端callback
private native void initIsyncNative(String userId, String deviceId, int proxyScore, HiDeviceCallback hiDeviceCallback);
(2)Native侧生成global reference
Java_com_huawei_hidevice_core_HiDeviceNative_initIsyncNative(JNIEnv *env, jobject, jstring jUserId,
jstring jDeviceId, jint jProxyScore, jobject cbObj) {
...
jclass jclazz = env->FindClass("com/huawei/hidevice/core/HiDeviceCallback");
if (jclazz == NULL) {
ALOGE("find HiDeviceCallback fail");
return;
}
deviceNotifyCb = env->GetMethodID(jclazz, "deviceNotifyCallback", "(Ljava/lang/String;I)V");
recvDataCb = env->GetMethodID(jclazz, "recvDataCallback", "(ILjava/lang/String;Ljava/lang/String;[BI)I");
serviceNotifyCb = env->GetMethodID(jclazz, "serviceNotifyCallback", "(ILjava/lang/String;Ljava/lang/String;I)V");
if (deviceNotifyCb == NULL || recvDataCb == NULL || serviceNotifyCb == NULL ) {
ALOGE("get method id fail");
return;
}
// 此处需要创建object的global ref,否则isync跨线程回调这个obj会找不到而crash
hiDeviceCbObj = env->NewGlobalRef(cbObj);
if (hiDeviceCbObj == NULL) {
ALOGE("init java callback fail");
}
...
}
这里删除了业务代码,只保留了跟本文相关基础代码,可以看到主要是拿到callback的jmethodID和global reference。
(3)其他线程利用JNIEnv,jmethodID,global reference和参数,回调到Java侧的callback。
由于不同线程的JNIEnv是不一样的,所以要回调到主线程的callback,需要利用JavaVM找到主线程的JNIEnv(保存在g_JNIEnv中),代码如下:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *){
vm->GetEnv((void **) &g_JNIEnv, JNI_VERSION_1_6);
__android_log_write(ANDROID_LOG_DEBUG, "HiDevice", "OnLoad");
g_JavaVM = vm;
return JNI_VERSION_1_6;
}
jmethodID和global reference在步骤2的时候已经拿到了,看下其他线程如何回调到Java侧的callback的,代码如下:
void HiDeviceDeviceNotifyCb(const char *device_id, device_event_e event_type) {
ALOGD("HiDeviceDeviceNotifyCb, deviceId:%s, type:%d", device_id, event_type);
// 由于isync是跨线程调用的,所以需要attach到initIsyncNative线程(主线程)
// 这里就是用JavaVM来attach的,否则拿不到hiDeviceObj
g_JavaVM->AttachCurrentThread(&g_JNIEnv, 0);
jstring deviceId = g_JNIEnv->NewStringUTF(device_id);
g_JNIEnv->CallVoidMethod(hiDeviceCbObj, deviceNotifyCb, deviceId, event_type);
g_JNIEnv->DeleteLocalRef(deviceId);
g_JavaVM->DetachCurrentThread();
}
这里需要重点关注下AttachCurrentThread,通过JavaVM找到对应的JNIEnv,否则在调用CallVoidMethod的时候会crash,回调完毕注意detach即可。
到此为止native跨线程调用java侧注册的回调函数就能正常运行了,本例演示的是回调Java侧HiDeviceCallback的deviceNotifyCallback函数。
更多关于【综合】HarmonyOS 鸿蒙Next Native侧跨线程调用Java侧回调函数的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
想认识这位大神
更多关于【综合】HarmonyOS 鸿蒙Next Native侧跨线程调用Java侧回调函数的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
作者:HW-LC
在Android开发过程中,我们经常会遇到native调用Java的接口,这个网上资料也很多,我这里不再赘述了,我要讲的一种场景是native跨线程调用Java侧注册的回调函数。
我们的场景如下:
Java侧注册一个callback到native,native侧其他线程当触发某些条件之后(比如设备入网,设备离网),回调设备相关信息到Java侧。
具体结构图如下:
对应的代码如下:
(1)Java侧主线程注册Java端callback
```java
private native void initIsyncNative(String userId, String deviceId, int proxyScore, HiDeviceCallback hiDeviceCallback);
(2)Native侧生成global reference
Java_com_huawei_hidevice_core_HiDeviceNative_initIsyncNative(JNIEnv *env, jobject, jstring jUserId,
jstring jDeviceId, jint jProxyScore, jobject cbObj) {
...
jclass jclazz = env->FindClass("com/huawei/hidevice/core/HiDeviceCallback");
if (jclazz == NULL) {
ALOGE("find HiDeviceCallback fail");
return;
}
deviceNotifyCb = env->GetMethodID(jclazz, "deviceNotifyCallback", "(Ljava/lang/String;I)V");
recvDataCb = env->GetMethodID(jclazz, "recvDataCallback", "(ILjava/lang/String;Ljava/lang/String;[BI)I");
serviceNotifyCb = env->GetMethodID(jclazz, "serviceNotifyCallback", "(ILjava/lang/String;Ljava/lang/String;I)V");
if (deviceNotifyCb == NULL || recvDataCb == NULL || serviceNotifyCb == NULL ) {
ALOGE("get method id fail");
return;
}
// 此处需要创建object的global ref,否则isync跨线程回调这个obj会找不到而crash
hiDeviceCbObj = env->NewGlobalRef(cbObj);
if (hiDeviceCbObj == NULL) {
ALOGE("init java callback fail");
}
...
}
这里删除了业务代码,只保留了跟本文相关基础代码,可以看到主要是拿到callback的jmethodID和global reference。
(3)其他线程利用JNIEnv,jmethodID,global reference和参数,回调到Java侧的callback。
由于不同线程的JNIEnv是不一样的,所以要回调到主线程的callback,需要利用JavaVM找到主线程的JNIEnv(保存在g_JNIEnv中),代码如下:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *){
vm->GetEnv((void **) &g_JNIEnv, JNI_VERSION_1_6);
__android_log_write(ANDROID_LOG_DEBUG, "HiDevice", "OnLoad");
g_JavaVM = vm;
return JNI_VERSION_1_6;
}
jmethodID和global reference在步骤2的时候已经拿到了,看下其他线程如何回调到Java侧的callback的,代码如下:
void HiDeviceDeviceNotifyCb(const char *device_id, device_event_e event_type) {
ALOGD("HiDeviceDeviceNotifyCb, deviceId:%s, type:%d", device_id, event_type);
// 由于isync是跨线程调用的,所以需要attach到initIsyncNative线程(主线程)
// 这里就是用JavaVM来attach的,否则拿不到hiDeviceObj
g_JavaVM->AttachCurrentThread(&g_JNIEnv, 0);
jstring deviceId = g_JNIEnv->NewStringUTF(device_id);
g_JNIEnv->CallVoidMethod(hiDeviceCbObj, deviceNotifyCb, deviceId, event_type);
g_JNIEnv->DeleteLocalRef(deviceId);
g_JavaVM->DetachCurrentThread();
}
这里需要重点关注下AttachCurrentThread,通过JavaVM找到对应的JNIEnv,否则在调用CallVoidMethod的时候会crash,回调完毕注意detach即可。
到此为止native跨线程调用java侧注册的回调函数就能正常运行了,本例演示的是回调Java侧HiDeviceCallback的deviceNotifyCallback函数。
沙发,图文并茂,赞一个
图文结合很清晰
作者:HW-LC
在Android开发过程中,我们经常会遇到native调用Java的接口,这个网上资料也很多,我这里不再赘述了,我要讲的一种场景是native跨线程调用Java侧注册的回调函数。
我们的场景如下:
Java侧注册一个callback到native,native侧其他线程当触发某些条件之后(比如设备入网,设备离网),回调设备相关信息到Java侧。
具体结构图如下:
对应的代码如下:
(1)Java侧主线程注册Java端callback
private native void initIsyncNative(String userId, String deviceId, int proxyScore, HiDeviceCallback hiDeviceCallback);
(2)Native侧生成global reference
Java_com_huawei_hidevice_core_HiDeviceNative_initIsyncNative(JNIEnv *env, jobject, jstring jUserId,
jstring jDeviceId, jint jProxyScore, jobject cbObj) {
...
jclass jclazz = env->FindClass("com/huawei/hidevice/core/HiDeviceCallback");
if (jclazz == NULL) {
ALOGE("find HiDeviceCallback fail");
return;
}
deviceNotifyCb = env->GetMethodID(jclazz, "deviceNotifyCallback", "(Ljava/lang/String;I)V");
recvDataCb = env->GetMethodID(jclazz, "recvDataCallback", "(ILjava/lang/String;Ljava/lang/String;[BI)I");
serviceNotifyCb = env->GetMethodID(jclazz, "serviceNotifyCallback", "(ILjava/lang/String;Ljava/lang/String;I)V");
if (deviceNotifyCb == NULL || recvDataCb == NULL || serviceNotifyCb == NULL ) {
ALOGE("get method id fail");
return;
}
// 此处需要创建object的global ref,否则isync跨线程回调这个obj会找不到而crash
hiDeviceCbObj = env->NewGlobalRef(cbObj);
if (hiDeviceCbObj == NULL) {
ALOGE("init java callback fail");
}
...
}
这里删除了业务代码,只保留了跟本文相关基础代码,可以看到主要是拿到callback的jmethodID和global reference。
(3)其他线程利用JNIEnv,jmethodID,global reference和参数,回调到Java侧的callback。
由于不同线程的JNIEnv是不一样的,所以要回调到主线程的callback,需要利用JavaVM找到主线程的JNIEnv(保存在g_JNIEnv中),代码如下:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *){
vm->GetEnv((void **) &g_JNIEnv, JNI_VERSION_1_6);
__android_log_write(ANDROID_LOG_DEBUG, "HiDevice", "OnLoad");
g_JavaVM = vm;
return JNI_VERSION_1_6;
}
jmethodID和global reference在步骤2的时候已经拿到了,看下其他线程如何回调到Java侧的callback的,代码如下:
void HiDeviceDeviceNotifyCb(const char *device_id, device_event_e event_type) {
ALOGD("HiDeviceDeviceNotifyCb, deviceId:%s, type:%d", device_id, event_type);
// 由于isync是跨线程调用的,所以需要attach到initIsyncNative线程(主线程)
// 这里就是用JavaVM来attach的,否则拿不到hiDeviceObj
g_JavaVM->AttachCurrentThread(&g_JNIEnv, 0);
jstring deviceId = g_JNIEnv->NewStringUTF(device_id);
g_JNIEnv->CallVoidMethod(hiDeviceCbObj, deviceNotifyCb, deviceId, event_type);
g_JNIEnv->DeleteLocalRef(deviceId);
g_JavaVM->DetachCurrentThread();
}
这里需要重点关注下AttachCurrentThread,通过JavaVM找到对应的JNIEnv,否则在调用CallVoidMethod的时候会crash,回调完毕注意detach即可。
到此为止native跨线程调用java侧注册的回调函数就能正常运行了,本例演示的是回调Java侧HiDeviceCallback的deviceNotifyCallback函数。
沙发,图文并茂,赞一个
图文结合很清晰
沙发,图文并茂,赞一个
作者:HW-LC
在Android开发过程中,我们经常会遇到native调用Java的接口,这个网上资料也很多,我这里不再赘述了,我要讲的一种场景是native跨线程调用Java侧注册的回调函数。
我们的场景如下:
Java侧注册一个callback到native,native侧其他线程当触发某些条件之后(比如设备入网,设备离网),回调设备相关信息到Java侧。
具体结构图如下:
对应的代码如下:
(1)Java侧主线程注册Java端callback
```java
private native void initIsyncNative(String userId, String deviceId, int proxyScore, HiDeviceCallback hiDeviceCallback);
(2)Native侧生成global reference
Java_com_huawei_hidevice_core_HiDeviceNative_initIsyncNative(JNIEnv *env, jobject, jstring jUserId,
jstring jDeviceId, jint jProxyScore, jobject cbObj) {
...
jclass jclazz = env->FindClass("com/huawei/hidevice/core/HiDeviceCallback");
if (jclazz == NULL) {
ALOGE("find HiDeviceCallback fail");
return;
}
deviceNotifyCb = env->GetMethodID(jclazz, "deviceNotifyCallback", "(Ljava/lang/String;I)V");
recvDataCb = env->GetMethodID(jclazz, "recvDataCallback", "(ILjava/lang/String;Ljava/lang/String;[BI)I");
serviceNotifyCb = env->GetMethodID(jclazz, "serviceNotifyCallback", "(ILjava/lang/String;Ljava/lang/String;I)V");
if (deviceNotifyCb == NULL || recvDataCb == NULL || serviceNotifyCb == NULL ) {
ALOGE("get method id fail");
return;
}
// 此处需要创建object的global ref,否则isync跨线程回调这个obj会找不到而crash
hiDeviceCbObj = env->NewGlobalRef(cbObj);
if (hiDeviceCbObj == NULL) {
ALOGE("init java callback fail");
}
...
}
这里删除了业务代码,只保留了跟本文相关基础代码,可以看到主要是拿到callback的jmethodID和global reference。
(3)其他线程利用JNIEnv,jmethodID,global reference和参数,回调到Java侧的callback。
由于不同线程的JNIEnv是不一样的,所以要回调到主线程的callback,需要利用JavaVM找到主线程的JNIEnv(保存在g_JNIEnv中),代码如下:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *){
vm->GetEnv((void **) &g_JNIEnv, JNI_VERSION_1_6);
__android_log_write(ANDROID_LOG_DEBUG, "HiDevice", "OnLoad");
g_JavaVM = vm;
return JNI_VERSION_1_6;
}
jmethodID和global reference在步骤2的时候已经拿到了,看下其他线程如何回调到Java侧的callback的,代码如下:
void HiDeviceDeviceNotifyCb(const char *device_id, device_event_e event_type) {
ALOGD("HiDeviceDeviceNotifyCb, deviceId:%s, type:%d", device_id, event_type);
// 由于isync是跨线程调用的,所以需要attach到initIsyncNative线程(主线程)
// 这里就是用JavaVM来attach的,否则拿不到hiDeviceObj
g_JavaVM->AttachCurrentThread(&g_JNIEnv, 0);
jstring deviceId = g_JNIEnv->NewStringUTF(device_id);
g_JNIEnv->CallVoidMethod(hiDeviceCbObj, deviceNotifyCb, deviceId, event_type);
g_JNIEnv->DeleteLocalRef(deviceId);
g_JavaVM->DetachCurrentThread();
}
这里需要重点关注下AttachCurrentThread,通过JavaVM找到对应的JNIEnv,否则在调用CallVoidMethod的时候会crash,回调完毕注意detach即可。
到此为止native跨线程调用java侧注册的回调函数就能正常运行了,本例演示的是回调Java侧HiDeviceCallback的deviceNotifyCallback函数。
沙发,图文并茂,赞一个
图文结合很清晰
在HarmonyOS(鸿蒙)系统中,实现Native侧跨线程调用Java侧回调函数,通常涉及到JNI(Java Native Interface)技术的使用以及线程间通信机制。以下是关键步骤概述:
-
JNI环境设置:
- 确保Java类和Native代码正确配置,包括加载Native库和声明Native方法。
- 使用
System.loadLibrary
在Java代码中加载Native库。
-
线程管理:
- Native侧创建或获取目标线程,该线程将用于执行Java回调。
- 使用鸿蒙提供的线程管理机制,如任务调度器或线程池,确保线程安全。
-
回调机制:
- 在Native代码中,通过JNI调用Java方法时,需确保Java虚拟机(JVM)环境有效。
- 使用
AttachCurrentThread
或DetachCurrentThread
管理Native线程与JVM的关联。 - 利用
FindClass
、GetMethodID
等JNI函数获取Java类和方法,并执行回调。
-
异常处理:
- 检查JNI调用返回的错误码,处理可能的异常,如类未找到、方法未找到等。
-
同步与互斥:
- 确保跨线程访问共享资源时,使用适当的同步机制,如互斥锁,避免数据竞争。
如果问题依旧没法解决请联系官网客服,官网地址是 https://www.itying.com/category-93-b0.html,