Android Studio 进行NDK开发,实现JNI,以及编写C++与Java交互(Java调用本地函数)并编译出本地so动态库

1.首先认识一下NDK。

(1)什么是NDK?
NDK全称是Native Development Kit,NDK提供了一系列的工具,帮助开发者快速开发C/C++的动态库,并能自动将so和java应用一起打包成apk。NDK集成了交叉编译器(交叉编译器需要UNIX或LINUX系统环境),并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。

(2)为什么使用NDK?
1)代码的保护:由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
2)可以方便地使用现存的开源库:大部分现存的开源库都是用C/C++代码编写的。
3)提高程序的执行效率:将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
4)便于移植:用C/C++写得库可以方便在其他的嵌入式平台上再次使用。

(3)什么是JNI?
JNI全称为:Java Native Interface。JNI是本地编程接口,它使得在 Java 虚拟机内部运行的 Java代码能够与用其它语言(如 C、C++)编写的代码进行交互。

(4)为什么使用JNI?
JNI的目的是使java方法能够调用C/C++实现的一些函数。

(5)安卓中的so文件是什么?
Android中用到的so文件是一个C/C++的函数库。在android的JNI中,要先将相应的C/C++打包成so库,然后导入到lib文件夹中供java调用。

2.Android Studio 配置NDK(使用Android Studio 4.2.2之后的稳定版本)

(1) 步骤一:点击红圈处(这是Mac配置流程,Windows对应的按钮是Settings),如下图一

(2)步骤二:下载下图一中第3步红圈中的一个NDK和一个CMake,下载成功后如下图一所示(建议下载前先配置Android Studio 国内镜像代理,详见:Android Studio 国内镜像代理设置(如果设置之后还是远程仓库下载失败,请仔细阅读其内容就可以解决了)_android studio 镜像_ErwinNakajima的博客-CSDN博客)。

3.开始开发,在main文件夹下面创建一个cpp文件夹,如下图一和下圖二,然后在cpp文件夹下创建native-lib.cpp、return-data.cpp和CMakeLists.txt,然后添加具体内容;

native-lib.cpp文件的内容;

//
// Created by ErwinNakashima on 2022/12/24.
//
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <string>static jclass contextClass;
static jclass signatureClass;
static jclass packageNameClass;
static jclass packageInfoClass;/**之前生成好的签名字符串
*/
const char *SIGN_RELEASE = "308203b130820299a00302010202045fa239d4300d06092a864886f70d01010b0500308188310c300a06035504061303555341311c301a060355040813135374617465206f662043616c69666f726e69613112301006035504071309437570657274696e6f310e300c060355040a13056170706c65310e300c060355040b13056170706c653126302406035504030c1d72786a617661325f616e645f726574726f666974325f72656c65617365301e170d3232313232313133333232365a170d3437313231353133333232365a308188310c300a06035504061303555341311c301a060355040813135374617465206f662043616c69666f726e69613112301006035504071309437570657274696e6f310e300c060355040a13056170706c65310e300c060355040b13056170706c653126302406035504030c1d72786a617661325f616e645f726574726f666974325f72656c6561736530820122300d06092a864886f70d01010105000382010f003082010a02820101009f09581713e084950d06b016bf86063343864fe59952c335f3bb44e14ed67686749dcd372f075d2d74757c18dff9082941cbc7c83c60c3ad51d69ca503826775b2b703a6c347d70b8364b5f140cf7e1025590c8dcfa1469d2b5af242323b6c7bcddf92fb44aaa82a7c735597112964432fc16bb6dbf360f2a44d0e9e9722295b070582ea72310c674aed8ef8aa24ec06e972edafcae51d93c7370cfbc3e804fda3cc6f22ec89f98dac2ceb5607ef564fd3151091d2e0c142b984a21c61bc63b75e0bdc931dca1a9cc76f1ee326d59ef6edeb1d5dd07dd12fa32e55de3572784e8adc67388d643b310560f77e75ec944e00e6ae62d283c90c89eae5ce71747a9f0203010001a321301f301d0603551d0e041604147387f3952464b66ce5bb906d56845bc4410d8bc1300d06092a864886f70d01010b050003820101003ca9530822c2f272bcfb94dc2552045db8d4038385fbac917e08266f6f47b00ba36a735fc62da0c2d4bbe8fcfaec0d87c7c6223c257a22240f69057f954d90fda7c7dfe4daa2f3fa482aa1a35b56c1220b449115a670324408ae9f4f6dfa3af40b9c55275c27785bcfeb1337c7228ca2deac5c9e5b4fabd33e77f3fab0c18df0facfd23980a037907acd215c11a450d98789f002081379a688686b23b3aec1fbf4e3bf1db0e34daac5140e60ad412c11c1717c3befb83ca5878d1f5b199f6f4fee89591c9dbcec13a340c7aa817ab4d68b19598f57e60b08e950ba2843d5400b576511d8b4a0ac45accf92d5c82f0d9afc11bce5c2d58ae4f3f8e9da604e83c7";
const char *SIGN_DEBUG = "308202e4308201cc020101300d06092a864886f70d010105050030373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b30090603550406130255533020170d3231303830363136353432345a180f32303531303733303136353432345a30373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b300906035504061302555330820122300d06092a864886f70d01010105000382010f003082010a02820101008b2616bebb0dd79ea6e0688ce89e5ae221e3132ee9b6cc96514119557f0be3e79371e22c3c91250988a833942714ae562e8a86d6fff4290cb7b7bf718aeacd480d2cffe38c9787218e6391e562843b95dd26642b24e2106694501f0fe39186bf5fcc5c3cca91b9d86c113ffb0acf6e0e6ac9a4cda01110c5f18729d8f2f091b9fb604595a492ddcad6ae71dd190672cd8a675483563d5a734f9ec040890456ee02a32b61ccfc61811c8311b61eb90ffb15fae0db04f52c562b3713781fd772331619a4670065ed574e96da2cf47e7c4b29af30d5bbc1e271f23f3ea33b1085bb228e44d948d1f2adb0c71ee1c2652fe5b554d5e8e430c68f35b090f7d6dccbb10203010001300d06092a864886f70d010105050003820101004f1fd6247b615c2216e23eb8fe38e20282e9d5742b9485fec941fa541c97203eb60e3419fd6742d50bd2d60274d8489d1c03ab87f604aa2632aebdb2c7cc46e42f9f6dfec32155cca601fcf4abb3724068ccda637aa11c22d361afe9ec91b0d15209a9121c849aef791ceb670052e943891c34c0d380947f442ff93a93e8c6ac594d003f40ee0880dd0a0742ad1aa5c18f692b6480c3cf3baf42f5bacd8f31e811e88e98c187da52d4ed74aeaadb5f5f2c8b99c63612ce5abf4532151bcc4f3cab9b320c12b5c5e2c7fb6a69e72d6d1acdb43415dcecf9737ed124f28850d9e691cdb03a17c6c62a51fbd5c460067f3f890df085c4a849c05b061062d2aab16c";std::string hello = "(*^__^*) 嘻嘻……~Hello from C++ 特朗普的头发是黄色的";/*根据context对象,获取签名字符串
*/
const char *getSignString(JNIEnv *env, jobject contextObject) {jmethodID getPackageManagerId = (env)->GetMethodID(contextClass, "getPackageManager","()Landroid/content/pm/PackageManager;");jmethodID getPackageNameId = (env)->GetMethodID(contextClass, "getPackageName","()Ljava/lang/String;");jmethodID signToStringId = (env)->GetMethodID(signatureClass, "toCharsString","()Ljava/lang/String;");jmethodID getPackageInfoId = (env)->GetMethodID(packageNameClass, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");jobject packageManagerObject = (env)->CallObjectMethod(contextObject, getPackageManagerId);jstring packNameString = (jstring) (env)->CallObjectMethod(contextObject, getPackageNameId);jobject packageInfoObject = (env)->CallObjectMethod(packageManagerObject, getPackageInfoId,packNameString, 64);jfieldID signaturefieldID = (env)->GetFieldID(packageInfoClass, "signatures","[Landroid/content/pm/Signature;");jobjectArray signatureArray = (jobjectArray) (env)->GetObjectField(packageInfoObject,signaturefieldID);jobject signatureObject = (env)->GetObjectArrayElement(signatureArray, 0);return (env)->GetStringUTFChars((jstring) (env)->CallObjectMethod(signatureObject, signToStringId), 0);
}extern "C"
JNIEXPORT jstring JNICALL
Java_com_phone_library_1common_JavaGetData_nativeGetString(JNIEnv *env, jclass clazz,jobject context, jboolean is_release) {const char *signStrng = getSignString(env, context);bool isRelease = is_release;const char *SIGN;if (isRelease) {SIGN = SIGN_RELEASE;} else {SIGN = SIGN_DEBUG;}if (strcmp(signStrng, SIGN) == 0)//签名一致  返回合法的 api key,否则返回错误{return (env)->NewStringUTF(hello.c_str());} else {return (env)->NewStringUTF("error");}
}//bool toCppBool(jboolean value) {
//    return value == JNI_TRUE;
//}/**利用OnLoad钩子,初始化需要用到的Class类.
*/
JNIEXPORT jintJNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env = NULL;jint result = -1;if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK)return result;contextClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/Context"));signatureClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/pm/Signature"));packageNameClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/pm/PackageManager"));packageInfoClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/pm/PackageInfo"));return JNI_VERSION_1_4;
}

return-data.cpp文件的内容;

#include <jni.h>
#include <stdio.h>
#include <string.h>#ifdef __cplusplus
extern "C" {
#endifstatic jclass contextClass;
static jclass signatureClass;
static jclass packageNameClass;
static jclass packageInfoClass;/**之前生成好的签名字符串
*/
const char *SIGN_RELEASE = "308203b130820299a00302010202045fa239d4300d06092a864886f70d01010b0500308188310c300a06035504061303555341311c301a060355040813135374617465206f662043616c69666f726e69613112301006035504071309437570657274696e6f310e300c060355040a13056170706c65310e300c060355040b13056170706c653126302406035504030c1d72786a617661325f616e645f726574726f666974325f72656c65617365301e170d3232313232313133333232365a170d3437313231353133333232365a308188310c300a06035504061303555341311c301a060355040813135374617465206f662043616c69666f726e69613112301006035504071309437570657274696e6f310e300c060355040a13056170706c65310e300c060355040b13056170706c653126302406035504030c1d72786a617661325f616e645f726574726f666974325f72656c6561736530820122300d06092a864886f70d01010105000382010f003082010a02820101009f09581713e084950d06b016bf86063343864fe59952c335f3bb44e14ed67686749dcd372f075d2d74757c18dff9082941cbc7c83c60c3ad51d69ca503826775b2b703a6c347d70b8364b5f140cf7e1025590c8dcfa1469d2b5af242323b6c7bcddf92fb44aaa82a7c735597112964432fc16bb6dbf360f2a44d0e9e9722295b070582ea72310c674aed8ef8aa24ec06e972edafcae51d93c7370cfbc3e804fda3cc6f22ec89f98dac2ceb5607ef564fd3151091d2e0c142b984a21c61bc63b75e0bdc931dca1a9cc76f1ee326d59ef6edeb1d5dd07dd12fa32e55de3572784e8adc67388d643b310560f77e75ec944e00e6ae62d283c90c89eae5ce71747a9f0203010001a321301f301d0603551d0e041604147387f3952464b66ce5bb906d56845bc4410d8bc1300d06092a864886f70d01010b050003820101003ca9530822c2f272bcfb94dc2552045db8d4038385fbac917e08266f6f47b00ba36a735fc62da0c2d4bbe8fcfaec0d87c7c6223c257a22240f69057f954d90fda7c7dfe4daa2f3fa482aa1a35b56c1220b449115a670324408ae9f4f6dfa3af40b9c55275c27785bcfeb1337c7228ca2deac5c9e5b4fabd33e77f3fab0c18df0facfd23980a037907acd215c11a450d98789f002081379a688686b23b3aec1fbf4e3bf1db0e34daac5140e60ad412c11c1717c3befb83ca5878d1f5b199f6f4fee89591c9dbcec13a340c7aa817ab4d68b19598f57e60b08e950ba2843d5400b576511d8b4a0ac45accf92d5c82f0d9afc11bce5c2d58ae4f3f8e9da604e83c7";
const char *SIGN_DEBUG = "308202e4308201cc020101300d06092a864886f70d010105050030373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b30090603550406130255533020170d3231303830363136353432345a180f32303531303733303136353432345a30373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b300906035504061302555330820122300d06092a864886f70d01010105000382010f003082010a02820101008b2616bebb0dd79ea6e0688ce89e5ae221e3132ee9b6cc96514119557f0be3e79371e22c3c91250988a833942714ae562e8a86d6fff4290cb7b7bf718aeacd480d2cffe38c9787218e6391e562843b95dd26642b24e2106694501f0fe39186bf5fcc5c3cca91b9d86c113ffb0acf6e0e6ac9a4cda01110c5f18729d8f2f091b9fb604595a492ddcad6ae71dd190672cd8a675483563d5a734f9ec040890456ee02a32b61ccfc61811c8311b61eb90ffb15fae0db04f52c562b3713781fd772331619a4670065ed574e96da2cf47e7c4b29af30d5bbc1e271f23f3ea33b1085bb228e44d948d1f2adb0c71ee1c2652fe5b554d5e8e430c68f35b090f7d6dccbb10203010001300d06092a864886f70d010105050003820101004f1fd6247b615c2216e23eb8fe38e20282e9d5742b9485fec941fa541c97203eb60e3419fd6742d50bd2d60274d8489d1c03ab87f604aa2632aebdb2c7cc46e42f9f6dfec32155cca601fcf4abb3724068ccda637aa11c22d361afe9ec91b0d15209a9121c849aef791ceb670052e943891c34c0d380947f442ff93a93e8c6ac594d003f40ee0880dd0a0742ad1aa5c18f692b6480c3cf3baf42f5bacd8f31e811e88e98c187da52d4ed74aeaadb5f5f2c8b99c63612ce5abf4532151bcc4f3cab9b320c12b5c5e2c7fb6a69e72d6d1acdb43415dcecf9737ed124f28850d9e691cdb03a17c6c62a51fbd5c460067f3f890df085c4a849c05b061062d2aab16c";const char *aesKey = "rxjava_and_re_ro";
const char *databaseEncryptKey = "Aa123456";/*根据context对象,获取签名字符串
*/
const char *getSignString(JNIEnv *env, jobject contextObject) {jmethodID getPackageManagerId = (env)->GetMethodID(contextClass, "getPackageManager","()Landroid/content/pm/PackageManager;");jmethodID getPackageNameId = (env)->GetMethodID(contextClass, "getPackageName","()Ljava/lang/String;");jmethodID signToStringId = (env)->GetMethodID(signatureClass, "toCharsString","()Ljava/lang/String;");jmethodID getPackageInfoId = (env)->GetMethodID(packageNameClass, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");jobject packageManagerObject = (env)->CallObjectMethod(contextObject, getPackageManagerId);jstring packNameString = (jstring) (env)->CallObjectMethod(contextObject, getPackageNameId);jobject packageInfoObject = (env)->CallObjectMethod(packageManagerObject, getPackageInfoId,packNameString, 64);jfieldID signaturefieldID = (env)->GetFieldID(packageInfoClass, "signatures","[Landroid/content/pm/Signature;");jobjectArray signatureArray = (jobjectArray) (env)->GetObjectField(packageInfoObject,signaturefieldID);jobject signatureObject = (env)->GetObjectArrayElement(signatureArray, 0);return (env)->GetStringUTFChars((jstring) (env)->CallObjectMethod(signatureObject, signToStringId), 0);
}extern "C"
JNIEXPORT jstring JNICALL
Java_com_phone_library_1common_JavaGetData_nativeAesKey(JNIEnv *env, jclass clazz, jobject context,jboolean is_release) {const char *signStrng = getSignString(env, context);bool isRelease = is_release;const char *SIGN;if (isRelease) {SIGN = SIGN_RELEASE;} else {SIGN = SIGN_DEBUG;}if (strcmp(signStrng, SIGN) == 0)//签名一致  返回合法的 api key,否则返回错误{return (env)->NewStringUTF(aesKey);} else {return (env)->NewStringUTF("error");}
}extern "C"
JNIEXPORT jstring JNICALL
Java_com_phone_library_1common_JavaGetData_nativeDatabaseEncryptKey(JNIEnv *env, jclass clazz,jobject context,jboolean is_release) {const char *signStrng = getSignString(env, context);bool isRelease = is_release;const char *SIGN;if (isRelease) {SIGN = SIGN_RELEASE;} else {SIGN = SIGN_DEBUG;}if (strcmp(signStrng, SIGN) == 0)//签名一致  返回合法的 api key,否则返回错误{return (env)->NewStringUTF(databaseEncryptKey);} else {return (env)->NewStringUTF("error");}
}/**利用OnLoad钩子,初始化需要用到的Class类.
*/
JNIEXPORT jintJNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env = NULL;jint result = -1;if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK)return result;contextClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/Context"));signatureClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/pm/Signature"));packageNameClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/pm/PackageManager"));packageInfoClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/pm/PackageInfo"));return JNI_VERSION_1_4;
}#ifdef __cplusplus
}
#endif

CMakeLists.txt的內容。

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.10.2)# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.#设置so库的输出路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})add_library( # Sets the name of the library.native-lib# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).native-lib.cpp)
add_library( # Sets the name of the library.return-data# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).return-data.cpp)# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log)# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library.native-libreturn-data# Links the target library to the log library# included in the NDK.${log-lib})

4.配置ndk,在module的build.gradle文件下的android下配置;

sourceSets {main {jniLibs.srcDirs = ['libs']}}ndkVersion '16.1.4479499'externalNativeBuild {cmake {path file('src/main/cpp/CMakeLists.txt')version '3.10.2'}}

module的build.gradle文件下的defaultConfig下配置;

ndk {//选择要添加的对应 cpu 类型的 .so 库。abiFilters 'armeabi-v7a', 'arm64-v8a'
//            abiFilters 'armeabi-v7a', 'arm64-v8a'// 还可以添加 'x86', 'x86_64', 'mips', 'mips64'}

項目的gradle.properties文件下配置,如下图一;

android.useDeprecatedNdk=true

 項目的local.properties 文件下配置,如下图一。

ndk.dir=/Users/erwinnakashima/Library/Android/sdk/ndk/16.1.4479499

 5.添加JavaGetData文件,JavaGetData文件内容;

package com.phone.library_common;import android.content.Context;public class JavaGetData {static {System.loadLibrary("return-data");System.loadLibrary("native-lib");}public static native String nativeAesKey(Context context, boolean isRelease);public static native String nativeDatabaseEncryptKey(Context context, boolean isRelease);public static native String nativeGetString(Context context, boolean isRelease);}

在Application的onCreate方法中调用JavaGetData.nativeAesKey(),然后ReBuild Project。就会生成两个文件这几个so文件,如下图一,然后就能正常获取到C++文件(也就是cpp文件)中的数据了,还可以把so动态库提供给第三方使用,具体方式详见Android 项目调用第三方库so动态库_ErwinNakajima的博客-CSDN博客

如对此有疑问,请联系qq1164688204。

推荐Android开源项目

项目功能介绍:原本是RxJava2 和Retrofit2 项目,现已更新使用Kotlin+RxJava2+Retrofit2+MVP架构+组件化和
Kotlin+Retrofit2+协程+MVVM架构+组件化, 添加自动管理token 功能,添加RxJava2 生命周期管理,集成极光推送、阿里云Oss对象存储和高德地图定位功能。

项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2


 

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

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

相关文章

什么是RabbitMQ死信队列?如何实现?

死信队列解释&#xff1a; RabbitMQ的死信队列(DEAD Letter Queue,简称DLQ)&#xff0c;是一种用于消息处理失败或者无法路由的机制。它允许将无法正常消费的消息路由到另一个队列&#xff0c;以便于后续处理、排查。 出现死信队列的情况&#xff1a; 1、消息处理失败&#xff…

Android 13 动态启用或禁用IPV6

介绍 客户想要通过APK来控制IPV6的启用和禁用&#xff0c;这里我们通过广播的方式来让客户控制IPV6。 效果展示 adb shell ifconfig 这里我们用debug软件&#xff0c;将下面节点置为1 如图ipv6已被禁用了 echo 1 > /proc/sys/net/ipv6/conf/all/disable_ipv6 修改 接下来…

算法学习系列(十五):最小堆、堆排序

目录 引言一、最小堆概念二、堆排序模板&#xff08;最小堆&#xff09;三、模拟堆 引言 这个堆排序的话&#xff0c;考的还挺多的&#xff0c;主要是构建最小堆&#xff0c;并且在很多情况下某些东西还用得着它来优化&#xff0c;比如说迪杰斯特拉算法可以用最小堆优化&#…

Spring Boot学习随笔- Jasypt加密数据库用户名和密码以及解密

学习视频&#xff1a;【编程不良人】2021年SpringBoot最新最全教程 第十九章、Jasypt加密 Jasypt全称是Java Simplified Encryption&#xff0c;是一个开源项目。 Jasypt与Spring Boot集成&#xff0c;以便在应用程序的属性文件中加密敏感信息&#xff0c;然后在应用程序运行…

Openslide安装

文章目录 安装open-slide python下载openslide二进制文件解压到Anaconda的library目录下配置环境变量在py文件中添加以下语句即可 官网链接 安装open-slide python 表面上这样就可以导入了但事实上会遇到 Couldn’t locate OpendSlide DLL的问题&#xff0c;openslide必须独立安…

VSCODE : SSH远程配置+免密登录

SSH基础配置 填入地址&#xff0c;回车 ssh userhost-or-ip 然后选择默认的配置&#xff0c;回车&#xff0c;得到以下结果&#xff1a; 点击链接 选择远程的系统 输入密码 免密登录 生成SSH密钥&#xff1a; 首先&#xff0c;确保你已经在本地生成了SSH密钥。你可以使…

为什么要建设日志分析平台?

建设日志分析平台有多个重要原因&#xff0c;这些原因通常与提高系统性能、提升安全性、优化用户体验和满足合规要求等方面有关&#xff1a; 1. 系统监控与性能优化&#xff1a; - 日志分析平台可以帮助监控系统性能&#xff0c;及时发现并解决性能瓶颈。 - 通过分析日志…

vuereact中的副作用

前言 副作用&#xff08;side effect&#xff09;是指在函数或组件中&#xff0c;除了返回值或渲染结果之外&#xff0c;对外部产生的影响。即一个动作引起的其他关联的动作&#xff0c;例如&#xff0c;修改全局变量、发送网络请求、操作DOM、打印日志等&#xff0c;都是副作用…

nodejs+vue+微信小程序+python+PHP的艺术展览馆艺术品管理系统-计算机毕业设计推荐

选择轻量级的关系型MySQL数据库存储数据。接着进行系统的需求分析、功能设计、数据库设计&#xff0c;最后进行编码实现。具体如下&#xff1a; 1&#xff09;网站首页&#xff1a;艺术品浏览展示&#xff0c;艺术品作者线下。供会员浏览查看。 2&#xff09;注册登录&#xff…

OpenCV-Python(21):OPenCV查找及绘制轮廓

1.认识轮廓 1.1 目标 理解什么是轮廓学习掌握找轮廓、绘制轮廓等学习使用cv2.findContours()、cv2.drawContours()函数的用法 1.2 什么是轮廓 在OpenCV中&#xff0c;轮廓是图像中连续的边界线的曲线&#xff0c;具有相同的颜色或者灰度&#xff0c;用于表示物体的形状。轮廓…

数据库索引简析

文章目录 前言一、索引是什么二、索引的有什么用三、索引的分类四、索引的数据结构总结 前言 在我们使用数据库的过程中&#xff0c;往往会碰到一个叫做索引的东西&#xff0c;不管是表的设计&#xff0c;还是数据库性能的优化往往都会涉及到索引。那么他是个什么东西&#xff…

vue中使用echarts实现省市地图绘制,根据数据显示省市天气图标及温度信息

一、实现效果 使用echarts实现省市地图绘制根据数据显示省下市的天气图标根据数据显示省下市的温度信息 二、实现方法 1、安装echarts插件 npm install echarts --save2、获取省市json数据 https://datav.aliyun.com/portal/school/atlas/area_selector 通过 阿里旗下的高…

记录华为云服务器(Linux 可视化 宝塔面板)-- Nginx配置出现403错误记录(四种情景)

文章目录 第一种、配置文件index指定文件找不到第二种、root配置问题第三种、文件操作权限第四种、防火墙问题 最近配置多页项目&#xff0c;需指定根目录为某个页面 配置nginx出现了403的情况 location / {# autoindex on;root AuditAndInspection/;index index.html;try_file…

简述Redis备份策略以及对应的实现机制

引言 Redis作为高性能的内存数据库&#xff0c;数据的安全性至关重要。一旦数据丢失&#xff0c;可能会对业务造成重大影响。因此&#xff0c;备份Redis数据是每个Redis使用者都必须考虑的问题。本文将介绍Redis的备份策略以及对应的实现机制。 一、备份策略 1.1 定期备份 …

easycython和cython将py编译为pyd对比

前提了解 为了实验的准确性&#xff0c;在全过程使用的python环境版本都为同一版本 easycython和cython编译为pyd文件的不同在于&#xff0c;easycython编译的原始文件后缀为pyx&#xff0c;cython编译的原始文件为py 1.cython 1.1原始文件 def ZWHCythonTest():print(&qu…

QT+OSG/osgEarth编译之六十三:bvh+Qt编译(一套代码、一套框架,跨平台编译,版本:OSG-3.6.5插件库osgdb_bvh)

目录 1、bvh介绍 2、文件分析 3、pro文件 4、编译实践 1、bvh介绍 BVH(BioVision Hierarchy)是BioVision等设备对人体运动进行捕获后产生数据文件,它存储了角色的骨骼和肢体关节旋转数据。BVH是一种通用的人体特征动画文件格式,广泛地被当今流行的各种动画制作软件(如…

怎么用Facebook找客户?Facebook开发客户攻略分享

跨境人最了解的电商平台之一就是Facebook了&#xff0c;说到Facebook&#xff0c;它拥有超过20亿的活跃用户&#xff0c;所以你可以在这个平台上面找到更多的潜在客户。今天的文章就主要分享用Facebook开发客户的方法&#xff0c;全是干货&#xff0c;建议收藏&#xff01; 一、…

2024 年政府和技术预测

新的一年即将来临&#xff0c;这意味着专家、技术专家和专栏作家应该尝试预测 2024 年政府和技术即将出现的一些最大趋势。今年可能使这些预测变得更加困难的是事实上&#xff0c;许多技术正在以惊人的速度向前发展。在某些情况下&#xff0c;过去需要多年才能慢慢发生的变化现…

LLM之RAG实战(九)| 高级RAG 03:多文档RAG体系结构

在RAG&#xff08;检索和生成&#xff09;这样的框架内管理和处理多个文档有很大的挑战。关键不仅在于提取相关内容&#xff0c;还在于选择包含用户查询所寻求的信息的适当文档。基于用户查询对齐的多粒度特性&#xff0c;需要动态选择文档&#xff0c;本文将介绍结构化层次检索…

二维数组调整

前言 NWAFU 2021阶段二 B 一、题目描述 题目描述 编写Adjust_Arr函数&#xff0c;对动态二维数组中的每一行做数据位置调整。将偶数行(行号从0开始&#xff0c;并计作偶数行&#xff09;中最大元素与最前一个元素做交换&#xff0c;最小元素与最后一个元素做交换&#xff0c…