NDK学习( 三),多线程与异步回调

[Android]使用函数指针实现native层异步回调,作为程序员一定不要仅仅追求物质 if(event == nullptr || sender == nullptr) { return; } EventData eventData; eventData.eventPointer = event; eventData.sender = sender; eventMap.insert(std::pair<EventEnum, EventData>(eventEnum, eventData)); } 编写函数指针对应函数的具体实现 void eeSleepWakeCallback 阅读详情
场景:在Java中传递任意类型参数,在JNI中新开子线程,将传入的参数处理后回调Java中的方法
技术点:
1、在JNI中新开子线程并传递参数
2、在子线程中回调Java方法(误区:JNIEnv指针可以共享)

对于技术点1,可参考一般的C++开发教程,一般创建子线程的方法有:
  • 通过pthread_create
  • C++11中thread的用法

另外要注意的是传递的参数应当是堆内存中的指针,或者是全局变量,对于局部变量,可能在子线程中调用时已经失效。

对于技术点2,需要注意的是 JNIEnv *这类指针是无法在线程间共享的,参考JNI文档。
步骤:
  • 在JNI_OnLoad或者Java调用的native方法中缓存JavaVM*,JavaVM*指针是唯一在线程间可以共享的。
  • 在子线程的native方法中调用AttachCurrentThread ,获取JNIEnv*指针。
  • 通过JavaVM*和JNIEnv*找到你需要的jclasses,jobjects,jmethodIDs,它们不能在线程间共享。
  • 如果需要,将jclasses,jobjects转换为全局变量,jmethodIDs不需要转换,因为它不是对象。
  • 在JNI_OnUnlaod时删除全局变量这类引用,当你不再需要时。


理解 JNI_OnLoad、JNI_OnUnload

当native lib被加载(如System.loadLibrary()执行后),虚拟机会执行JNI_OnLoad,该方法必须返回当前JNI版本号。如果native lib不exportJNI_OnLoad方法,虚拟机认为lib只需要JNI_VERSION_1_1.如果返回的版本号不能被识别,该lib将不能被加载。

当包含native lib的类加载器被垃圾回收器回收,JNI_OnUnload会被调用。该方法可用来清理不再需要的指针。


Java代码:
public native void asyncCallBack(String str);//执行后向native lib传入字符串

native代码:
extern "C" {
JavaVM* javaVM; //定义要全局共享的虚拟机指针
jobject jinstance; //定义要共享的Java对象

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved){
javaVM = jvm; //将全局虚拟机指针赋值
JNIEnv* env = NULL;
jint result = -1;

//获取JNI版本

if (jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
{
LOGE("GetEnv failed!");
return result;
}
LOGE("JNI_VERSION:%d",JNI_VERSION_1_4);
return JNI_VERSION_1_4;
}

//线程函数
void* runAsync(void* args) {
const char* chars = (const char*) args;

JNIEnv *env = NULL;
//Attach主线程
if (javaVM->AttachCurrentThread(&env, NULL) != JNI_OK) {
LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
return NULL;
}
if (env != NULL) {
LOGE("jnienv* obtained");
}else {
return NULL;
}
LOGE("runAsync chars:%s", chars);
jclass jclass1 = env->GetObjectClass(jinstance);
if(jclass1==NULL){
LOGE("sub thread : failed get class");
return NULL;
}else{
LOGE("sub thread : find class");
}
jmethodID jmethodID =env->GetMethodID(jclass1,"callback","(Ljava/lang/String;)V");
if(jmethodID==NULL){
LOGE("sub thread : failed get jmethodID");
return NULL;
}else{
LOGE("sub thread : find jmethodID");
}
string str =chars;
string str2 = " from native";
str.append(str2);
jstring jstr=env->NewStringUTF(str.c_str());


env->CallVoidMethod(jinstance,jmethodID,jstr);
env->DeleteGlobalRef(jinstance);//删除全局引用
if(javaVM!=NULL){
javaVM=NULL;
LOGE("release JavaVM*");
}
if(jinstance!=NULL){
jinstance=NULL;
LOGE("release jinstance*");
}
LOGE("sub thread:task finish");
}

JNIEXPORT void JNICALL
Java_dev_mars_jnidemo_NativeThread_asyncCallBack(JNIEnv *env, jobject instance,jstring jstr) {
const char* chars = env->GetStringUTFChars(jstr,0);
LOGE("Java_dev_mars_jnidemo_NativeThread_asyncCallBack pass parameter:%s",chars);

//将jobject转换为全局变量,因为jclass、jobject也不能在线程间共享
jinstance = env->NewGlobalRef(instance);
pthread_t thread_1;
pthread_create(&thread_1,NULL,runAsync,(void*)chars);
}
}

小结:jobject、jclass、jmethodID这些类型无法在线程间共享,需要转换为全局变量。本例中在JNI接口线程中将jobject转换为全局变量,在子线程通过JavaVM*获取JNIEnv*,并通过全局变量jobect获取jclass,从而得到jmethod。

native调用java方法的函数主要有三种:
参考:

NativeType   Call<type>Method (JNIEnv *env, jobject obj,
jmethodID methodID, ...);
NativeType   Call<type>MethodA (JNIEnv *env, jobject obj,
jmethodID methodID, const jvalue *args);
NativeType   Call<type>MethodV (JNIEnv *env, jobject obj,
jmethodID methodID, va_list args);

通过观察发现,Method、MethodA、MethodV所带的参数类型不同,其中<type>是回调的返回类型。

创建jobject的全局引用通过 env->NewGlobalRef(instance);
回调完释放全局引用通过JNIEnv*的 env->DeleteGlobalRef(jinstance);

参考:

如本文有任何问题欢迎指出,谢谢
【鸿蒙实战开发】—— NDK开发 build目录:放置预定义的toolchain脚本文件ohos.toolchain.cmakeCMake编译时需要读取该文件中的默认值,比如编译器架构、C++库链接方式等,因此在编译时会通过CMAKE_TOOLCHAIN_FILE指出该文件的路径,便于CMake在编译时定位到该文件。build-tools文件夹:放置NDK提供的编译工具# 键入下一行命令查看CMake的版本# 结果llvm文件夹:放置NDK提供的编译器。 阅读详情

相关推荐

深入理解JNI与Android NDK在本地层异步工作中的应用

本文详细探讨了在Android开发中,如何通过JNI和NDK使用本地代码来提升性能和执行复杂的后台任务。首先介绍了如何配置HTTP超时和使用自签名的SSL证书来建立安全连接。接着,深入讲解了JNI接口的使用,包括如何在Java中调用C/C++函数,以及如何从原生代码中访问Java对象。此外,还讨论了如何使用Android NDK来编译C/C++代码到不同CPU架构的机器代码,并给出了在Java中加载和使用这些本地库的具体步骤。最后,文章还展示了如何在原生层创建和管理线程,以及如何处理Java异常。

weixin_26907223的博客 952

在JNI代码中异步回调Java层方法

楼主开发中有这样的情况,native代码中某回调函数需要回调Java层某方法,由于从c层代码回调时JNIEnv这个指针是取不到的,所以这成了一个问题。  google一下,这个问题还是可以解决的。 1.首先我们需要保存下来一个JavaVM的指针,如下: static JavaVM* jvm; 在一个init的方法里将这个指针保存下来(可以在JNI_OnLoad()里做) env-

ueryueryuery的专栏 8856

轻松学会JNI层多线程回调java方法

轻松学会JNI层多线程回调java方法,解决在c/c++层时,多线程环境下用过findClass异常问题。

Android - NDK :JNI实现异步回调

在android代码中,通过JNI调用c层子线程执行耗时任务,在c层子线程中把结果回调到android层,C语言小白,请批评指正!

dami_lixm的专栏 915

JNA 回调 范例 C++异步回调实现

这几天搞搞JNA,主要是组内小弟靠不住。 找了下资料,居然没有回调函数的样例,郁闷的我不行,于是就自己折腾一晚上,自己实现一个。 C++部分使用ACE实现了一个线程,线程中调用Java部分的回调函数。 C++使用vc8开发,代码跨平台,不过偷懒,没写makefile, 其中使用了ACE这个库。 Java部分,使用eclipse。jdk1.5版本。

JNI实现回调及JNI开启线程

实现了JNI回调JAVA的函数,以及在JNI中开启线程。

JNI 接口回调

JNI 接口回调 这里主要演示从 Java 层传入自定义listener,然后由 c/c++ 层在某一时期某一线程主动调用 listener 的回调函数,完成异步回调的功能。 关于 jni 的其他部分基础知识本文不做详细介绍。 Java 层定义代码 java-native 函数以及接口定义 package com.jnidemo; public class Greet { stat...

pang_gua的博客 5546

如何理解鲁棒性?为什么robustness会翻译为鲁棒性?

鲁棒性,英文为Robustness(承受故障和干扰的能力),是许多复杂系统(包括复杂网络)的关键属性。复杂网络的鲁棒性研究对许多领域都非常重要。本文着重介绍了鲁棒性的基本定义、命名起源、分类区别、提升方法和具体应用,供大家学习参考。

weixin_46880696的博客 8万+

JNI异步条件下(多线程/回调函数),如何取得JNI Env

JNI使用(异步条件下) JNIJVM多线程 1、JNI异步条件下(多线程/回调函数),如何取得JNI Env  使用AttachCurrentThread()函数。  示例代码:          #ifdef JNI_VERSION_1_4              jint res = cached_jvm->AttachCurrentThread((void

chenrunhua的专栏 4631

STM32CubeIDE离线安装芯片PACK包全攻略(附离线包下载与导入步骤)

本文详细介绍了在STM32CubeIDE中离线安装芯片PACK包的完整流程,包括官方和第三方资源获取方法、本地安装步骤及常见问题解决方案。特别针对网络隔离环境下的嵌入式开发需求,提供了实用的版本管理和自动化部署技巧,帮助开发者高效完成芯片固件支持包的离线配置。

weixin_30825581的博客 303

惊呆了!Oracle 11g从入门到放弃的保姆级指南(新手必看)

本文为Oracle 11g数据库新手提供了一份详尽的入门指南。内容涵盖从安装配置、SQL*Plus工具使用、核心的SQL增删改查操作,到事务、索引等进阶概念的解析。文章旨在帮助初学者避开常见陷阱,快速掌握这一经典关系型数据库管理系统的基本操作与核心技能,实现从入门到上手。

ooo22的博客 267

5【鸿蒙/OpenHarmony/NDK】应用太卡?用 Node-API 异步任务解决:从卡顿根源到流畅方案

鸿蒙NDK异步处理开发指南 本文聚焦鸿蒙NDK开发中的异步处理技术,解析核心概念、应用场景与避坑要点。异步处理通过避免主线程阻塞,保障应用流畅性,适用于文件操作、网络请求等耗时场景。文章结合Node-API开发规范,强调四个关键避坑点: execute回调禁止使用JS环境变量; complete回调仅做结果转换,避免耗时操作; 动态内存需严格遵循申请-传递-释放流程; 异步任务删除须在complete回调后执行。 附官方文档与系列教程链接,帮助开发者系统掌握NDK异步编程。

最简示例破局!基础用法秒会,深层原理不绕弯。 1181

为什么类型论是函数式编程的理想基础?

函数式编程(如Haskell、Scala、OCaml)以“函数为核心”“无副作用”“不可变数据”等特性著称,但它的“安全高效”并非凭空而来——背后的关键支撑是类型论。本文将聚焦“类型论如何支撑函数式编程”,从基础概念到具体案例,解释两者的本质联系。本文将按“故事引入→核心概念解释→关系拆解→原理+代码案例→实际应用→总结”的逻辑展开,用“乐高积木”“菜谱”等比喻贯穿全文,确保抽象理论可视化。类型论:程序的“接口规则手册”,定义类型和函数的约束;函数式编程。

AI天才研究院 694

NDK_OpenGLES_3_0多线程渲染:共享上下文与资源管理策略

Android OpenGL ES 3.0开发中,多线程渲染是提升复杂场景性能的关键技术。本文将详细介绍NDK_OpenGLES_3_0项目中共享EGL上下文的实现方案,以及多线程环境下的资源管理策略,帮助开发者构建高效稳定的图形渲染系统。 ## 为什么需要多线程渲染? 在移动图形应用开发中,单线程渲染往往会遇到性能瓶颈。特别是当需要同时处理复杂计算(如模型加载、滤镜处理)和实时渲染时,主线程

gitblog_00689的博客 823

交货单冲销发货过账的正确姿势:用 VL09 与 ABAP 批量反冲 Goods Issue

摘要 本文详细解析了SAP系统中VL09事务码的技术实现与业务应用。VL09用于反冲发货的货物移动,通过标准冲销路径(移动类型602冲销601)确保单据链路一致、状态机正确回退和业务规则生效。文章对比了经典VL09与Fiori应用在不同SAP产品形态中的位置,并针对STO场景提供了ME23N验证方法。核心内容包括WS_REVERSE_GOODS_ISSUE函数模块的调用要点、必须COMMIT的原因(SAP LUW机制),以及处理Handling Unit等限制条件的注意事项。最后给出了将VL09工程化的项目

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 594

已解决“TimeouEror [WinEror 10060由于连接方在一段时间后没有正确答复 或连接的主机没有反应,连接尝试失败“,亲测有效!!!

已解决"TimeouEror [WinEror 10060由于连接方在一段时间后没有正确答复 或连接的主机没有反应,连接尝试失败",亲测有效!!!

wbajsjhhhhh的博客 1万+

NDK基础

Android.mk:在 Android 上编译需要的配置文件,相当于 build.gradle,详细细节后面会讲到。各个平台架构的区别就是指令集不一样,浮点运算能力不一样,按照上面排列的顺序,浮点运算能力运行从低到高。需要一一对应,如果没有一一对应,外部模块调用了接口,编译的时候会提示找不到方法。存在的意义,方便代码不用重复编译, 最终为了生成。库对外开放的接口声明。这句代码代表着,使用这个类之前都会去加载。合并到一块的集合,经常在编译。所以接下来我们要构建。为了减少包体大小只接入。

qq_33552379的博客 2742

【Bug已解决】codex: conversation context window exceeded / Token limit reached — CodeX CLI 上下文窗口超限解决方案

CodeX CLI 上下文窗口超限(context_length_exceeded)的解决方案,含开新会话、更大窗口模型、精简提示与限制工具输出。

博客 4181

HarmonyOS 6 API 22 新特性NDK支持多线程创建组件能力介绍

HarmonyOS 6 API22引入的NDK多线程创建组件能力,突破了传统UI线程限制,显著提升UI开发性能。该特性允许在任意线程直接调用组件创建接口,无需线程切换,简化开发流程并充分利用多核CPU算力。开发者通过获取多线程NDK接口集合即可实现并发组件创建,但需注意线程安全规则,避免操作已挂载组件。该能力特别适用于高负载场景,如页面跳转和列表滑动,可有效降低UI线程负载,提升用户体验。

轻口味的专栏 1万+

halcon与QT联合:(5.1)瓶盖检测以及QT界面搭建

halcon实现瓶盖检测与定位代码: dev_close_window() dev_open_window(0, 0, 1024, 1024, 'black', WindowHandle) list_image_files('C:/Users/TIMI/Desktop/picture/circle', 'default', [], ImageFiles) for J := 1 to 4 by 1 *read image read_image(halconImage, ImageFil

m0_37957160的博客 1044

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

如何突破Android NDK限制:Tokio异步运行时适配完整指南

Tokio是一个使用Rust编写的可靠异步应用运行时,提供I/O、网络、调度和定时器等核心功能。本指南将详细介绍如何在Android NDK环境中集成和优化Tokio运行时,解决常见的性能瓶颈和兼容性问题,帮助开发者构建高效的异步应用。 ## 为什么选择Tokio进行Android NDK开发? Tokio作为Rust生态中最成熟的异步运行时,具备以下优势: - **高效的任务调度**:通过[

gitblog_01003的博客 480

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

鸿蒙5.0开发进阶:NDK开发-使用Node-API接口处理异步操作

使用Node-API接口处理异步操作。

Androidbye的博客 716

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Android-NDK-06-C++基础,多线程学习

学习工具:clion 学习环境:windows 环境配置:导入pthread包(自己可以去网上下载) CMakeLists文件环境配置代码: # cmake_minimum_required(VERSION <specify CMake version here>) project(ClionTestProject) # 目标库:ClionTestProject # TODO 第一步:引入线程库的所有头文件 include_directories("D:/NDK/Courseware

qq_23956889的博客 198

android 查看ndk 线程,NDK多线程的使用与问题

1. 前言前面已经介绍了POSIX线程原语,如果有不清楚的,可以查看之前的博客Linux入门之POSIX线程原语,下面我将在NDK开发中用POSIX线程开启线程执行异步操作。2. 多线程这里的需求就是在子线程中调用UUIDUtils(自定义类,非系统类)的get方法,这时候就需要用到JNIEnv,而每个线程都有独立的JNIEnv,可以通过JavaVM获取到每个线程关联的JNIEnv,那么问题就在于...

weixin_30152861的博客 600

鸿蒙5.0开发进阶:NDK开发-在非ArkTS线程中回调ArkTS接口

本示例将介绍通过napi_get_uv_event_loop和uv_queue_work实现在非ArkTS线程中通过NAPI接口回调ArkTS函数。

Androidbye的博客 1072

Android NDK开发 Java与C互相调用实例详解

http://abc20899.iteye.com/blog/1861121 一、概述       对于大部分应用开发者来说可能都不怎么接触到NDK,但如果涉及到硬件操作的话就不得不使用NDK了。使用NDK还有另一个原因,就是C/C++的效率比较高,因此我们可以把一些耗时的操作放在NDK中实现。       关于java与c/c++的互相调用,网上有一大堆的文章介绍。但仔细观察可以发

孙建军的博客 1029
上一篇: NDK学习( 二),在NDK开发中引入第三方库(AndroidStudio Cmake)
SouthMa
博客等级 码龄15年 6粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值