Android JNI基础

news2024/11/18 1:37:08

目录

  • 一、JNI简介
    • 1.1 什么是JNI
    • 1.2 用途
    • 1.3 优点
  • 二、初探JNI
    • 2.1 新建cpp\cmake
    • 2.2 build.gradle配置
    • 2.3 java层配置
    • 2.4 cmake和c++
  • 三、API详解
    • 3.1 JNI API
      • 3.1.1 数据类型
      • 3.1.2 方法
    • 3.2 CMake脚本
  • 四、再探JNI


一、JNI简介

1.1 什么是JNI

JNI(Java Native Interface)是Java提供的一种机制,用于实现Java和本地(Native)代码之间的交互。通过JNI,Java程序可以调用本地代码(如C、C++)中的函数,实现跨语言的互操作性。

在这里插入图片描述

1.2 用途

JNI主要用于以下几个方面:

调用系统级别的库和函数:可以使用JNI调用操作系统提供的底层功能,如文件操作、网络通信等。
提高性能:通过JNI将性能关键的部分用本地代码实现,可以提高程序的执行效率。
访问硬件资源:JNI可以用于访问硬件资源,如摄像头、传感器等。
调用第三方库:可以使用JNI调用第三方的本地库,以实现更多功能。

1.3 优点

使用JNI具有以下几个优点:

跨语言支持:可以与本地代码(如C、C++)进行无缝交互,扩展了Java的能力。
性能优势:通过JNI可以将性能关键的部分用本地代码实现,提高程序的执行效率。
访问系统资源:可以通过JNI访问系统级别的资源和功能,扩展了Java程序的能力。
调用第三方库:可以利用JNI调用第三方的本地库,扩展了Java程序的功能。
native层的代码往往更加安全,反编译so文件比反编译jar文件要难得多,往往把涉及到密码密钥相关的功能用C/C++实现,然后java层通过jni调用

通过JNI,Java程序可以充分利用本地代码的优势,实现更多功能和性能优化,同时也扩展了Java的应用领域。


二、初探JNI

为了更直接了解JNI,用一个简单的示例先来看一下JNI。

2.1 新建cpp\cmake

使用安卓Studio
在app/src/main文件夹下新建cpp文件夹
在这里插入图片描述

cpp文件夹下新建CMakeLists.txt和native-lib.cpp
在这里插入图片描述

2.2 build.gradle配置

配置模块名称"myjni"和产出的架构平台

  defaultConfig {
        applicationId "com.henry.basic"
        namespace "com.henry.basic"
		......
        ndk {
            // 指定库名称
            moduleName "myjni"
            // 指定需要产出哪些架构平台
            abiFilters "arm64-v8a", "armeabi-v7a", "x86", "x86_64"
        }
    }

指定CMake脚本路径和版本号

android {
    compileSdk 34
	......
    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.22.1'
        }
    }
}

2.3 java层配置

定义一个JNIDemo 类
静态加载myjni 库
native方法返回String字符串,一会在c++层面实现。

public class JNIDemo {

    static {
        System.loadLibrary("myjni");
    }

    public JNIDemo() {

    }

    public native String helloJni();

}

static { System.loadLibrary("myjni"); } 会在类被加载到内存中时执行。当首次使用该类时,Java虚拟机会加载并初始化这个类,执行静态代码块中的代码,从而加载本地库 myjni。这确保了在调用任何类方法或创建类实例之前,本地库已经被加载进内存,以便后续的JNI调用可以正常进行。

Launcher中的Activity
在Oncreate中实例化JNIDemo,并调用native方法。

public class jniActivity extends AppCompatActivity {

    String TAG = "henry";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog_test);
        JNIDemo demo = new JNIDemo();
        Log.d(TAG, "      : " + demo.helloJni());
    }
    
}

2.4 cmake和c++

上层和配置已经完成
接下来就剩下c++层面的实现和cmake脚本的编写了。

初学者看到cmake脚本肯定会头大,建议新建工程时选择C++,工程会自动生成Cmake脚本和cpp层面的实现,在此基础上学习Cmake。
在这里插入图片描述
回到我们当前的工程编写c++和Cmake

native-lib.cpp:

#include <jni.h>
#include <string>


extern "C"
JNIEXPORT jstring JNICALL
Java_com_henry_cmaketest_JNIDemo_helloJni(JNIEnv *env, jobject thiz) {
    // TODO: implement helloJni()
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

1.#include <jni.h>:包含了JNI的头文件,提供了JNI的相关函数和数据结构的定义。
2.#include < string>:包含了C++标准库中的字符串处理相关的头文件。
3.extern “C”:指定了使用C语言的调用约定,确保编译器正确处理函数名。
4.JNIEXPORT jstring JNICALL:定义了本地方法的返回类型为 jstring,表示返回一个Java的字符串对象。JNIEXPORT 和 JNICALL 是JNI的宏定义,用于声明本地方法。
5.Java_com_henry_cmaketest_JNIDemo_helloJni(JNIEnv *env, jobject thiz):实现了一个名为 helloJni 的本地方法,该方法与Java中的 JNIDemo 类中的 helloJni 方法对应。JNIEnv *env 是JNI环境指针,jobject thiz 是Java对象。
std::string hello = “Hello from C++”;:创建一个C++字符串 hello,内容为 “Hello from C++”。
return env->NewStringUTF(hello.c_str());:使用JNI环境指针 env 的 NewStringUTF 方法将C++字符串转换为Java字符串对象,并返回给Java调用方。

CMakeLists.txt

cmake_minimum_required(VERSION 3.22.1)

project("myjni")

add_library(${CMAKE_PROJECT_NAME} SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        native-lib.cpp)

target_link_libraries(${CMAKE_PROJECT_NAME}
        # List libraries link to the target library
        android
        log)

1.指定了CMake的最低版本要求为3.22.1
2.定义了项目名称为 “myjni”。
3.添加一个名为 ${CMAKE_PROJECT_NAME} 的共享库,这里会被替换为 “myjni”。共享库类型为 SHARED,表示构建一个共享库。native-lib.cpp 是该共享库的源文件,用于实现JNI的本地方法。
4.将目标库 ${CMAKE_PROJECT_NAME} 与指定的库进行链接。在这里,目标库会链接到 android 库和 log 库。android 库提供了Android平台相关的功能和API,而 log 库用于在Android平台上输出日志。

运行,看一下LOG输出: 正是我们native-lib.cpp里面定义的字符串。
在这里插入图片描述

本地使用Everything搜一下myjni字段:
在这里插入图片描述

打开.bat:
相关命令带一些参数
在这里插入图片描述

打开txt:
先编译cpp文件
然后链接共享库生成libmyjni.so
在这里插入图片描述
打开相关.so所在文件夹:四个架构的so库都已生成。
在这里插入图片描述

三、API详解

初探JNI已经完毕,接下来需要归纳一些知识了,稍微大一点的项目用到的不仅仅是上面的皮毛,比如CMake脚本的各种命令所代表的含义?native-lib.cpp里的JNIEXPORT、jstring 、JNICALL都是什么意思?

3.1 JNI API

上例只是简单的返回了一个字符串,实际上我们还可以做很多事情,jni.h都给我们定义好了标准,我们按照它的标准来即可。
比如,java中叫boolean,jni中叫jboolean,jni给我们提供了若干个映射表,将java中的类型与jni中的类型进行了一 一映射,其中包括基本数据类型映射,引用数据类型映射,方法签名(包含参数和返回值)映射,以下是这三个映射表:

3.1.1 数据类型

基本数据类型映射表
在这里插入图片描述
引用数据类型映射表
在这里插入图片描述
方法签名
在这里插入图片描述
示例

    public native String stringJni();
    public native float floatJni(int number,Boolean enabled);

对应的native层:

extern "C"
JNIEXPORT jfloat JNICALL
Java_com_henry_cmaketest_JNIDemo_floatJni(JNIEnv *env, jobject thiz, jint number, jobject enabled) {
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_henry_cmaketest_JNIDemo_stringJni(JNIEnv *env, jobject thiz) {
}

顺便讲一下J开头所代表的含义:

符号描述
extern “C”用于指定函数按照 C 的方式进行编译。在 JNI 开发中,由于 JNI 是用 C 语言编写的,因此需要使用 extern “C” 来告诉编译器按照 C 的方式进行编译,以确保函数名不会被 C++ 的名称修饰(name mangling)。
JNIEXPORT这是一个宏定义,用于指示编译器将函数导出为共享库中的一个 JNI 函数。在 JNI 开发中,通常会使用这个宏定义来声明 JNI 函数。
JNICALL这是一个宏定义,用于指示 JNI 函数的调用约定。在 JNI 开发中,通常会使用这个宏定义来声明 JNI 函数的调用约定。
JNIEnv *env这是一个指向 JNI 环境的指针,用于在本地代码中与 Java 虚拟机进行交互。通过 env 可以调用 JNI 提供的函数来获取 Java 对象、调用 Java 方法等操作。
jobject thiz这是一个代表当前对象的引用,通常用于在本地代码中调用 Java 对象的方法。在 JNI 中,thiz 代表当前调用 JNI 函数的 Java 对象。

3.1.2 方法

jni访问调用对象

方法描述
GetObjectClass获取调用对象的类,我们称其为target
FindClass根据类名获取某个类,我们称其为target
IsInstanceOf判断一个类是否为某个类型
IsSamObject是否指向同一个对象

jni访问java成员变量的值

方法描述
GetFieldId根据变量名获取target中成员变量的ID
GetIntField根据变量ID获取int变量的值,对应的还有byte,boolean,long等
SetIntField修改int变量的值,对应的还有byte,boolean,long等

jni访问java静态变量的值

方法描述
GetStaticFieldId根据变量名获取target中静态变量的ID
GetStaticIntField根据变量ID获取int静态变量的值,对应的还有byte,boolean,long等
SetStaticIntField修改int静态变量的值,对应的还有byte,boolean,long等

jni访问java成员方法

方法描述
GetMethodID根据方法名获取target中成员方法的ID
CallVoidMethod执行无返回值成员方法
CallIntMethod执行int返回值成员方法,对应的还有byte,boolean,long等

jni访问java静态方法

方法描述
GetStaticMethodID根据方法名获取target中静态方法的ID
CallStaticVoidMethod执行无返回值静态方法
CallStaticIntMethod执行int返回值静态方法,对应的还有byte,boolean,long等

jni访问java构造方法

方法描述
GetMethodID根据方法名获取target中构造方法的ID,注意,方法名传
NewObject创建对象

jni创建引用

方法描述
NewGlobalRef创建全局引用
NewWeakGlobalRef创建弱全局引用
NewLocalRef创建局部引用
DeleteGlobalRef释放全局对象,引用不主动释放会导致内存泄漏
DeleteLocalRef释放局部对象,引用不主动释放会导致内存泄漏

除此之外,jni还提供了异常处理机制,处理方式跟java一样有两种,要么往上(java层)抛,要么自己捕获处理

方法描述
ExceptionOccurred判断是否有异常发生
ExceptionClear清除异常
Throw往上(java层)抛出异常
ThrowNew往上(java层)抛出自定义异常

以上只是常用API,其他的可以自行到jni.h文件里去查看。

3.2 CMake脚本

CMake编写规则:

CMakeLists会自动创建两个变量,PROJECT_SOURCE_DIRPROJECT_NAME

字段描述示例
cmake_minimum_required指定所需的最低 CMake 版本。cmake_minimum_required(VERSION 3.10)
project定义项目的名称project(MyProject)
PROJECT_SOURCE_DIR本CMakeLists.txt所在的文件夹路径
PROJECT_NAME本CMakeLists.txt的project名称add_library(${CMAKE_PROJECT_NAME} SHARED native-lib.cpp)
add_executable添加可执行文件,指定可执行文件的名称和源文件。add_executable(my_executable main.cpp)
add_library添加库文件,指定库文件的名称和源文件。add_library(my_library my_source.cpp)
target_link_libraries链接库文件到可执行文件或其他库文件。target_link_libraries(my_executable my_library)
include_directories添加头文件搜索路径。include_directories(include)
link_directories添加库文件搜索路径。link_directories(lib)
set设置变量。set(SRC_FILES main.cpp)
if条件判断。if(CONDITION)…endif()
foreach遍历列表。foreach(item IN LISTS LIST_VARIABLE) …endforeach()
message输出消息。message(“Hello, CMake!”)
install安装目标文件到指定目录。install(TARGETS my_executable DESTINATION bin)
find_package查找外部库。find_package(OpenGL REQUIRED)
add_definitions添加编译器选项。add_definitions(-DDEBUG)
add_subdirectory添加子目录。add_subdirectory(subdir)
find_library在 CMake 中用于查找指定的库文件。find_library(VAR name)VAR:指定一个变量,用于存储查找到的库文件的路径。name:要查找的库文件的名称。
set_target_properties用于设置目标的属性。set_target_properties(my_executable PROPERTIES CXX_STANDARD 11)
IMPORTED_LOCATION指定 IMPORTED 目标的位置。set_target_properties(my_library PROPERTIES IMPORTED_LOCATION /path/to/libmylib.so)
log-lib是一个变量,通常用于存储 Android NDK 中日志库的名称target_link_libraries(my_executable ${log-lib})。
loglog 是 Android NDK 中的一个系统库,用于输出日志信息。target_link_libraries(my_executable log) 来链接 log 库。
PROPERTIESPROPERTIES 是用于设置目标属性的关键字 通过 set_target_properties 函数,可以设置目标的各种属性,如编译选项、输出路径等。set_target_properties(my_executable PROPERTIES CXX_STANDARD 11) 设置可执行文件的 C++ 标准为 C++11

四、再探JNI

这一次,多用一些JNI相关知识写一个demo

新建一个test方法,并添加到native-lib.cpp中。
在这里插入图片描述

再新建一个mycpp.cpp文件,activity中新建一个test2()方法,jni实现在mycpp.cpp文件中

CMakeLists.txt中添加mycpp.cpp:

cmake_minimum_required(VERSION 3.22.1)

project("myjni")

add_library(${CMAKE_PROJECT_NAME} SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        native-lib.cpp mycpp.cpp)

target_link_libraries(${CMAKE_PROJECT_NAME}
        # List libraries link to the target library
        android
        log)

Activity:

public class jniActivity extends AppCompatActivity {
    static {
        System.loadLibrary("myjni");
    }
    private int num = 1;
    String TAG = "henry";
    private TextView tv;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.jnitest);
        JNIDemo demo = new JNIDemo();
        tv = findViewById(R.id.jniview);
        Log.d(TAG, "      : " + demo.stringJni());
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                test();
                test2();
            }
        });
    }

    public native void test();
    public native void test2();
}

mycpp.cpp:

#include <jni.h>
#include <string>
#include <android/log.h>
#define TAG    "henry"
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__) // 定义LOGD类型

extern "C"
JNIEXPORT void JNICALL
Java_com_henry_cmaketest_jniActivity_test2(JNIEnv *env, jobject thiz) {
    LOGD("-------------new cpp test");
}

native-lib.cpp:

#include <jni.h>
#include <string>
#include <android/log.h>

#define TAG    "henry"
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__) // 定义LOGD类型


extern "C"
JNIEXPORT jstring JNICALL
Java_com_henry_cmaketest_JNIDemo_stringJni(JNIEnv *env, jobject thiz) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

/**
 * 参数一:JNIEnv* env表示指向可用JNI函数表的接口指针,所有跟jni相关的操作都需要通过env来完成
 * 参数二:jobject是调用该方法的java对象,这里是MainActivity调用的,所以thiz代表MainActivity
 * 方法名:Java_包名_类名_方法名
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_henry_cmaketest_jniActivity_test(JNIEnv *env, jobject thiz) {
    //获取Activity的class对象
    jclass clazz = env->GetObjectClass(thiz);
    //获取MainActivity中num变量id
    /**
    参数1:MainActivity的class对象
    参数2:变量名称
    参数3:变量类型,具体见上《表3-方法签名》
    **/
    jfieldID numFieldId = env->GetFieldID(clazz, "num", "I");
    //根据变量id获取num的值
    jint oldValue = env->GetIntField(thiz, numFieldId);
    //将num变量的值+1
    env->SetIntField(thiz, numFieldId, oldValue + 1);
    //重新获取num的值
    jint num = env->GetIntField(thiz, numFieldId);
    //先获取tv变量id
    jfieldID tvFieldId = env->GetFieldID(clazz, "tv", "Landroid/widget/TextView;");
    //根据变量id获取textview对象
    jobject tvObject = env->GetObjectField(thiz, tvFieldId);
    //获取textview的class对象
    jclass tvClass = env->GetObjectClass(tvObject);
    //获取setText方法ID
    /**
    参数1:textview的class对象
    参数2:方法名称
    参数3:方法参数类型和返回值类型,具体见上《表3-方法签名》
    **/
    jmethodID methodId = env->GetMethodID(tvClass, "setText", "([CII)V");
    //获取setText所需的参数
    //先将num转化为jstring
    char buf[64];
    sprintf(buf, "%d", num);
    jstring pJstring = env->NewStringUTF(buf);
    const char *value = env->GetStringUTFChars(pJstring, JNI_FALSE);
    //创建char数组,长度为字符串num的长度
    jcharArray charArray = env->NewCharArray(strlen(value));
    //开辟jchar内存空间
    jchar *pArray = (jchar *) calloc(strlen(value), sizeof(jchar));
    //将num字符串缓冲到内存空间中
    for (int i = 0; i < strlen(value); ++i) {
        *(pArray + i) = *(value + i);
    }
    //将缓冲的值写入到上面创建的char数组中
    env->SetCharArrayRegion(charArray, 0, strlen(value), pArray);
    //调用setText方法
    env->CallVoidMethod(tvObject, methodId, charArray, 0, env->GetArrayLength(charArray));
    //释放资源
    env->ReleaseCharArrayElements(charArray, env->GetCharArrayElements(charArray, JNI_FALSE), 0);
    free(pArray);
    pArray = NULL;
}

看一下效果:
在这里插入图片描述

参考链接:
CMakeLists.txt的超傻瓜手把手教程
JNI 从入门到实践,爆肝万字详解!
基础JNI语法和常见使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1567693.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医疗器械网络安全 | 美国FDA审批程序和欧盟合格评定程序的区别

要进入美国与欧洲市场&#xff0c;均需要通过评定程序审批。 两者的审批流程核心区别在于&#xff1a;所有在美国上市流通的医疗器械产品必须经过FDA的审核认证&#xff0c;才能投放市场。而欧盟市场&#xff0c;医疗器械制造商只需要自证设备合规性&#xff0c;并有指定机构干…

5.2 通用代码,数组求和,拷贝数组,si配合di翻转数组

5.2 通用代码&#xff0c;数组求和&#xff0c;拷贝数组&#xff0c;si配合di翻转数组 1. 通用代码 通用代码类似于一个用汇编语言写程序的一个框架&#xff0c;也类似于c语言的头文件编写 assume cs:code,ds:data,ss:stack data segmentdata endsstack segmentstack endsco…

超文本传输协议HTTP

HTTP协议 在网络通信中&#xff0c;我们可以自己进行定制协议&#xff0c;但是也有许多已经十分成熟的应用层协议&#xff0c;比如我们下面说的HTTP协议。 HTTP协议简介 HTTP&#xff08;Hyper Text Transfer Protocol&#xff09;协议又叫做超文本传输协议&#xff0c;是一…

前端html+css+js常用总结快速入门

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 学习前端全套所有技术性价比低下且容易忘记&#xff0c;先入门学会所有基础的语法&#xff08;cssjsheml&#xff09;&#xff…

LabVIEW太赫兹波扫描成像系统

LabVIEW太赫兹波扫描成像系统 随着科技的不断发展&#xff0c;太赫兹波成像技术因其非电离性、高穿透性和高分辨率等特点&#xff0c;在生物医学、材料质量无损检测以及公共安全等领域得到了广泛的应用。然而&#xff0c;在实际操作中&#xff0c;封闭性较高的信号采集软件限制…

使用ffmpeg将视频解码为帧时,图像质量很差

当使用ffmpeg库自带的ffmpeg.exe对对视频进行解帧或合并时&#xff0c;结果质量很差。导致这种原因的是在使用ffmpeg.exe指令进行解帧或合并时使用的是默认的视频码率&#xff1a;200kb/s。 如解帧指令&#xff1a; ffmpeg.exe -i 600600pixels.avi -r 2 -f image2 img/%03d.…

AI绘图:Stable Diffusion WEB UI 详细操作介绍:进阶-面部修复和调参

结合两篇文章完成了本地部署和基础操作,现在我们来介绍下进阶内容:面部修复,高清修复和调参区。 一:脸部修复 面部修复的适用在画真人、三次元的场景,特别是在画全身的时候 一般在画全身,由于脸部占比的空间比较小,那么绘制出来的效果就会比较差 1.面部修复 SD 支持…

日志服务 HarmonyOS NEXT 日志采集最佳实践

作者&#xff1a;高玉龙&#xff08;元泊&#xff09; 背景信息 随着数字化新时代的全面展开以及 5G 与物联网&#xff08;IoT&#xff09;技术的迅速普及&#xff0c;操作系统正面临前所未有的变革需求。在这个背景下&#xff0c;华为公司自主研发的鸿蒙操作系统&#xff08…

Linux Shell:`cat`命令

Linux Shell&#xff1a;cat命令 Linux 系统中的 cat 命令是一种多用途的工具&#xff0c;主要用于查看、创建、连接和追加文件内容。其名称来源于 concatenate 的缩写&#xff0c;意味着它可以用来连接文件内容到标准输出&#xff08;屏幕&#xff09;。在日常使用中&#xf…

算法基础--二分

&#x1f600;前言 二分查找是一种常见的算法技巧&#xff0c;通过不断缩小搜索范围&#xff0c;快速找到目标值的算法。在实际应用中&#xff0c;二分查找可以应用于有序数组中的查找、求上界、求下界等问题&#xff0c;具有较高的效率和广泛的应用价值。 &#x1f3e0;个人主…

scoped原理及使用

一、什么是scoped&#xff0c;为什么要用 在vue文件中的style标签上&#xff0c;有一个特殊的属性&#xff1a;scoped。 当一个style标签拥有scoped属性时&#xff0c;它的CSS样式就只能作用于当前的组件&#xff0c;通过该属性&#xff0c;可以使得组件之间的样式不互相污染。…

C# 登录界面代码

背景 MVVM 是一种软件架构模式&#xff0c;用于创建用户界面。它将用户界面&#xff08;View&#xff09;、业务逻辑&#xff08;ViewModel&#xff09;和数据模型&#xff08;Model&#xff09;分离开来&#xff0c;以提高代码的可维护性和可测试性。 MainWindow 类是 View&a…

网络协议——VRRP(虚拟路由冗余协议)原理与配置

1. VRRP概述 单网关出现故障后下联业务中断&#xff0c;配置两个及以上的网关时由于IP地址冲突&#xff0c;导致通讯时断时续甚至通信中断。VRRP组播类的网络层协议 2. 协议版本 VRRP v2: 支持认证,仅适用于IPv4网络 VRRP v3: 不支持认证&#xff0c; 适用于IPv4和IPv6两种网…

ES学习日记(九)-------logstash导入数据

一、安装和下载 es官网下载地址 官方介绍:Logstash是开源的服务器端数据处理管道&#xff0c;能够同时从多个来源采集数据&#xff0c;转换数据&#xff0c;然后将数据发送到您最喜欢的“存储库”中。(我们的存储库当然是 Elasticsearch。) 下载和ES一样的版本(很重要,必须这…

适配器: stack与queue

模板的使用 容器的复用 传容器: 控制底层是那个控制传仿函数: 控制大小堆的建立 stack 特点: 后进先出底层: 容器的封装(vector, list, dequeue)场景: 模拟递归, 函数压栈等接口:empty(), size(), top(), push(), pop()代码: stack queue 特点: 先进先出底层: 容器的封装…

Linux中的shell脚本之流程控制循环遍历

3 条件判断 4 流程控制语句 1&#xff09;if 语句 案例&#xff0c;用户输入用户名和密码&#xff0c;判断用户名是否是admin,密码是否是123,如果正确&#xff0c;则显示登录成功 首先我创建了shell文件&#xff0c;touch getpawer 其中getpawer 是我自己命的名 #!/bin/bas…

基于SpringBoot的药品管理系统设计与实现

介绍 药品管理员&#xff1a; 登录、退出、药品信息录入、药厂信息录入、采购人信息录入、药品信息浏览、药厂信息浏览、采购人信息浏览、药品信息查询入库修改删除、药信息查询修改删除采购人信息查询修改删除、入库记录浏览出库记录浏览、系统帮助 取药处人员: 登录、退出、…

[源码] Android 上的一些快捷方式,如通知、快捷方式等

目录 一、通知0. 配置权限1. 测试发送通知代码2. 打开通知设置界面代码3. 前台服务创建常驻通知 二、快捷方式1. 测试添加动态快捷方式代码 三、开发者图块四、桌面小部件 基于jetpack compose 框架的使用代码 一、通知 参见 官方文档 0. 配置权限 <uses-permission andr…

Matlab梁单元有限元编程:铁木辛柯梁VS欧拉梁

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

PTA题解 --- 天梯赛的赛场安排(C语言)

今天是PTA题库解法讲解的第八天&#xff0c;今天我们要讲解天梯赛的赛场安排&#xff0c;题目如下&#xff1a; 解题思路&#xff1a; 这个问题的关键在于高效地为参赛学校的队员分配赛场&#xff0c;同时满足给定的条件。我们可以通过以下步骤解决这个问题&#xff1a; 存储每…