Android NDK与JNI开发实战:从零构建Java与C++的桥梁
1. 项目概述为什么要在Android里调用C/C如果你是一个Android开发者可能大部分时间都在和Java或Kotlin打交道。但当你遇到一些对性能要求极高的场景时比如图像处理、音视频编解码、物理引擎计算或者需要复用大量成熟的C/C库时纯Java/Kotlin就显得力不从心了。这时你就需要请出Android NDKNative Development Kit和JNIJava Native Interface这两位“老将”。简单来说这个过程就是让运行在Java虚拟机JVM上的Java/Kotlin代码能够跨越语言的边界去调用和执行由C/C编写的、直接运行在操作系统上的原生Native代码。这听起来有点复杂像是让两个说不同语言的人合作完成一项精密工作。但别担心只要工具和步骤对了这件事就像搭积木一样清晰。我见过很多新手卡在环境配置和第一个“Hello World”上被CMakeLists.txt、NDK路径、头文件生成这些概念绕晕。所以这篇内容的目标就是充当你的“贴身向导”我会用最详细的截图和讲解带你从零开始在Android Studio中完成一次完整的、可运行的C/C代码调用。我们不仅会跑通流程更会拆解每一步背后的原理让你彻底明白“为什么这么做”以及踩坑时“该怎么解决”。2. 环境准备与核心概念扫盲在动手之前我们必须把“工具箱”准备好并理解几个关键名词。这能避免你后续操作时一头雾水。2.1 必备工具安装与配置1. Android Studio这是我们的主战场。确保你安装的是较新版本如Hedgehog或Iguana以上因为它们对NDK和CMake的支持更完善、界面更友好。安装过程就是一路“Next”这里不赘述。安装完成后首次启动可能会提示安装Android SDK同意即可。2. Android NDKNative Development Kit这是谷歌官方提供的工具集包含了允许你在Android应用中使用C/C代码所需的编译器、库和头文件。没有它你的Java代码和C代码就无法“对话”。安装方式推荐在Android Studio内安装打开Android Studio点击File-Settings(Windows/Linux) 或Android Studio-Preferences(macOS)。 在左侧找到Appearance Behavior-System Settings-Android SDK。 切换到SDK Tools标签页。你会看到一个列表找到NDK (Side by side)并勾选它。同时确保CMake和LLDB也被勾选上。CMake是我们的构建脚本工具LLDB是用于调试Native代码的调试器。 点击Apply等待下载安装完成。(此处为示意图位置实际行文时需描述在SDK Tools选项卡中勾选NDK、CMake和LLDB)验证安装安装完成后NDK会默认放在Android SDK目录下的ndk文件夹里例如C:\Users\YourName\AppData\Local\Android\Sdk\ndk\25.2.9519653。这个长数字是版本号。记住这个路径有时需要手动指定。3. CMake你可以把它理解为一个高级的、跨平台的“项目构建说明书”生成器。我们不再直接编写复杂的Android.mk或Application.mk文件旧方式而是编写一个更简洁的CMakeLists.txt文件。CMake会根据这个文件为你的C/C代码生成对应平台如x86, arm64-v8a的构建脚本如Makefile或Ninja文件。Android Studio就是通过CMake来管理和编译你的Native代码的。注意在SDK Tools中安装的CMake是一个特定版本如3.22.1。如果你的项目需要其他版本可以在这里勾选多个版本然后在项目配置中指定。2.2 核心概念解析JNI与CMakeJNI (Java Native Interface)这是Java平台定义的一套标准编程接口是Java世界和Native世界C/C之间的“桥梁协议”或“翻译官”。它规定了如何声明在Java类中你需要用native关键字声明一个方法告诉JVM“这个方法的具体实现不在我这里在Native代码里”。如何命名Native层对应的C/C函数必须遵循一套严格的命名规则Java_包名_类名_方法名这样JVM才能准确地找到它。如何交互JNI提供了一大套函数JNIEnv指针让你在C/C代码里能够创建Java对象、调用Java方法、访问Java字段反之亦然。CMake如前所述它是构建系统的构建系统。在Android项目中CMakeLists.txt文件是核心它告诉CMake需要编译哪些C/C源文件.cpp,.c。这些源文件生成什么样的库动态库.so还是静态库.a。需要链接哪些其他的库比如OpenCV、FFmpeg。头文件.h的搜索路径在哪里。Android Studio的Gradle插件会读取你的模块级build.gradle文件中的CMake配置然后调用CMake去执行CMakeLists.txt最终编译出.so文件并打包进APK。关系梳理Java代码(声明native方法)--(通过JNI规则)-- C/C代码(实现函数)--(通过CMake编译)-- 原生库.so--(打包进APK)-- 在设备上Java代码通过System.loadLibrary加载.so库最终调用到Native函数。3. 实战创建并配置一个支持C/C的Android项目理论说再多不如动手做一遍。我们从一个新项目开始。3.1 创建新项目打开Android Studio选择New Project。在模板选择界面为了简单起见我们选择Empty Views ActivityEmpty Activity也可以。点击Next。配置项目信息Name:NativeDemoPackage name:com.example.nativedemoSave location: 按你的习惯选择Language: 选择Java(为了更直观地展示JNIKotlin原理相同但语法稍有差异)Minimum SDK: 选择一个足够低的版本以覆盖更多设备例如API 24: Android 7.0。在底部你会看到一个Use legacy android.support libraries的选项不要勾选。关键一步在配置页面的最下方找到Configuration折叠菜单点击展开。这里有一个C Standard的下拉菜单。这就是创建支持Native项目的关键从下拉菜单中选择C17或Toolchain Default。这决定了编译你的C代码时使用的语言标准。下方的Exceptions Support和Runtime Type Information保持默认勾选即可它们允许你使用C异常和RTTI特性。点击Finish。Android Studio会自动创建一个包含C支持的基本项目结构这会比普通项目多花一点时间。3.2 项目结构解析创建完成后观察你的项目Android视图app/src/main/java/: 你的Java代码在这里。app/src/main/cpp/:这是一个新文件夹里面存放了你的C源文件。默认会有一个native-lib.cpp文件和一个CMakeLists.txt文件。这就是项目的Native部分。app/build.gradle (Module :app): Gradle构建脚本已经自动添加了CMake的配置。我们重点看一下自动生成的两个核心文件1.app/src/main/cpp/native-lib.cpp#include jni.h #include string extern C JNIEXPORT jstring JNICALL Java_com_example_nativedemo_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello Hello from C; return env-NewStringUTF(hello.c_str()); }#include jni.h: 引入JNI头文件这是必须的。extern C: 告诉C编译器以C语言的方式编译这个函数。因为JVM是C语言实现的它需要按照C语言的函数名修饰规则来查找函数。没有这个C的命名修饰mangling会导致JVM找不到函数。JNIEXPORT和JNICALL: 这是JNI规范定义的宏确保函数在动态库中可见且调用约定正确。jstring: JNI数据类型对应Java中的String。Java_com_example_nativedemo_MainActivity_stringFromJNI: 这就是JNI函数命名规则Java_包名类名方法名。包名中的点用下划线代替。JNIEnv* env: 指向JNI环境的指针是所有JNI操作的入口比如创建Java字符串 (NewStringUTF)。jobject this: 对应调用这个native方法的Java对象实例如果是静态方法则是jclass类型。这里参数名被注释掉了因为没用到。2.app/src/main/cpp/CMakeLists.txtcmake_minimum_required(VERSION 3.22.1) # 指定CMake最低版本 project(nativedemo) # 项目名称 add_library( # 添加一个库 native-lib # 库的名称稍后在Java中加载库时要用这个名字 SHARED # 库的类型SHARED 表示动态库 (.so)STATIC 表示静态库 (.a) native-lib.cpp # 库的源文件可以列出多个 ) find_library( # 查找一个系统预构建库 log-lib # 给找到的库起个别名 log # 要查找的库的实际名称这里是Android的日志库liblog.so ) target_link_libraries( # 将目标库与其它库链接起来 native-lib # 目标库就是我们上面定义的 native-lib ${log-lib} # 要链接的库这里链接了日志库这样我们才能在C中使用__android_log_print )这个文件逻辑很清晰定义项目、创建库、查找依赖库、链接依赖库。3.app/build.gradle (Module :app)中的CMake配置android { ... defaultConfig { ... externalNativeBuild { cmake { cppFlags // 可以在这里传递C编译标志如-stdc17 // 指定ABI过滤器减少APK体积。例如只打包arm64-v8a和armeabi-v7a // abiFilters arm64-v8a, armeabi-v7a, x86, x86_64 } } } buildTypes { ... } externalNativeBuild { cmake { path file(src/main/cpp/CMakeLists.txt) // 指定CMakeLists.txt的路径 version 3.22.1 // 指定CMake版本与CMakeLists.txt中声明的一致 } } }Gradle通过这段配置知道去哪里找CMake构建脚本以及传递哪些参数给CMake。3.3 编写Java层代码调用Native方法现在看MainActivity.javapackage com.example.nativedemo; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // 1. 加载原生库。参数“native-lib”必须与CMakeLists.txt中add_library定义的库名一致 static { System.loadLibrary(native-lib); } // 2. 声明一个native方法。这个方法没有方法体其实现将在C中完成 public native String stringFromJNI(); Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv findViewById(R.id.sample_text); // 3. 调用native方法并将返回的字符串设置到TextView上 tv.setText(stringFromJNI()); } }流程非常清晰静态代码块加载库 - 声明native方法 - 像调用普通Java方法一样使用它。3.4 构建与运行连接你的Android设备或启动模拟器确保是64位系统镜像因为现代NDK默认编译64位库。点击Android Studio工具栏上的绿色运行按钮或ShiftF10。应用安装并启动后你应该看到屏幕上显示“Hello from C”。恭喜你已经完成了Android调用C代码的“Hello World”。但这只是开始我们还需要深入更多细节。4. 深入JNI数据类型映射与复杂操作在实际项目中我们不可能只传递一个字符串。JNI定义了Java类型和Native类型C/C类型之间完整的映射关系。4.1 基本数据类型映射Java 类型JNI 类型C/C 类型大小/范围booleanjbooleanunsigned char8位bytejbytesigned char8位charjcharunsigned short16位shortjshortsigned short16位intjintsigned int32位longjlongsigned long long64位floatjfloatfloat32位doublejdoubledouble64位voidvoidvoidN/A在Native函数中你可以直接使用这些JNI类型如jint,jdouble进行计算。4.2 引用类型与字符串操作对于对象如String、数组、自定义类JNI使用不透明的“引用”jobject,jstring,jarray等。你不能直接操作它们的内存必须通过JNIEnv*指针提供的函数来操作。字符串示例// Java public native String manipulateString(String input);// C extern C JNIEXPORT jstring JNICALL Java_com_example_nativedemo_MainActivity_manipulateString( JNIEnv* env, jobject thiz, jstring input) { // 1. 将jstring转换为C风格的字符串UTF-8编码。这是一个必须释放的指针 const char* c_str env-GetStringUTFChars(input, nullptr); if (c_str nullptr) { return nullptr; // 内存不足异常已抛出 } // 2. 对c_str进行操作例如转换为大写 std::string cpp_str(c_str); std::transform(cpp_str.begin(), cpp_str.end(), cpp_str.begin(), ::toupper); // 3. 释放从Java字符串获取的字符数组非常重要否则内存泄漏。 env-ReleaseStringUTFChars(input, c_str); // 4. 将C字符串转换回jstring并返回 return env-NewStringUTF(cpp_str.c_str()); }重要提示GetStringUTFChars和ReleaseStringUTFChars必须成对调用。对于GetTypeArrayElements这类函数也是如此。忘记释放是Native内存泄漏的常见原因。4.3 数组操作数组操作同样需要通过JNIEnv函数。// Java public native int sumArray(int[] arr);// C extern C JNIEXPORT jint JNICALL Java_com_example_nativedemo_MainActivity_sumArray( JNIEnv* env, jobject thiz, jintArray arr) { // 1. 获取数组指针和长度 jint* c_array env-GetIntArrayElements(arr, nullptr); jsize length env-GetArrayLength(arr); // 2. 执行计算 jint sum 0; for (jsize i 0; i length; i) { sum c_array[i]; } // 3. 释放数组元素。第三个参数是模式 // 0 复制回内容并释放原生数组 // JNI_COMMIT 复制回内容但不释放原生数组 // JNI_ABORT 不复制回内容直接释放原生数组 env-ReleaseIntArrayElements(arr, c_array, 0); return sum; }4.4 调用Java方法与访问字段有时需要在C中回调Java方法或修改Java对象的字段。这需要先获取类引用、方法ID或字段ID。// 假设我们想在C中调用一个Java对象的实例方法 extern C JNIEXPORT void JNICALL Java_com_example_nativedemo_MainActivity_callBackToJava( JNIEnv* env, jobject thiz) { // thiz就是MainActivity的实例 // 1. 获取对象的类 jclass clazz env-GetObjectClass(thiz); // 2. 获取方法ID。参数(类引用, 方法名, 方法签名) // 方法签名格式(参数类型)返回类型。例如 ()V 表示无参无返回。 // 可以使用 javap -s -p com.example.nativedemo.MainActivity 命令查看类中所有方法的签名。 jmethodID methodId env-GetMethodID(clazz, showToast, (Ljava/lang/String;)V); if (methodId nullptr) { // 方法没找到可能签名错误或方法不存在 return; } // 3. 创建要传递的字符串参数 jstring msg env-NewStringUTF(Called from C!); // 4. 调用Java实例方法 // 参数(对象实例, 方法ID, 实际参数...) env-CallVoidMethod(thiz, methodId, msg); // 5. 局部引用管理可选但建议clazz和msg是局部引用函数返回后会自动释放。 // 但在长时间运行的Native函数或大量创建引用时应使用env-DeleteLocalRef()主动管理防止局部引用表溢出。 }5. CMakeLists.txt 进阶配置随着项目复杂你的CMakeLists.txt也需要变得更强大。5.1 添加多个源文件与头文件# 添加多个源文件 add_library( native-lib SHARED native-lib.cpp another-lib.cpp utils.cpp ) # 添加头文件搜索目录。这样#include utils.h时CMake就知道去哪里找 include_directories( src/main/cpp/include ) # 或者更现代的方式使用target_include_directories作用域更清晰 target_include_directories( native-lib PRIVATE src/main/cpp/include )5.2 添加预构建库第三方.so或.a假设你有一个预编译好的第三方库libawesome.so和它的头文件awesome.h。# 1. 将第三方库导入为一个IMPORTED库目标 add_library( awesome-lib SHARED IMPORTED ) # 2. 设置这个导入库的路径。${CMAKE_SOURCE_DIR} 是CMakeLists.txt所在目录 set_target_properties( awesome-lib PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libawesome.so ) # 注意按ABI分目录存放 # 3. 指定头文件目录 target_include_directories( native-lib PRIVATE ${CMAKE_SOURCE_DIR}/third_party/include ) # 4. 链接这个第三方库到你的库 target_link_libraries( native-lib awesome-lib ${log-lib} )5.3 条件编译与变量# 根据构建类型Debug/Release设置不同的编译选项 if (${CMAKE_BUILD_TYPE} STREQUAL Debug) target_compile_options( native-lib PRIVATE -O0 -g ) # 关闭优化包含调试信息 else() target_compile_options( native-lib PRIVATE -O3 ) # 开启优化 endif() # 定义宏可以在C代码中使用 #ifdef ENABLE_LOG target_compile_definitions( native-lib PRIVATE -DENABLE_LOG1 )6. 调试Native代码与性能分析6.1 使用LLDB进行Native调试Android Studio集成了LLDB调试器。调试Native代码和调试Java代码类似。在C代码行号左侧点击设置断点红色圆点。以Debug模式运行应用点击虫子图标或ShiftF9。当执行到断点时程序会暂停。你可以在Debug窗口查看变量值、调用栈并可以单步执行Step Over/Into。在Debug窗口的LLDB标签页你还可以直接输入LLDB命令进行更底层的检查。实操心得有时LLDB可能无法正确加载符号显示??。确保你的build.gradle中debug构建类型没有剥离调试符号默认不会。如果问题依旧尝试在CMakeLists.txt的Debug配置中显式添加-g标志。6.2 使用Android Logcat输出日志在C中使用__android_log_print函数输出日志到Logcat比printf更可靠。#include android/log.h // 需要包含这个头文件 // 定义日志标签 #define LOG_TAG NativeDemo // 定义日志宏方便使用 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) extern C JNIEXPORT void JNICALL Java_com_example_nativedemo_MainActivity_logFromNative( JNIEnv* env, jobject thiz) { int value 42; LOGD(This is a debug log from native, value %d, value); LOGE(This is an error log!); }记得在CMakeLists.txt中链接log库 (target_link_libraries(native-lib ${log-lib}))。6.3 简单的性能考量JNI调用开销跨越Java-Native边界是有成本的。避免在紧凑循环中进行大量的、细粒度的JNI调用。应该尽量在一次JNI调用中完成更多工作或者将循环移到Native侧执行。局部引用与全局引用在Native函数中创建的jobject,jstring,jclass等默认都是局部引用函数返回后会被自动释放。如果你需要长时间持有比如保存在一个全局变量中必须使用env-NewGlobalRef()创建全局引用并在不再需要时用env-DeleteGlobalRef()释放否则会导致内存泄漏和引用混乱。异常处理JNI函数调用可能会抛出Java异常如NullPointerException。在调用可能抛出异常的JNI函数后应使用env-ExceptionCheck()或env-ExceptionOccurred()检查异常并妥善处理打印日志、清理资源、返回安全值。Native代码中的异常不会自动传播到Java层。7. 常见问题排查与解决实录在实际开发中你几乎一定会遇到下面这些问题。7.1UnsatisfiedLinkError或java.lang.UnsatisfiedLinkError这是最常见的错误意思是Java运行时找不到对应的Native函数实现。可能原因及排查步骤库名不匹配System.loadLibrary(“mylib”)中的“mylib”必须与CMakeLists.txt中add_library(mylib ...)定义的名称完全一致且不包含lib前缀和.so后缀。函数签名错误JNI函数名必须严格遵守Java_包名_类名_方法名的格式。包名中的点.必须替换为下划线_。使用javah旧或javac -h新命令自动生成头文件可以避免手写错误。# 在项目根目录或app模块目录下执行 javac -h ./jni src/main/java/com/example/nativedemo/MainActivity.java这会生成一个com_example_nativedemo_MainActivity.h头文件里面包含了正确的函数签名。ABI不兼容你的设备CPU架构如arm64-v8a与编译的.so库的ABI不匹配。检查build.gradle中的abiFilters是否包含了目标设备的ABI以及APK中是否包含了对应ABI的库文件。C命名修饰Name Mangling确保你的C函数用extern “C”包裹以防止编译器进行名称修饰。库未成功打包构建完成后检查app/build/intermediates/cmake/debug/obj/或app/build/intermediates/stripped_native_libs/目录下是否有对应ABI的.so文件。也可以解压APK查看lib/目录下是否有.so文件。7.2CMake Error或构建失败CMake版本不匹配检查build.gradle中cmake.version和本地安装的版本以及CMakeLists.txt中cmake_minimum_required指定的版本确保它们兼容。路径错误CMakeLists.txt中add_library指定的源文件路径、IMPORTED_LOCATION指定的第三方库路径是否正确。使用${CMAKE_CURRENT_SOURCE_DIR}来表示当前CMakeLists.txt所在目录是更安全的方式。缺少依赖如果使用了find_library或find_package确保对应的库已正确安装且路径已配置。7.3 运行时崩溃SIGSEGV, SIGABRT这通常是Native代码本身的bug比如空指针解引用、数组越界、内存泄漏、使用已释放的内存等。排查方法查看Logcat崩溃时Android系统会输出详细的墓碑tombstone信息到Logcat包含错误信号、寄存器状态、调用栈回溯backtrace。仔细阅读这些信息它们能精确定位到崩溃的代码行如果调试符号已包含。使用AddressSanitizer (ASan)这是一个强大的内存错误检测工具。在build.gradle中启用它android { defaultConfig { externalNativeBuild { cmake { // 其他标志... arguments “-DANDROID_ARM_MODEarm”, “-DANDROID_STLc_shared” // 启用ASan cppFlags “-fsanitizeaddress -fno-omit-frame-pointer” } } packagingOptions { // 对于使用c_shared STL的情况 doNotStrip ‘**/*.so’ } } }启用后内存错误会被更早、更清晰地检测出来。逐步注释代码如果崩溃点不明确可以尝试逐步注释掉可疑的Native代码块定位问题区域。7.4 中文乱码问题在C中处理Java传过来的中文字符串时有时会出现乱码。原因与解决Java内部使用UTF-16编码字符串。GetStringUTFChars获取的是修改过的UTF-8MUTF-8编码对于大部分字符是兼容标准UTF-8的但处理所有Unicode字符更安全的方式是使用GetStringChars获取UTF-16或GetStringCritical。确保你的C源文件保存的编码与编译环境期望的编码一致通常为UTF-8。在Android Studio中可以通过File-Settings-Editor-File Encodings全局设置为UTF-8。在Logcat中输出时确保Logcat的编码设置正确默认通常是UTF-8。从第一次配置环境时对着满屏英文错误日志发呆到如今能相对从容地搭建JNI桥梁、调试Native崩溃这个过程里最大的体会就是细节决定成败。一个拼写错误的方法名、一个忘记释放的局部引用、一个ABI过滤器的疏忽都可能导致应用崩溃或行为异常。我的建议是建立一个自己的“检查清单”在每次创建新的Native方法或引入第三方库时逐项核对函数签名对了没库加载了吗内存管理妥当吗ABI兼容吗多用自动生成工具如javac -h善用调试工具LLDB, ASan并把关键的操作步骤和配置写成脚本或模板。这样Android的Native开发就不再是令人畏惧的黑魔法而是一个强大且可控的性能利器。

相关新闻

深入解析网站建设的目的和意义,揭秘企业数字化转型的底层逻辑与核心价值

深入解析网站建设的目的和意义,揭秘企业数字化转型的底层逻辑与核心价值

说实话,在这个互联网高度普及的年代,如果你还在问“我们公司到底需不需要做一个官网?”或者“建个网站到底有啥用?我觉得搞搞微信视频号、抖音号就足够了”,那我只能恭喜你,你可能正站在一个即将被淘汰的边缘,或者至少是错过了一个巨大的增量市场。我们聊聊网站建设的目…

2026/8/6 3:20:19 阅读更多 →
从Chromium源码编译到Delphi自动化:完整实战指南

从Chromium源码编译到Delphi自动化:完整实战指南

这次我们来看一个技术圈里流传的“Chromium.org 宣传片”事件。这并非官方发布,而是一个由社区技术爱好者制作的、高度还原 Chromium 项目精髓的技术演示视频。它之所以引发关注,是因为它精准地捕捉了 Chromium 作为现代 Web 基石的核心价值:…

2026/8/6 3:20:19 阅读更多 →
AI批量生成知乎爆款内容:3天搭建自动涨粉系统,实测单月引流5000+精准用户

AI批量生成知乎爆款内容:3天搭建自动涨粉系统,实测单月引流5000+精准用户

更多请点击: https://intelliparadigm.com 第一章:AI做知乎账号 借助大语言模型与自动化工具链,个人或团队可高效运营知乎账号,实现内容生成、互动响应与数据反馈闭环。关键在于将AI能力嵌入选题策划、文案撰写、评论管理及效果分…

2026/8/6 3:20:19 阅读更多 →

最新新闻

Claude Opus 4 生产接入:长文本代码审查的成本与效果博弈

Claude Opus 4 生产接入:长文本代码审查的成本与效果博弈

Claude Opus 4 生产接入:长文本代码审查的成本与效果博弈上周有个需求,风控系统的历史代码库需要接入AI辅助审查。业务方要求对超过5000行的核心交易链路做一次全量分析,找出潜在的死代码和逻辑漏洞。Sonnet 4 之前我们已经跑通了&#xff0c…

2026/8/6 23:34:07 阅读更多 →
终极休息提醒指南:如何用BreakTimer科学管理你的工作健康 [特殊字符]

终极休息提醒指南:如何用BreakTimer科学管理你的工作健康 [特殊字符]

终极休息提醒指南:如何用BreakTimer科学管理你的工作健康 🚀 【免费下载链接】breaktimer-app Manage periodic breaks. Avoid eye-strain and RSI. 项目地址: https://gitcode.com/gh_mirrors/br/breaktimer-app 在数字时代,长时间盯…

2026/8/6 23:34:07 阅读更多 →
【K8S 运维实战】39-etcd脑裂故障复盘

【K8S 运维实战】39-etcd脑裂故障复盘

案例:etcd 脑裂故障复盘一句话定位:3 节点 etcd 集群因机房网络分区导致 2 节点失联,集群瞬间不可写,核心业务全挂 23 分钟——一次典型 Raft 多数派失效故障的完整应急复盘。写在前面 etcd 是 K8s 的"心脏",所有集群状态都存在它里面。平时我们聊 etcd 高可用,大多停…

2026/8/6 23:34:07 阅读更多 →
OpenNews MCP市场异常信号解析:价格波动、资金费率与大额清算一网打尽

OpenNews MCP市场异常信号解析:价格波动、资金费率与大额清算一网打尽

OpenNews MCP市场异常信号解析:价格波动、资金费率与大额清算一网打尽 【免费下载链接】opennews-mcp News Aggregation AI Ratings Trading Signals Real-time Updates 项目地址: https://gitcode.com/gh_mirrors/op/opennews-mcp OpenNews MCP是一款功能…

2026/8/6 23:34:07 阅读更多 →
终极指南:3个步骤快速解决AMD ROCm GPU识别问题与性能优化实战

终极指南:3个步骤快速解决AMD ROCm GPU识别问题与性能优化实战

终极指南:3个步骤快速解决AMD ROCm GPU识别问题与性能优化实战 【免费下载链接】ROCm AMD ROCm™ Software - GitHub Home 项目地址: https://gitcode.com/GitHub_Trending/ro/ROCm AMD ROCm™是一个开源GPU计算软件栈,为深度学习、高性能计算和科…

2026/8/6 23:34:07 阅读更多 →
HTTP、Socket、WebSocket与WebService协议对比与应用指南

HTTP、Socket、WebSocket与WebService协议对比与应用指南

1. 网络通信协议的基本分类与定位在分布式系统和网络编程中,HTTP、Socket、WebSocket和WebService(SOAP)这四种技术扮演着不同角色。要理解它们的区别,首先需要明确它们在网络协议栈中的位置:传输层技术:Socket是操作系统提供的AP…

2026/8/6 23:33:07 阅读更多 →

日新闻

深入解析LimboAI C++内核:架构设计与性能优化实战

深入解析LimboAI C++内核:架构设计与性能优化实战

1. 项目概述:为什么我们需要深入LimboAI的C内核?如果你是一名使用Godot引擎的游戏开发者,尤其是对AI行为逻辑有较高要求的项目,那么LimboAI这个名字你大概率不会陌生。它作为Godot 4生态中一个备受瞩目的行为树与状态机插件&#…

2026/8/6 0:00:06 阅读更多 →
Unity 2D游戏敌人AI系统:基于PlayMaker状态机与2D Toolkit的实战开发

Unity 2D游戏敌人AI系统:基于PlayMaker状态机与2D Toolkit的实战开发

1. 项目概述与核心思路大家好,我是老张,一个在游戏开发一线摸爬滚打了十多年的老码农。今天咱们接着聊《空洞骑士》风格2D动作游戏的Demo制作。上一期我们搭好了基础框架,处理了角色移动和碰撞,这一期,我们要让游戏世界…

2026/8/6 0:00:06 阅读更多 →
被动防火门市场前景发展趋势

被动防火门市场前景发展趋势

被动防火门依靠材质结构、密闭构造阻隔烟火蔓延,无需电控启动,是建筑被动消防系统核心构件,行业依托新规管控、城市更新、工业安全升级迎来稳定扩容,整体朝着合规化、专项化、低碳化、智能化方向发展。现阶段 GB12955‑2024 新版国…

2026/8/6 0:00:06 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/6 22:02:27 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/6 22:02:27 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/6 22:02:27 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/5 23:28:39 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/6 22:02:28 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/5 23:46:51 阅读更多 →