安卓JNI基础知识
JNI基础知识
- JNI简介
- NDK
- 配置开发环境
- JNI实践
- 配置CMake
- JNI编码
- JNI注册
- 1.静态注册
- 2.动态注册
- 编译方式
- CMakeLists编译
- Makefile编译
- 命令编译
- JNI和C/C++代码分离
- Java调用C/C++
- 查看so中包含的方法
- C/C++调用Java
- 打印C/C++的log
- 生成多个共享库so
- JNI调试
本文整理了JNI技术基础知识
JNI简介
JNI 是java原生接口(Java Native Interface),它定义了 Android 从受管理代码(使用 Java 或 Kotlin 编程语言编写)编译的字节码与原生代码(使用 C/C++ 编写)进行交互的方法,也就是安卓通过JNI技术提供Java调用C/C++或者C/C++调用Java的能力。JNI 不依赖于供应商,支持从动态共享库加载代码,虽然有时较为繁琐,但效率较高。
NDK
Android NDK(Native Development Kit),原生开发工具包,它是一组能将C或C++(“原生代码”)嵌入到Android 应用中的工具。可以帮助开发者快速开发C/C+的动态库,自动将so和java应用一起打包成apk。
NDK集成了一些交叉编译器,并提供了相应的mk文件,用于隔离CPU、平台、ABI等差异,开发人员通过配置mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以生成自己的so库。
原生共享库:NDK 从 C/C++ 源代码构建这些库或 .so 文件。so是shared object的缩写。
原生静态库:NDK 也可构建静态库或 .a 文件,而您可将静态库关联到其他库。
配置开发环境
- 下载NDK
File>Settings>Android SDK>SDK Tools>勾选需要的版本号>apply>OK - 配置项目NDK
如果NDK location无法编辑输入,可以在local.properties中新增ndk.dir
进行设置:
较新的项目,直接在app\build.gradle中直接配置ndk版本号即可。sdk.dir=D\:\\win10_program\\develop\\Android\\AndroidSDK ndk.dir=D\:\\win10_program\\develop\\Android\\AndroidSDK\\ndk\\26.1.10909125 // 已经过时的用法,可以直接删除此行配置,具体参考下面的操作。
命令查看ndk版本号:android {namespace 'com.xxx.xxx'compileSdk 33ndkVersion "25.2.9519653"... }
ndk-build --version
JNI实践
这里使用官方例子介绍。
配置CMake
配置CMake的目的是:告诉CMake改如何从源码编译生成目标库。
# 需要生成的目标库native-lib
# 也可以使用add_executable()生成可执行文件
add_library( # 指定要生成的目标库名称为native-libnative-lib# 将native-lib库设置默认为SHARED(原生共享库.so)或STATIC(原生静态库.a)SHARED# 生成native-lib库所需源码的相对路径列表。包含.cpp和.hsrc/main/cpp/native-lib.cppNativeImpl.cpp) # NativeImpl.cpp在后面代码分离部分实现# 指定源文件关联的头文件(适用于头文件和源文件分离的情况,但是也可以不写,因为.cpp中已经include了)
include_directories(src/main/cpp/include/)# 在已有库中查找需要的库,并将它的路径存储在变量xxx-lib中。类似用法的函数
# find_file()、find_path()、find_program()、find_package()
find_library( # 自定义变量的名称xxx-libxxx-lib#在ndk开发包中查找需要的libyyy.so,存储到xxx-lib变量中yyy ) # 将依赖的库文件链接到此目标库上
target_link_libraries(# 指定目标库native-lib# 将下面的库列表全部连接到目标库上${xxx-lib} # 获取find_library找到的yyy库android # 获取android库log) # 获取log库
注意:
1、如果对库文件有修改变动,请务必在Gradle之前清理一下项目 Build > Clean Project
。
2、如果需要生成多个共享库,可以在CMakeLists.txt文件中增加多个成对的add_library
和target_link_libraries
函数。
JNI编码
- 在Java侧声明调用方法。如
stringFromJNI
// 应用启动时,调用此函数会加载原生共享文件sodemo.sostatic {System.loadLibrary("sodemo"); //官方推荐使用:ReLinker.loadLibrary}/*** 声明此方法在原生端(共享文件sodemo.so)中实现,它与该应用程序打包在一起。*/public native String stringFromJNI();
- 在C侧实现具体方法
Java_com_wingtech_sodemo_JNIUtils_stringFromJNI
#include <jni.h>
#include <string>extern "C" JNIEXPORT jstring JNICALL
Java_com_wingtech_sodemo_JNIUtils_stringFromJNI(JNIEnv *env, jobject thiz) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
方法名称:Java_包名_类名_方法名
方法参数:JNIEnv* 是指向虚拟机环境的指针。jobject 是指向从 Java 端传递的隐式 this 对象的指针。
重要:C/C++和Java通过此方法名称建立了一对一映射关系。
JNI注册
1.静态注册
如果只有一个类具有原生方法,建议使用静态注册。使用标准 System.loadLibrary 从共享库加载原生代码。
从静态类初始化程序中调用 System.loadLibrary(或 ReLinker.loadLibrary)。具体静态注册同前面JNI编码部分所述。
2.动态注册
如果有多个类有原生方法,可以使用RegisterNatives
注册,也可以让运行时使用dlsym
动态查找它们。可以从 Application进行调用,这样始终加载该库,而且总是会提前加载。当执行到System.loadLibrary()
函数时,会回调JNI组件中的JNI_OnLoad()
函数;当释放该组件时会回调JNI_OnUnload()
函数。
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {JNIEnv* env;// 通过调用了GetEnv函数获取JNIEnv结构体指针env(JNI环境变量),JNIEnv结构体是指向一个函数表的,// 该函数表又指向了一些列对应的JNI函数。所以可以通过env和java交互,如GetObjectClass,CallVoidMethod等if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {return JNI_ERR;}// Find your class. JNI_OnLoad is called from the correct class loader context for this to work.jclass c = env->FindClass("com/example/app/package/MyClass");if (c == nullptr) return JNI_ERR;// 将所有方法装进数组中。这里数组中每个元素是结构体JNINativeMethod。// typedef struct {// const char* name;//Java层native方法的名字// const char* signature;//Java层native方法的描述符// void* fnPtr;//对应JNI函数的指针// } JNINativeMethod;static const JNINativeMethod methods[] = {{"nativeFoo", "()V", reinterpret_cast<void*>(nativeFoo)},{"nativeBar", "(Ljava/lang/String;I)Z", reinterpret_cast<void*>(nativeBar)},};// 使用RegisterNatives注册所有原生方法int rc = env->RegisterNatives(c, methods, sizeof(methods)/sizeof(JNINativeMethod));if (rc != JNI_OK) return rc;return JNI_VERSION_1_6;
}
jni函数的指针
void regist(JNIEnv *env, jobject thiz, jobject jCallback) {
LOGD(“–动态注册调用成功–>”);
jstring pJstring = env->NewStringUTF(“动态注册调用成功”);
jclass pJclass = env->GetObjectClass(thiz);
jmethodID id = env->GetMethodID(pJclass, “beInjectedDebug”, “(Ljava/lang/String;)V”);
//执行函数
env->CallVoidMethod(thiz,id,pJstring);
}
编译方式
一般有两种编译方式:
- 1、CMakeLists编译
- 2、Makefile编译
- 3、命令编译
CMakeLists编译
1、CMakeLists配置
具体配置如前面配置CMake的介绍,这里使用cpp目录下的CMakeLists.txt、native-lib.cpp文件生成.so库。
2、gradle配置
在app\build.gradle中设置库文件适配的CPU架构类型和CMakeLists.txt 文件路径。
android {namespace 'com.xxx.sodemo'compileSdk 33defaultConfig {applicationId "com.xxx.sodemo"minSdk 31targetSdk 33versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"ndk {// 生成so库类型abiFilters 'armeabi-v7a', 'arm64-v8a','x86','x86_64'}}externalNativeBuild {cmake {// 设置CMakeLists.txt文件路径path file('src/main/cpp/CMakeLists.txt')version '3.22.1'}}...}
3、编译库文件
点击Make按钮或Build->Make Project,运行结束后,会在 根目录/app/build/intermediates/cmake/debug/obj
路径下生成对应平台的.so库文件。
Makefile编译
使用MK文件编译,不需要编辑CMakeLists.txt,也不需要在build.gradle中配置,只要在Android.mk和Application.mk文件中配置好即可。一般在C/C++同目录下创建mk文件。
1、编写Android.mk文件
#设置当前编译路径为当前文件夹路径
LOCAL_PATH :=$(call my-dir)#清空编译环境的变量(由其他模块设置过的变量)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -lm -llog#指定生成模块的名称(库引用名称),编译时会自动添加lib前缀
LOCAL_MODULE :=JNITest123#需要编译的源文件。如果存在多个.cpp文件时使用"\"隔开
LOCAL_SRC_FILES :=native-lib.cpp \NativeImpl.cpp # NativeImpl.cpp在后面代码分离部分实现#生成动态库
include $(BUILD_SHARED_LIBRARY)
2、编写Application.mk文件
#模块名字,与Android.mk中保持一致
APP_MODULES := JNITest123#支持平台,这里支持所有平台
APP_ABI := all
APP_ALLOW_MISSING_DEPS=true
3、编译库文件
方式一:
1)检查编译环境
打开cmd窗口,运行ndk-build --version
,如下输出,说明ndk配置正确。
2)在cmd中进入C/C++文件所在目录下,执行ndk-build
命令编译。
NDK_PROJECT_PATH=. # 当前项目
APP_PLATFORM=android-16 # 有默认值,可以不设置
APP_BUILD_SCRIPT=./Android.mk # 当前目录下的Android.mk文件。注意:这里根据实际情况修改路径
NDK_APPLICATION_MK=./Application.mk # 当前目录下的Application.mk文件。注意:这里根据实际情况修改路径
NDK_LOG=1 # 打印编译日志
整理成一行命令执行:
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk NDK_LOG=1
3)执行结果:
说明:在哪个目录下执行ndk-build
命令编译,就在此目录下生成库文件。
E:\work\Test\Andriod\SoDemo\app\src\main>ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk APP_PLATFORM=android-16
Android NDK: WARNING: APP_PLATFORM android-16 is higher than android:minSdkVersion 1 in ./AndroidManifest.xml. NDK binaries will *not* be compatible with devices older than android-16. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md for more information.
[arm64-v8a] Compile++ : JNITest123 <= native-lib.cpp
[arm64-v8a] SharedLibrary : libJNITest123.so
[arm64-v8a] Install : libJNITest123.so => libs/arm64-v8a/libJNITest123.so
[x86_64] Compile++ : JNITest123 <= native-lib.cpp
[x86_64] SharedLibrary : libJNITest123.so
[x86_64] Install : libJNITest123.so => libs/x86_64/libJNITest123.so
[armeabi-v7a] Compile++ thumb: JNITest123 <= native-lib.cpp
[armeabi-v7a] SharedLibrary : libJNITest123.so
[armeabi-v7a] Install : libJNITest123.so => libs/armeabi-v7a/libJNITest123.so
[x86] Compile++ : JNITest123 <= native-lib.cpp
[x86] SharedLibrary : libJNITest123.so
[x86] Install : libJNITest123.so => libs/x86/libJNITest123.so
方式二:在Android Studio中,打开终端Terminal,cd进入C/C++文件所在目录的父目录下,执行ndk-build.cmd
即可。只要代码没有问题,一般可以在同级目录下生成文件。
PS E:\work\Test\Andriod\SoDemo\app\src\main> D:\win10_program\develop\Android\AndroidSDK\ndk\26.1.10909125\ndk-build.cmd
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-21.
[arm64-v8a] SharedLibrary : libJNITest123.so
[arm64-v8a] Install : libJNITest123.so => libs/arm64-v8a/libJNITest123.so
[x86_64] Compile++ : JNITest123 <= native-lib.cpp
[x86_64] SharedLibrary : libJNITest123.so
[x86_64] Install : libJNITest123.so => libs/x86_64/libJNITest123.so
[armeabi-v7a] Compile++ thumb: JNITest123 <= native-lib.cpp
[armeabi-v7a] SharedLibrary : libJNITest123.so
[armeabi-v7a] Install : libJNITest123.so => libs/armeabi-v7a/libJNITest123.so
[x86] Compile++ : JNITest123 <= native-lib.cpp
[x86] SharedLibrary : libJNITest123.so
[x86] Install : libJNITest123.so => libs/x86/libJNITest123.so
常见问题:
E:\work\Test\Andriod\SoDemo\app\src\main> D:\win10_program\develop\Android\AndroidSDK\ndk\21.0.6113669\ndk-build.cmd
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-16.
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file:
D:\win10_program\develop\Android\AndroidSDK\ndk\21.0.6113669/jni/Android.mk // 这里是路径问题jni
D:/win10_program/develop/Android/AndroidSDK/ndk/21.0.6113669/build//../build/core/add-application.mk:88: *** Android NDK: Aborting... . Stop.
因为这里的mk文件实际在src\main\cpp中,而NDK编译环境默认在jni目录下找mk文件,所以报错无法找到。这里可以将文件名称cpp修改为默认路径jni,也可以在ndk-build命令里指定mk的路径,具体修改如下。
ndk-build.cmd APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk
PS E:\work\Test\Andriod\SoDemo\app\src\main> D:\win10_program\develop\Android\AndroidSDK\ndk\26.1.10909125\ndk-build.cmd APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-21.
[arm64-v8a] Compile++ : JNITest123 <= native-lib.cpp
[arm64-v8a] SharedLibrary : libJNITest123.so
...
命令编译
JNI和C/C++代码分离
分离设计目的是希望在JNI文件中出现少量的C++代码。
1、编写.cpp文件
这里以NativeImpl.cpp为例
#include <jni.h>
#include "NativeImpl.h"NativeImpl::NativeImpl() {}NativeImpl::~NativeImpl() {}int NativeImpl::Clear_Zero() {LOGD("打印C++ LOGD");LOGE("打印C++ LOGE");LOGI("打印C++ LOGI");LOGW("打印C++ LOGW");return 0;
}
2、编写.h文件
这里以NativeImpl.h为例
#ifndef SODEMO_NATIVEIMPL_H
#define SODEMO_NATIVEIMPL_H
#include "LOG.h"#include <vector>
class NativeImpl {public:NativeImpl();virtual ~NativeImpl();virtual int Clear_Zero();
};#endif //SODEMO_NATIVEIMPL_H
3、在JNI文件中调用C++方法。
#include <jni.h>
#include "NativeImpl.h"NativeImpl nativeImpl;
NativeImpl* getNativeImpl(){return &nativeImpl;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_mytest_sodemo_JNIUtils_clrNumber(JNIEnv *env, jclass clazz) {// TODO: implement clrNumber()int zero = getNativeImpl()->Clear_Zero();return zero;
}
注意:代码分离后,需要将纯C++的源码添加到编译环境中,也就是在CMakeLists.txt的add_library方法中添加NativeImpl.cpp,或者在Android.mk的LOCAL_SRC_FILES中添加NativeImpl.cpp。具体参考上面的编译方式。
Java调用C/C++
Java调用C或C++程序,前提是给定了C或C++的动态库dll(Windows)或so(Linux)文件和函数头文件说明,这里介绍如何正确调用第三方so。
Java层调用C++函数主要通过建立的映射关系,这里jni函数调用java层的函数就要通过JNIEnv。
1、将第三方提供的so文件全部放进app\libs目录下,然后在app\build.gradle的sourceSets中配置libs,这样就会在打包时,自动把libs下的文件副本迁移到apk的lib目录下。
当然,这里的路径可以自定义,只要Gradle在打包时能通过你配置的路径,找到so的存放位置即可。
android {defaultConfig {...}sourceSets {main {jniLibs.srcDirs = ['libs'] // 打包时会把app\libs下的共享库.so的副本迁移到apk的lib目录下。// jniLibs.srcDirs = ['libs/test'] // 也可以在app\libs下新建各个公司或模块提供的库目录。// jniLibs.srcDirs = ['src/mylibs'] // 也可以自定义路径so的存储路径,只要能找到就行。}}...
}
2、根据已知的Java_xx包名_yy类名_方法名
格式(也可以通过nm命令获取so库的方法),在自己的项目中app\src\main\java目录下新建xx包名
,然后再创建一个和so中的yy类名
相同的类,这里要确保包名、类名、方法名、库名(不带lib前缀)四者一致,最后在自己的项目中直接调用yy类
中的方法即可。
extern "C" JNIEXPORT jstring JNICALL
Java_com_mytest_sodemo_JNIUtils_stringFromJNI(JNIEnv *env, jobject) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
注意:这一步非常重要,通过如此设计的方法名,建立了java和C之间的映射关系,所以在其他应用中使用时,也需要建立这种映射关系,否则报错UnsatisfiedLinkError。
//todo 缺图
3、清理和检查
清理后重新打包:Build>Clean Project>等一会儿>Make Project或者Build APK(s)>等一会儿。
检查so是否打入包内:Build>Analyze APK>OK>打开目标APK的lib目录。
4、常见异常
如果检查没有发现错误,编译运行后还是出现UnsatisfiedLinkError异常,多半是因为apk中的so没有打入包内,请按照第3步处理.
查看so中包含的方法
需要使用nm工具,一般在sdk\ndk\xx版本\toolchains\x86-4.9\prebuilt\windows-x86_64\i686-linux-android\bin
nm -D "so文件路径"
C/C++调用Java
打印C/C++的log
在CPP目录下新建head文件LOG.h
#ifndef SODEMO_LOG_H
#define SODEMO_LOG_H#include <android/log.h>#define TAG "haitao"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);#endif //SODEMO_LOG_H
在需要使用的文件中#include "LOG.h"
即可。
生成多个共享库so
如果需要生成多个共享库,可以在CMakeLists.txt再增加add_library
和target_link_libraries
参考配置CMake
# 生成libtest-1.so
add_library(test-1SHAREDnative-lib.cpp)
target_link_libraries(test-2 android)
...# 生成libtest-2.so
add_library(test-2 SHAREDnative-lib.cpp)
target_link_libraries(test-2android)
JNI调试
在Debug模式下,有时候会出现这个Permission denied的提示。
解决方法:退出App重新debug运行。如果退出无法解决此问题,重新USB连接即可。
与君共勉:人生自当扶摇上,揽星衔月逐日光。你只管去劈浪,与众生争锋芒,你举步是八万里宽广,你眼望是千江拍白浪,你平生这一趟,要让旁人想都不敢想!
相关文章:

安卓JNI基础知识
JNI基础知识 JNI简介NDK配置开发环境JNI实践配置CMakeJNI编码JNI注册1.静态注册2.动态注册 编译方式CMakeLists编译Makefile编译命令编译 JNI和C/C代码分离Java调用C/C查看so中包含的方法 C/C调用Java打印C/C的log生成多个共享库soJNI调试 本文整理了JNI技术基础知识 JNI简介 …...

Nginx高级技巧:实现负载均衡和反向代理
文章目录 Nginx概述Nginx作用正向代理反向代理负载均衡动静分离 Nginx的安装 -->Docker3.1 安装Nginx3.2 Nginx的配置文件3.3 修改docker-compose文件 Nginx源码安装nginx常用命令nginx配置文件配置文件位置配置文件结构详情 Nginx的反向代理【重点】基于Nginx实现反向代理4…...

2024年2月最新微信域名检测拦截接口源码
这段PHP代码用于检测指定域名列表中的域名是否被封。代码首先定义了一个包含待检测域名的数组 $domainList,然后遍历该数组,对每个域名发送HTTP请求并检查响应内容以判断域名是否被封。 具体步骤如下: 1. 定义待检测的域名列表。 2. 遍历域名…...

1、Linux-安装
一、Linux和Windows的一些区别 1、Linux严格区分大小写——【Windows创建文件夹时不区分大小写】 2、Linux中所有内容都以文件形式存储,包括硬件 3、Linux不靠拓展名区分文件类型,而是可以通过读取文件开头的一些字节来区分。 但是在实际使用中一般要…...
flutter 父组件调用子组件方法
当子组件是有状态组件 声明GlobalKey 如 声明 GlobalKey formKey GlobalKey<FormState>(); Form( key: formKey, autovalidateMode: AutovalidateMode.always, child: Column( children: <Widget>[ TextFormField( autofocus: true, initialValue: "a&quo…...

京东云硬钢阿里云:承诺再低10%
关注卢松松,会经常给你分享一些我的经验和观点。 阿里云刚刚宣布史上最大规模的全线产品降价20%,这热度还没过,京东云当晚就喊话:“随便降、比到底!,全网比价,击穿低价,再低10%”,并…...

Phoncent博客:探索AI写作与编程的无限可能
Phoncent博客,一个名为Phoncent的创新AIGC博客网站,于2023年诞生。它的创始人是庄泽峰,一个自媒体人和个人站长,他在网络营销推广领域有着丰富的经验。庄泽峰深知人工智能技术在内容创作和编程领域的潜力和创造力,因此…...

【Go-Zero】测试API查询信息无法返回数据库信息与api、rpc文件编写规范
【Go-Zero】测试API查询信息无法返回数据库信息与api、rpc文件编写规范 大家好 我是寸铁👊 总结了一篇测试API查询信息无法返回数据库信息与api、rpc文件编写规范的文章✨ 喜欢的小伙伴可以点点关注 💝 问题背景 大家好,我是寸铁!…...

SpringBootWeb快速入门
1.创建springboot工程,新建module 2.勾选web开发相关依赖 3.删除多余文件 4.新建类 5.启动类中运行main方法 6.启动 默认端口号8080 7.打开浏览器,地址栏输入 8.报错 9.原因,控制层位置放错,剪切controller层放进com.example …...

【书生·浦语大模型实战营】第 2 节 -课后作业
第二节 -轻松玩转书生浦语大模型趣味 Demo-课后作业 0.课程体验0.1 鸡兔同笼0.2 逻辑推理0.3 AI会毁灭人类吗? 1.课后作业1.1 基础作业1.1.1 作业11.1.2 作业2 0.课程体验 课程链接:https://github.com/internLM/tutorial 首先,这个课程是免费…...
Java如何使用OpenCV
背景:利用OpenCV功能批量处理视频 OpenCV(Open Source Computer Vision Library)是一个广泛应用于计算机视觉和图像处理领域的开源库。它提供了丰富的功能和工具,可用于处理图像和视频数据,并在各种应用中发挥着重要作…...

C++指针(三)
个人主页:PingdiGuo_guo 收录专栏:C干货专栏 文章目录 前言 1.字符指针 1.1字符指针的概念 1.2字符指针的用处 1.3字符指针的操作 1.3.1定义 1.3.2初始化 1.4字符指针使用注意事项 2.数组参数,指针参数 2.1数组参数 2.1.1数组参数的概念 2.1…...

消息中间件之RocketMQ源码分析(二十七)
Broker提交或回滚事务消息 当生产者本地事务处理完成并且Broker回查事务消息后,不管执行Commit还是Rollback,都会根据用户本地事务的执行结果发送一个End_transaction的RPC请求给Broker,Broker端处理该请求的类是EndTransactionProcessor 第一步&…...

C习题002:澡堂洗澡
问题 输入样例 在这里给出一组输入。例如: 2 5 1 3 3 2 3 3 输出样例 在这里给出相应的输出。例如: No代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB 栈限制 8192 KB 代码 #include<stdio.h> int main() {int N,W,s,t,p;int arr_s[…...

智能双星:遥测终端机与柳林“巡检机器人“,助力智能运维新升级!
随着科技的不断发展,智能化、自动化的运维管理已经成为企业追求高效、稳定运营的重要方向。柳林遥测终端机、柳林e拍云平台以及巡检机器人的组合,为企业带来了一种全新的、前置的、无感的智能运维体验。 柳林遥测终端机,以其强大的数据采集和…...
算法复习之前缀和【备战蓝桥杯】
一维前缀和 S[i] a[1] a[2] ... a[i] a[l] ... a[r] S[r] - S[l - 1]二维前缀和 S[i, j] 第i行j列格子左上部分所有元素的和 以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为: S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] S[x1 - 1, y1 - …...

IDEA基础——Maven配置tomcat
配置方案 一、配置maven-tomcat plugin插件(只最高支持到tomcat 8)~~1.添加镜像源,获取tomcat 8插件配置~~~~1.1 在pom.xml里先添加镜像源~~~~1.2 添加tomcat插件配置~~ 2. 添加tomact官方发布的插件配置(无需添加镜像源ÿ…...

数据结构测试题
目录 1.闰年判断 2.志愿者选拔 3.单词接龙 4.对称二叉树 5.英雄南昌欢迎您 6.时间转换 7.矩阵乘法 8. Huffuman树 1.闰年判断 题目描述: 给定一个年份,判断这一年是不是闰年。 当以下情况之一满足时,这一年是闰年: 1. 年…...

【MATLAB】兔子机器人总系统_动力学模型解读(及simulink中的simscape的各模块介绍)
1、动力学模型 Rectangular Joint 控制平面上(x,y轴)的移动,去掉以后,机器人在原地翻滚不移动 Rigid Transform 坐标转换,B站视频已收藏 去掉,机体与地面贴合 此处的作用是设定机体的初…...

Launch学习
参考博客: (1) 史上最全的launch的解析来啦,木有之一欧 1 ROS工作空间简介 2 元功能包 src目录下可以包含多个功能包,假设需要使用机器人导航模块,但是这个模块中包含着地图、定位、路径规划等不同的功能包,它们的逻…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...