Android JNI3--JNI基础

1,C预处理器

C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。

所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。

#include  导入头文件
#if       if判断操作  【if的范畴 必须endif】
#elif     else if
#else     else
#endif    结束if
#define   定义一个宏
#ifdef    如果定义了这个宏 【if的范畴 必须endif】
#ifndef   如果没有定义这个宏 【if的范畴 必须endif】
#undef    取消宏定义
#pragma   设定编译器的状态

#if  举例:

#if 1 // ifcout <<  "真" << endl;#elif 0 // else ifcout <<  "假" << endl;#elsecout << "都不满足" << endl;#endif // 结束ifcout << "结束if" << endl;
#ifdef举例:
#ifndef isRelease // 如果没有isRelease这个宏
#define isRelease 1 // 是否是正式环境下 【我就定义isRelease这个宏】#if isRelease == true
#define RELEASE // 正式环境下 定义RELEASE宏#elif isRelease == false
#define DEBUG // 测试环境下  定义DEBUG宏#endif 
#endif 
#ifdef DEBUG // 是否定义了DEBUG这个宏cout << "在测试环境" << endl;
#else RELEASEcout << "在正式环境" << endl;
#endif // 结束IF

宏的取消#undef: 

#ifdef TEST// 是否定义了这个宏cout << "TEST" << endl;
#undef TEST// 取消宏的定义,下面的代码,就没法用这个宏了,相当于:没有定义过TEST宏
#endif

宏变量:

#define VALUE_I 99
#define VALUE_S "字符串"int main() {int i = VALUE_I; // 预处理阶段 宏会直接完成文本替换工作,替换后的样子:int i = 99;string s = VALUE_S; // 预处理阶段 宏会直接完成文本替换工作,替换后的样子:string s = "字符串";return 0;
}

宏函数,宏函数都是大写

优点:文本替换,不会造成函数调用开销

缺点:会导致代码体积增大

#define ADD(n1, n2) n1 + n2int main() {int r = ADD(1, 2);cout << r << endl;//返回3return 0;
}

2,JNI  java与native代码互调

在MainActivity中调用native方法:

public class MainActivity extends AppCompatActivity {public String name = "测试"; // 签名:Ljava/lang/String;static {System.loadLibrary("native-lib"); //静态代码块中加载native-lib文件}   public native void changeName();//调用native方法@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView tv = findViewById(R.id.txt_name);changeName();tv.setText(name);}}

使用命令javah -classpath . -jni com.test.MainActivity 生成头文件com_test_MainActivity.h

#include <jni.h>
#include <string>// 解决循环Copy的问题 第二次就进不来了
#ifndef _Included_com_test_MainActivity // 如果没有定义这个宏
#define _Included_com_test_MainActivity // 我就定义这个宏#ifdef __cplusplus // 如果是C++环境
extern "C" { // 全部采用C的方式 不准你函数重载,函数名一样的问题
#endif// 函数的声明
JNIEXPORT jstring JNICALL Java_com_test_MainActivity_changeName(JNIEnv *, jobject);#ifdef __cplusplus // 省略  如果是C++,啥事不干
}#endif

在native-lib.cpp里面实现changeName()方法

#include "com_test_MainActivity.h"// NDK工具链里面的 log 库 引入过来
#include <android/log.h>#define TAG "MainAcitvity"
//自动填充
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, 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__)// extern "C": 必须采用C的编译方式
// // 无论是C还是C++ 最终是调用到 C的JNINativeInterface,所以必须采用C的方式 extern "C"
// 函数的实现
extern "C"JNIEXPORT  // 标记该方法可以被外部调用jstring // Java <---> native 转换用的JNICALL // 代表是 JNI标记// Java_包名_类名_方法名 // JNIEnv * env  JNI:的桥梁环境   所有的JNI操作,必须靠他// jobject jobj  谁调用,就是谁的实例  MainActivity this
// jclass clazz 谁调用,就是谁的class MainActivity.classextern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_changeName(JNIEnv *env, jobject thiz) {// 获取classjclass j_cls = env->GetObjectClass(thiz);// 获取属性  L对象类型 都需要L// jfieldID GetFieldID(MainActivity.class, 属性名, 属性的签名)jfieldID j_fid = env->GetFieldID(j_cls, "name", "Ljava/lang/String;");// 转换工作 jstring 为JNI类型jstring j_str = static_cast<jstring>(env->GetObjectField(thiz ,j_fid));// 打印字符串  目标char * c_str = const_cast<char *>(env->GetStringUTFChars(j_str, NULL));LOGD("native : %s\n", c_str);LOGE("native : %s\n", c_str);LOGI("native : %s\n", c_str);// 修改名字 返回给java层jstring jName = env->NewStringUTF("张三");env->SetObjectField(thiz, j_fid, jName);
}

签名规则:

    Java的boolean  --- Z Java的byte  --- BJava的char  --- CJava的short  --- SJava的int  --- IJava的long  --- J   Java的float  --- FJava的double  --- DJava的void  --- VJava的引用类型  --- Lxxx/xxx/xx/类名;Java的String  --- Ljava/lang/String;Java的array  int[]  --- [I         double[][][][]  --- [[[Dint add(char c1, char c2) ---- (CC)Ivoid a()     ===  ()V

Java调用native方法,native方法调用java方法,互调

在MainActivity中:

public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}public native void callSum();public int sum(int number1, int number2) {return number1 + number2;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);callSum();}}

在native-lib.cpp中,实现callSum()代码:

extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_callSum(JNIEnv *env, jobject job) {jclass  mainActivityClass = env->GetObjectClass(job);// GetMethodID(MainActivity.class, 方法名, 方法的签名)jmethodID j_mid = env->GetMethodID(mainActivityClass, "sum", "(II)I");// 调用 Java的方法jint sum = env->CallIntMethod(job, j_mid, 8, 8);LOGE("sum result:%d", sum);}

3,JNI 数组操作

MainActivity:

public class MainActivity extends AppCompatActivity {private final static String TAG = MainActivity.class.getSimpleName();static {// System.load(D:/xxx/xxxx/xxx/native-lib); 这种是可以绝对路径的加载动态链接库文件System.loadLibrary("native-lib"); // 这种是从库目录遍历层级目录,去自动的寻找   apk里面的lib/libnative-lib.so}public native void testArrayAction(int count, String textInfo, int[] ints, String[] strs);@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);arrTest();}public void arrTest() {int[] ints = new int[]{1,2,3,4,5,6}; // 基本类型的数组String[] strs = new String[]{"张三","周五","王五"}; // 对象类型的数组testArrayAction(99, "你好", ints, strs);}
}

在native-lib.cpp中:

extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_testArrayAction(JNIEnv *env, jobject thiz,jint count,jstring text_info,jintArray ints,jobjectArray strs) {// ① 基本数据类型  jint count, jstring text_info, 最简单的int countInt = count; // jint本质是int,所以可以用int接收LOGI("参数一 countInt:%d\n", countInt);// const char* GetStringUTFChars(jstring string, jboolean* isCopy)const char *textInfo = env->GetStringUTFChars(text_info, NULL);LOGI("参数二 textInfo:%s\n", textInfo);// ② 把int[] 转成 int*// jint* GetIntArrayElements(jintArray array, jboolean* isCopy)int *jintArray = env->GetIntArrayElements(ints, NULL);// Java层数组的长度// jsize GetArrayLength(jarray array) // jintArray ints 可以放入到 jarray的参数中去jsize size = env->GetArrayLength(ints);for (int i = 0; i < size; ++i) {*(jintArray + i) += 100; // C++的修改,影响不了Java层LOGI("参数三 int[]:%d\n", *jintArray + i);}// 目前无法控制Java的数组 变化 +100// 操作杆 ----> JMV// env->/*** 0:           刷新Java数组,并 释放C++层数组* JNI_COMMIT:  只提交 只刷新Java数组,不释放C++层数组* JNI_ABORT:   只释放C++层数组*/env->ReleaseIntArrayElements(ints, jintArray, 0);// ③:jobjectArray 代表是Java的引用类型数组,不一样jsize strssize = env->GetArrayLength(strs);for (int i = 0; i < strssize; ++i) {jstring jobj = static_cast<jstring>(env->GetObjectArrayElement(strs, i));// 模糊:isCopy内部启动的机制// const char* GetStringUTFChars(jstring string, jboolean* isCopy)const char *jobjCharp = env->GetStringUTFChars(jobj, NULL);LOGI("参数四 引用类型String 具体的:%s\n", jobjCharp);// 释放jstringenv->ReleaseStringUTFChars(jobj, jobjCharp);}
}

4,JNI对象操作

创建Student类

public class Student {private final static String TAG = Student.class.getSimpleName();public String name;public int age;public String getName() {return name;}public void setName(String name) {Log.d(TAG, "Java setName name:" + name);this.name = name;}public int getAge() {return age;}public void setAge(int age) {Log.d(TAG, "Java setAge age:" + age);this.age = age;}public static void showInfo(String info) {Log.d(TAG, "showInfo info:" + info);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}

MainActivity:

public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}public native void putObject(Student student, String str); // 传递引用类型,传递对象public native void insertObject(); // 凭空创建Java对象public native void testQuote(); // 测试引用public native void delQuote(); // 释放全局引用@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void test02(View view) {Student student = new Student(); // Java newstudent.name = "史泰龙";student.age = 88;putObject(student, "九阳神功");}public void test03(View view) {insertObject();}public void test04(View view) {testQuote();}public void test05(View view) {delQuote(); // 必须释放全局引用}@Overrideprotected void onDestroy() {super.onDestroy();delQuote(); // Activity销毁时: 必须释放全局引用}
}

在native-lib.cpp中:

extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_putObject(JNIEnv *env,jobject thiz,jobject student,jstring str) {const char *strChar = env->GetStringUTFChars(str, NULL);LOGI("strChar:%s\n", strChar);env->ReleaseStringUTFChars(str, strChar);// --------------// 1.寻找类 Student// jclass studentClass = env->FindClass("com/test/Student"); // 第一种jclass studentClass = env->GetObjectClass(student); // 第二种// 2.Student类里面的函数规则  签名jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");jmethodID getName = env->GetMethodID(studentClass, "getName", "()Ljava/lang/String;");jmethodID showInfo = env->GetStaticMethodID(studentClass, "showInfo", "(Ljava/lang/String;)V");// 3.调用 setNamejstring value = env->NewStringUTF("Zhangsan");env->CallVoidMethod(student, setName, value);// 4.调用 getNamejstring getNameResult = static_cast<jstring>(env->CallObjectMethod(student, getName));const char *getNameValue = env->GetStringUTFChars(getNameResult, NULL);LOGE("调用到getName方法,值是:%s\n", getNameValue);// 5.调用静态showInfojstring jstringValue = env->NewStringUTF("静态方法你好,我是C++");env->CallStaticVoidMethod(studentClass, showInfo, jstringValue);
}
// C++ 堆 栈 ...
// JNI函数  局部引用,全局引用,...
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_insertObject(JNIEnv *env, jobject thiz) {/*jstring str = env->GetStringUTFChars();jstring str = env->GetStringUTFChars();jstring str = env->GetStringUTFChars();jstring str = env->GetStringUTFChars();jstring str = env->GetStringUTFChars();jstring str = env->GetStringUTFChars();jstring str = env->GetStringUTFChars();// 好习惯:// 我用完了,我记释放,在我函数执行过程中,不会导致 内存占用多env->ReleaseStringUTFChars()*/// 1.通过包名+类名的方式 拿到 Student class  凭空拿classconst char *studentstr = "com/test/Student";jclass studentClass = env->FindClass(studentstr);// 2.通过student的class  实例化此Student对象   C++ new Studentjobject studentObj = env->AllocObject(studentClass); // AllocObject 只实例化对象,不会调用对象的构造函数// 方法签名的规则jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");jmethodID setAge = env->GetMethodID(studentClass, "setAge", "(I)V");// 调用方法jstring strValue = env->NewStringUTF("zhansan");env->CallVoidMethod(studentObj, setName, strValue);env->CallVoidMethod(studentObj, setAge, 99);// env->NewObject() // NewObject 实例化对象,会调用对象的构造函数// ====================  下面是 Person对象  调用person对象的  setStudent 函数等// 4.通过包名+类名的方式 拿到 Student class  凭空拿classconst char *personstr = "com/test/Person";jclass personClass = env->FindClass(personstr);jobject personObj = env->AllocObject(personClass); // AllocObject 只实例化对象,不会调用对象的构造函数// setStudent 此函数的 签名 规则jmethodID setStudent = env->GetMethodID(personClass, "setStudent","(Lcom/test/Student;)V");env->CallVoidMethod(personObj, setStudent, studentObj);// 规范:一定记得释放【好习惯】// 第一类env->DeleteLocalRef(studentClass);env->DeleteLocalRef(personClass);env->DeleteLocalRef(studentObj);env->DeleteLocalRef(personObj);// 第二类// env->ReleaseStringUTFChars()// TODO 局部引用: jobject jclass jstring ...  【函数结束后,会自动释放】
}jclass dogClass; // 你以为这个是全局引用,实际上他还是局部引用extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_testQuote(JNIEnv *env, jobject thiz) {if (NULL == dogClass) {/*const char * dogStr = "com/derry/as_jni_project/Dog";dogClass = env->FindClass(dogStr);*/// 升级全局引用: JNI函数结束也不释放,反正就是不释放,必须手动释放   ----- 相当于: C++ 对象 new、手动deleteconst char * dogStr = "com/test/Dog";jclass temp = env->FindClass(dogStr);dogClass = static_cast<jclass>(env->NewGlobalRef(temp)); // 提升全局引用// 记住:用完了,如果不用了,马上释放env->DeleteLocalRef(temp);}// <init> V  是不会变的// 构造函数一jmethodID init = env->GetMethodID(dogClass, "<init>", "()V");jobject dog = env->NewObject(dogClass, init);// 构造函数2init = env->GetMethodID(dogClass, "<init>", "(I)V");dog = env->NewObject(dogClass, init, 100);// 构造函数3init = env->GetMethodID(dogClass, "<init>", "(II)V");dog = env->NewObject(dogClass, init, 200, 300);// 构造函数4init = env->GetMethodID(dogClass, "<init>", "(III)V");dog = env->NewObject(dogClass, init, 400, 500, 600);env->DeleteLocalRef(dog); // 释放// dogClass = NULL; // 是不是问题解决了,不能这样干(JNI函数结束后,还怎么给你释放呢)// 这样就解决了/*env->DeleteGlobalRef(studentClass);studentClass = NULL;*/
}// JNI函数结束,会释放局部引用   dogClass虽然被释放,但是还不等于NULL,只是一个悬空指针而已,所以第二次进不来IF,会崩溃// 非常方便,可以使用了
extern int age; // 声明age
extern void show(); // 声明show函数  5000行代码// 手动释放全局引用
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_delQuote(JNIEnv *env, jobject thiz) {if (dogClass != NULL) {LOGE("全局引用释放完毕,上面的按钮已经失去全局引用,再次点击会报错");env->DeleteGlobalRef(dogClass);dogClass = NULL; // 最好给一个NULL,指向NULL的地址,不要去成为悬空指针,为了好判断悬空指针的出现}// 测试下show();
}

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

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

相关文章

数据通信——VRRP

引言 之前把实验做了&#xff0c;结果发现我好像没有写过VRRP的文章&#xff0c;连笔记都没记过。可能是因为对STP的记忆&#xff0c;导致现在都没忘太多。 一&#xff0c;什么是VRRP VRRP全名是虚拟路由冗余协议&#xff0c;虚拟路由&#xff0c;看名字就知道这是运行在三层接…

Linux 基础(三)常用命令-文件目录

常用命令 man 和 --help文件&#xff08;夹&#xff09;文件补充命令 man 和 --help 查看命令帮助信息 man 命令命令 --helphelp 命令&#xff08;适用于绑定在shell里面的一些基础命令&#xff09; 文件&#xff08;夹&#xff09; mkdir -p dirrmdir -p dirrm -rf …cp […

Solr的入门使用

Solr是Apache下的一个顶级开源项目&#xff0c;采用Java开发&#xff0c;它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言&#xff0c;同时实现了可配置、可扩展&#xff0c;并对索引、搜索性能进行了优化&#xff0c;被很多需要搜索的网站中广泛使用。…

人员数量统计和跟踪的实现原理详细介绍,并提供完整实现代码

人数统计和跟踪项目的方法 目标是构建一个具有以下功能的系统。 读取视频中的帧。在输入框架上绘制所需的参考线。使用对象检测模型检测人员。标记检测到的人的质心。跟踪该标记质心的运动。计算质心移动的方向(向上还是向下移动)。计算进入或离开参考线的人数。根据计数,递…

C语言三子棋小游戏--数组的应用

注&#xff1a;在最后面&#xff0c;完整源码会以两种形式展现。在讲解时&#xff0c;以三个源文件的形式。 前言&#xff1a;三子棋&#xff0c;顾名思义&#xff0c;就是三个子连在一起就可以胜出。在本节我们要介绍的三子棋模式是这样子的&#xff1a;在键盘输入坐标&#x…

HTML详解连载(3)

HTML详解连载&#xff08;3&#xff09; 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽表单作用使用场景 input标签基本使用示例type属性值以及说明 input标签占位文本示例注意 单选框 radio代码示例 多选框-checkbox注意代码示例 文本域作用标签&#xff1…

【前端 | CSS】flex布局

基本概念 Flexible模型&#xff0c;通常被称为 flexbox&#xff0c;是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力 我们说 flexbox 是一种一维的布局&#xff0c;是因为一个 flexbox 一次只能处理一个维度上的元素布局&#xff0c;一行或者…

R语言4_安装BayesSpace

环境Ubuntu22/20, R4.1 你可能会报错说你的R语言版本没有这个库&#xff0c;但其实不然。这是一个在Bioconductor上的库。 同时我也碰到了这个问题&#xff0c;ERROR: configuration failed for package systemfonts’等诸多类似问题&#xff0c;下面的方法可以一并解决。 第…

Mr. Cappuccino的第61杯咖啡——Spring之BeanPostProcessor

Spring之BeanPostProcessor 概述基本使用项目结构项目代码运行结果源代码 常用处理器项目结构项目代码执行结果 概述 BeanPostProcessor&#xff1a;Bean对象的后置处理器&#xff0c;负责对已创建好的bean对象进行加工处理&#xff1b; BeanPostProcessor中的两个核心方法&am…

[内网渗透]CFS三层靶机渗透

文章目录 [内网渗透]CFS三层靶机渗透网络拓扑图靶机搭建Target10x01.nmap主机探活0x02.端口扫描0x03.ThinkPHP5 RCE漏洞拿shell0x04.上传msf后门(reverse_tcp)反向连接拿主机权限 内网渗透Target2&#xff08;1&#xff09;路由信息探测&#xff08;2&#xff09;msf代理配置&a…

LeetCode150道面试经典题--找出字符串中第一个匹配项的下标(简单)

1.题目 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 2.示例 3.思路 回溯算法&#xff1a;首先将…

LeetCode[1122]数组的相对排序

难度&#xff1a;Easy 题目&#xff1a; 给你两个数组&#xff0c;arr1 和 arr2&#xff0c;arr2 中的元素各不相同&#xff0c;arr2 中的每个元素都出现在 arr1 中。 对 arr1 中的元素进行排序&#xff0c;使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现…

【mysql】MySQL CUP过高如何排查?

文章目录 一. 问题锁定二. QPS激增会导致CPU飘高三. 慢SQL会导致CPU飘高四. 大量空闲连接会导致CPU飘高五. MySQL问题排查常用命令 一. 问题锁定 通过top命令查看服务器CPU资源使用情况&#xff0c;明确CPU占用率较高的是否是mysqld进程&#xff0c;如果是则可以明确CUP飘高的原…

[ubuntu]创建root权限的用户 该用户登录后自动切换为root用户

一、创建新用户 1、创建新用户 sudo useradd -r -m -s /bin/bash 用户名 # -r&#xff1a;建立系统账号 -m&#xff1a;自动建立用户的登入目录 -s&#xff1a;指定用户登入后所使用的shell2、手动为用户设置密码 passwd 用户名 二、为用户增加root权限 1、添加写权限 ch…

【redis基础】

目录 一、概述 1.NoSQL 1.1 简述 1.2 类型 1.3 应用场景 1.3.1 缓存 1.3.2 分布式锁 1.3.3 计数器 1.3.4 会话管理 1.3.5 消息队列 2.Redis 2.1 简述 2.2 特性 2.3 监听端口号 2.4 数据类型 二、安装 1.编译安装 2.RPM安装 三、目录结构 1.查看 2.主配置文…

[保研/考研机试] KY163 素数判定 哈尔滨工业大学复试上机题 C++实现

题目链接&#xff1a; 素数判定https://www.nowcoder.com/share/jump/437195121691718831561 描述 给定一个数n&#xff0c;要求判断其是否为素数&#xff08;0,1&#xff0c;负数都是非素数&#xff09;。 输入描述&#xff1a; 测试数据有多组&#xff0c;每组输入一个数…

Nginx转发的原理和负载均衡

一、Nginx转发的原理 Nginx是一个高性能的反向代理服务器&#xff0c;它可以用于实现请求的转发和负载均衡。以下是Nginx转发的基本原理&#xff1a; 客户端发送请求&#xff1a;客户端向Nginx服务器发送HTTP请求。 Nginx接收请求&#xff1a;Nginx服务器接收到客户端的请求。…

[Docker实现测试部署CI/CD----构建成功后钉钉告警(7)]

目录 15、钉钉告警创建项目群&#xff0c;然后添加机器人添加机器人Jenkins 系统配置项目配置修改Jenkinsfile文件&#xff0c;添加钉钉提示信息测试 不修改Jenkinsfile文件&#xff0c;添加钉钉提示信息测试 15、钉钉告警 创建项目群&#xff0c;然后添加机器人 首先需要在钉…

搭建 Python 环境 | Python、PyCharm

计算机 计算机能完成的工作&#xff1a; 算术运算逻辑判断数据存储网络通信…更多的更复杂的任务 以下这些都可以称为 “计算机”&#xff1a; 一台计算机主要由以下这几个重要的组件构成 CPU 中央处理器&#xff1a;大脑&#xff0c;算术运算&#xff0c;逻辑判断 存储器&…

Ubuntu下RabbitMQ安装与简单使用

一&#xff1a;RabbitMQ基本安装 1.更新依赖包(提前更新依赖包避免出现报错) sudo apt-get update 2.由于rabbitMq使用erlang语言开发&#xff0c;在安装rabbitMq之前需要安装erlang sudo apt-get install erlang 3.查看erlang是否安装成功 sudo erl 安装成功会出现下面的提示…