Android JNI开发环境搭建与实战指南
1. Android JNI开发环境搭建1.1 NDK工具包安装在Android Studio中安装NDK(Native Development Kit)是进行JNI开发的第一步。打开Android Studio后依次点击File Settings Appearance Behavior System Settings Android SDK切换到SDK Tools选项卡。勾选NDK(Side by side)和CMake选项进行安装。建议选择较新的稳定版本比如当前最新的NDK 25.x系列。安装完成后NDK默认会存放在Android SDK目录下的ndk文件夹中路径通常是~/Android/Sdk/ndk/[version]1.2 CMake配置CMake是Android官方推荐的Native代码构建工具。在项目的build.gradle文件中需要添加CMake配置android { defaultConfig { externalNativeBuild { cmake { cppFlags -stdc17 arguments -DANDROID_STLc_shared } } } externalNativeBuild { cmake { path src/main/cpp/CMakeLists.txt version 3.22.1 } } }注意如果项目中使用的是较新版本的NDK建议使用c_shared而非c_static作为STL这样可以减少最终APK的体积。2. 创建第一个JNI项目2.1 Java层Native方法声明首先在Java类中声明native方法public class NativeLib { static { System.loadLibrary(native-lib); } public native String stringFromJNI(); }关键点说明System.loadLibrary用于加载动态库参数是库名(去掉lib前缀和.so后缀)native关键字表明这是一个JNI方法方法命名遵循Java规范但实现将在C中完成2.2 C头文件生成使用javac和javah工具生成对应的C头文件javac -h ./jni NativeLib.java这会生成一个名为com_example_NativeLib.h的头文件内容类似/* DO NOT EDIT THIS FILE - it is machine generated */ #include jni.h /* Header for class com_example_NativeLib */ #ifndef _Included_com_example_NativeLib #define _Included_com_example_NativeLib #ifdef __cplusplus extern C { #endif /* * Class: com_example_NativeLib * Method: stringFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_NativeLib_stringFromJNI (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif2.3 C实现文件创建native-lib.cpp实现头文件中声明的方法#include jni.h #include string #include com_example_NativeLib.h extern C JNIEXPORT jstring JNICALL Java_com_example_NativeLib_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello Hello from C; return env-NewStringUTF(hello.c_str()); }代码解析JNIEnv指针提供了访问JVM功能的接口jobject参数代表调用该方法的Java对象实例NewStringUTF用于将C字符串转换为Java字符串3. CMake构建配置3.1 CMakeLists.txt编写在cpp目录下创建CMakeLists.txt文件cmake_minimum_required(VERSION 3.4.1) add_library( # 设置库名称 native-lib # 设置库类型 SHARED # 提供源文件路径 native-lib.cpp ) find_library( # 设置路径变量名称 log-lib # 指定NDK库名称 log ) target_link_libraries( # 指定目标库 native-lib # 链接日志库到目标库 ${log-lib} )3.2 关键配置解析add_library定义要构建的库SHARED表示生成动态库(.so)find_library定位NDK提供的预构建库target_link_libraries指定库依赖关系对于更复杂的项目还可以添加# 添加头文件搜索路径 include_directories(${CMAKE_SOURCE_DIR}/include) # 添加编译定义 add_definitions(-DMY_DEFINE1) # 设置C标准 set(CMAKE_CXX_STANDARD 17)4. 构建与调试技巧4.1 多ABI支持在build.gradle中配置支持的CPU架构android { defaultConfig { ndk { abiFilters armeabi-v7a, arm64-v8a, x86, x86_64 } } }不同ABI的区别armeabi-v7a32位ARM处理器arm64-v8a64位ARM处理器x86Intel处理器x86_6464位Intel处理器4.2 调试Native代码在AndroidManifest.xml中启用可调试application android:debuggabletrue在C代码中添加断点选择Debug运行配置使用LLDB进行Native调试调试技巧使用__android_log_print输出日志在Android Studio的LLDB控制台检查变量使用ndk-stack分析Native崩溃5. 常见问题解决5.1 UnsatisfiedLinkError可能原因及解决方案库名不匹配检查System.loadLibrary参数与CMake中的库名ABI不兼容确保设备CPU架构在abiFilters列表中方法签名错误使用javap -s验证方法签名5.2 JNI引用管理内存泄漏常见场景未释放局部引用全局引用未正确管理最佳实践// 创建局部引用 jstring localStr env-NewStringUTF(test); // 必要时提升为全局引用 jstring globalStr (jstring)env-NewGlobalRef(localStr); // 使用后释放 env-DeleteLocalRef(localStr); env-DeleteGlobalRef(globalStr);5.3 性能优化建议减少JNI调用次数批量处理数据使用Direct Buffer处理大量数据缓存方法ID和字段ID使用Critical Native方法避免GC干扰// 缓存字段和方法ID jclass clazz env-FindClass(com/example/NativeLib); jfieldID fieldId env-GetFieldID(clazz, field, I); jmethodID methodId env-GetMethodID(clazz, method, ()V);6. 进阶开发技巧6.1 异常处理JNI中的异常处理模式// 调用Java方法 env-CallVoidMethod(obj, methodID); // 检查是否发生异常 if (env-ExceptionCheck()) { env-ExceptionDescribe(); // 打印异常信息 env-ExceptionClear(); // 清除异常 // 处理异常情况 }6.2 线程管理在非JVM创建的线程中使用JNIJavaVM* g_vm; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { g_vm vm; return JNI_VERSION_1_6; } void* thread_func(void* arg) { JNIEnv* env; // 附加当前线程到JVM g_vm-AttachCurrentThread(env, NULL); // 在这里可以安全调用JNI方法 // 分离线程 g_vm-DetachCurrentThread(); return NULL; }6.3 使用现代C特性在CMake中启用C17支持set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)可以使用智能指针管理资源#include memory std::unique_ptrjstring[] strings(new jstring[count]);7. 项目结构与构建优化7.1 推荐的项目结构app/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ ├── cpp/ │ │ │ ├── CMakeLists.txt │ │ │ ├── common/ # 公共代码 │ │ │ ├── lib1/ # 模块1 │ │ │ ├── lib2/ # 模块2 │ │ │ └── third_party/ # 第三方代码7.2 预编译库的使用对于已有的.so库可以在CMake中这样引入add_library(prebuilt-lib SHARED IMPORTED) set_target_properties(prebuilt-lib PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libprebuilt.so) target_link_libraries(native-lib prebuilt-lib)7.3 构建速度优化在gradle.properties中添加android.defaultConfig.externalNativeBuild.cmake.arguments-DCMAKE_BUILD_TYPERelease使用ccache加速编译# 在CMakeLists.txt开头添加 find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif()启用并行编译android { externalNativeBuild { cmake { arguments -DANDROID_TOOLCHAINclang, -DANDROID_STLc_shared, -j${Runtime.runtime.availableProcessors()} } } }8. 实际开发中的经验分享8.1 类型转换技巧Java与C类型转换常见场景// Java String转C string std::string jstring2string(JNIEnv* env, jstring jStr) { const char* cStr env-GetStringUTFChars(jStr, nullptr); std::string str(cStr); env-ReleaseStringUTFChars(jStr, cStr); return str; } // C string转Java String jstring string2jstring(JNIEnv* env, const std::string str) { return env-NewStringUTF(str.c_str()); } // Java数组处理 jintArray javaArray ...; jint* nativeArray env-GetIntArrayElements(javaArray, nullptr); jsize length env-GetArrayLength(javaArray); // 处理数组... env-ReleaseIntArrayElements(javaArray, nativeArray, 0);8.2 跨平台开发考虑使用预处理器处理平台差异#if defined(__ANDROID__) // Android特定代码 #include android/log.h #define LOG_TAG NativeLib #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #else // 其他平台 #define LOGD(...) printf(__VA_ARGS__) #endif8.3 性能分析工具SimpleperfAndroid官方的Native性能分析工具# 记录性能数据 adb shell simpleperf record -p pid -o /data/local/tmp/perf.data # 拉取分析 adb pull /data/local/tmp/perf.data ./simpleperf report -i perf.dataSystrace分析系统级性能python $ANDROID_HOME/platform-tools/systrace/systrace.py -o trace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input resNDK的perfetto工具更现代的追踪方案9. 安全最佳实践9.1 输入验证所有从Java层传入的参数都需要验证JNIEXPORT void JNICALL Java_com_example_NativeLib_processData (JNIEnv* env, jobject obj, jbyteArray data) { if (data nullptr) { env-ThrowNew(env-FindClass(java/lang/NullPointerException), Input data cannot be null); return; } jsize length env-GetArrayLength(data); if (length MAX_ALLOWED_SIZE) { env-ThrowNew(env-FindClass(java/lang/IllegalArgumentException), Input data too large); return; } // 安全处理数据... }9.2 内存安全使用现代C特性避免内存问题// 使用智能指针 auto buffer std::make_uniquechar[](size); // 使用vector代替原始数组 std::vectorjint safeArray(length); // 使用string代替char* std::string safeString;9.3 符号隐藏在CMake中隐藏不必要的符号# 在CMakeLists.txt中添加 set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)在源代码中使用#define JNI_EXPORT __attribute__ ((visibility (default))) JNI_EXPORT jstring JNICALL Java_com_example_NativeLib_getString(JNIEnv* env, jobject obj);10. 测试与持续集成10.1 Native单元测试使用Google Test框架在CMakeLists.txt中添加enable_testing() add_executable(native-tests test/test1.cpp test/test2.cpp) target_link_libraries(native-tests gtest gtest_main native-lib) add_test(NAME native-tests COMMAND native-tests)编写测试用例#include gtest/gtest.h #include native-lib.h TEST(StringTest, BasicTest) { JNIEnv* env ...; // 获取JNIEnv EXPECT_STREQ(Hello, getTestString(env)); }10.2 自动化构建在CI脚本中添加NDK构建步骤#!/bin/bash # 安装NDK sdkmanager ndk;25.1.8937393 # 构建项目 ./gradlew assembleDebug # 运行测试 ./gradlew connectedCheck10.3 代码质量检查使用clang-tidy进行静态分析# 在CMakeLists.txt中添加 set(CMAKE_CXX_CLANG_TIDY clang-tidy;-checks*)常用检查选项-checksclang-analyzer-*-checksperformance-*-checksmodernize-*11. 发布与兼容性考虑11.1 ABI过滤策略根据项目需求选择支持的ABIandroid { defaultConfig { ndk { // 只支持64位架构以减小APK体积 abiFilters arm64-v8a, x86_64 } } }11.2 API级别兼容在CMake中指定最低API级别# 在CMakeLists.txt中设置 set(ANDROID_PLATFORM android-21)或者在gradle中配置android { defaultConfig { minSdkVersion 21 } }11.3 版本管理策略在CMake中定义版本号project(native-lib VERSION 1.0.0 LANGUAGES CXX)在Java代码中获取Native库版本public native String getNativeVersion();实现对应的JNI方法JNIEXPORT jstring JNICALL Java_com_example_NativeLib_getNativeVersion (JNIEnv* env, jobject obj) { return env-NewStringUTF(1.0.0); }12. 高级主题探索12.1 使用RenderScript替代方案在NDK中使用计算密集型任务替代方案#include arm_neon.h // 使用NEON intrinsics进行SIMD计算 void neon_processing(float* data, int length) { for (int i 0; i length; i 4) { float32x4_t vec vld1q_f32(data[i]); vec vmulq_f32(vec, vec); // 平方计算 vst1q_f32(data[i], vec); } }12.2 Vulkan图形编程在CMake中启用Vulkan支持find_package(Vulkan REQUIRED) target_link_libraries(native-lib Vulkan::Vulkan)12.3 机器学习支持使用NDK的ML库#include nnapi.h ANeuralNetworksModel* model nullptr; ANeuralNetworksModel_create(model); // 配置模型... ANeuralNetworksCompilation* compilation nullptr; ANeuralNetworksCompilation_create(model, compilation); // 编译和执行模型...13. 性能关键代码优化13.1 内联汇编使用在性能关键路径使用ARM汇编void fast_memcpy(void* dest, const void* src, size_t n) { asm volatile ( mov r3, %0\n 1:\n ldmia r1!, {r4-r11}\n stmia r0!, {r4-r11}\n subs r3, r3, #32\n bge 1b\n : : r(n / 32), r(src), r(dest) : r3, r4, r5, r6, r7, r8, r9, r10, r11, memory ); }13.2 缓存优化优化数据访问模式// 不好的访问模式 for (int i 0; i WIDTH; i) { for (int j 0; j HEIGHT; j) { data[j * WIDTH i] process(i, j); } } // 优化后的访问模式 for (int j 0; j HEIGHT; j) { for (int i 0; i WIDTH; i) { data[j * WIDTH i] process(i, j); } }13.3 避免JNI开销对于频繁调用的简单方法使用Fast Native或Critical Nativepublic class NativeLib { static { System.loadLibrary(native-lib); } // Fast Native方法 public static native int fastMethod(); // Critical Native方法 public static native void criticalMethod(byte[] data); }在C实现中// Fast Native实现 JNIEXPORT jint JNICALL Java_com_example_NativeLib_fastMethod(JNIEnv*, jclass) { return 42; } // Critical Native实现 JNIEXPORT void JNICALL Java_com_example_NativeLib_criticalMethod(JNIEnv* env, jclass, jbyteArray arr) { jbyte* data env-GetPrimitiveArrayCritical(arr, nullptr); if (data ! nullptr) { // 直接操作数组数据 process_data(data, env-GetArrayLength(arr)); env-ReleasePrimitiveArrayCritical(arr, data, 0); } }14. 跨语言交互模式14.1 回调Java方法从Native代码调用Java方法// 获取类和方法ID jclass clazz env-FindClass(com/example/NativeLib); jmethodID callback env-GetStaticMethodID(clazz, callback, (I)V); // 调用静态方法 env-CallStaticVoidMethod(clazz, callback, 123); // 调用实例方法 jmethodID instanceCallback env-GetMethodID(clazz, instanceCallback, (Ljava/lang/String;)V); env-CallVoidMethod(obj, instanceCallback, env-NewStringUTF(test));14.2 使用JNI_OnLoad注册方法在库加载时注册Native方法JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if (vm-GetEnv(reinterpret_castvoid**(env), JNI_VERSION_1_6) ! JNI_OK) { return JNI_ERR; } jclass clazz env-FindClass(com/example/NativeLib); if (clazz nullptr) { return JNI_ERR; } static const JNINativeMethod methods[] { {stringFromJNI, ()Ljava/lang/String;, reinterpret_castvoid*(stringFromJNI)}, {nativeMethod, (I)V, reinterpret_castvoid*(nativeMethod)} }; if (env-RegisterNatives(clazz, methods, sizeof(methods)/sizeof(JNINativeMethod)) 0) { return JNI_ERR; } return JNI_VERSION_1_6; }14.3 使用Java反射在Native代码中使用Java反射jclass clazz env-FindClass(java/lang/reflect/Method); jmethodID invoke env-GetMethodID(clazz, invoke, (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;); // 获取目标方法 jclass targetClass env-FindClass(com/example/SomeClass); jmethodID targetMethod env-GetMethodID(targetClass, someMethod, (I)V); // 创建参数数组 jobjectArray args env-NewObjectArray(1, env-FindClass(java/lang/Object), nullptr); env-SetObjectArrayElement(args, 0, env-NewObject(env-FindClass(java/lang/Integer), env-GetMethodID(env-FindClass(java/lang/Integer), init, (I)V), 123)); // 调用方法 env-CallObjectMethod(targetMethod, invoke, nullptr, args);15. 工具链与生态系统15.1 使用Clang工具链在CMake中指定Clang工具链set(CMAKE_C_COMPILER clang) set(CMAKE_CXX_COMPILER clang)启用Clang的高级特性add_compile_options( -Wall -Wextra -Werror -fcolor-diagnostics -fstack-protector-strong )15.2 使用NDK的预构建库链接NDK提供的库find_library(android-lib android) find_library(log-lib log) target_link_libraries(native-lib ${android-lib} ${log-lib})15.3 使用第三方构建系统对于使用autotools或makefile的项目# 在CMake中调用外部构建命令 add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/external/libexternal.a COMMAND make -C ${CMAKE_CURRENT_SOURCE_DIR}/external WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external ) add_library(external STATIC IMPORTED) set_property(TARGET external PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/external/libexternal.a) target_link_libraries(native-lib external)16. 调试与性能分析进阶16.1 使用AddressSanitizer在CMake中启用ASan# 在CMakeLists.txt中添加 set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -fsanitizeaddress -fno-omit-frame-pointer) set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -fsanitizeaddress -fno-omit-frame-pointer) set(CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} -fsanitizeaddress)在gradle中配置android { defaultConfig { externalNativeBuild { cmake { arguments -DANDROID_ARM_MODEarm, -DANDROID_STLc_shared, -DANDROID_TOOLCHAINclang, -DANDROID_USE_LEGACY_TOOLCHAIN_FILEON } } } }16.2 使用HWASan检测内存错误HWASan(Hardware-assisted AddressSanitizer)是更高效的检测工具# 对于arm64-v8a架构 set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -fsanitizehwaddress -fno-omit-frame-pointer) set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -fsanitizehwaddress -fno-omit-frame-pointer)16.3 使用perfetto进行系统追踪集成perfetto SDKfind_package(perfetto REQUIRED) target_link_libraries(native-lib perfetto::perfetto)在代码中使用#include perfetto.h PERFETTO_TRACE_EVENT_BEGIN(category, EventName); // 要追踪的代码... PERFETTO_TRACE_EVENT_END(category);17. 兼容性与未来展望17.1 64位兼容性要求从Android 5.0开始64位支持成为必须在gradle中配置android { defaultConfig { ndk { abiFilters arm64-v8a, x86_64 } } }检查指针大小static_assert(sizeof(void*) 8, Requires 64-bit architecture);17.2 支持新硬件特性检测和利用新CPU特性#include cpu-features.h void use_neon_if_available() { if (android_getCpuFamily() ANDROID_CPU_FAMILY_ARM (android_getCpuFeatures() ANDROID_CPU_ARM_FEATURE_NEON) ! 0) { // 使用NEON指令集 } else { // 回退方案 } }17.3 模块化Native开发使用Android动态功能模块// 在动态模块的build.gradle中 android { dynamicFeatures [:native_feature] }在CMake中构建为独立库add_library(native_feature SHARED feature.cpp)18. 实际项目经验总结18.1 JNI开发的最佳实践命名规范Java包名转换为C命名空间使用一致的命名风格如驼峰式为JNI函数添加详细注释错误处理检查所有JNI调用的返回值合理处理异常添加详细的日志输出资源管理使用RAII模式管理资源确保释放所有本地引用避免全局引用泄漏18.2 性能调优经验减少跨语言调用批量处理数据而非单条处理使用Direct Buffer减少拷贝考虑临界区优化内存优化重用内存缓冲区避免不必要的对象创建使用内存池技术多线程优化合理使用线程局部存储减少锁竞争使用无锁数据结构18.3 团队协作建议代码组织清晰的目录结构模块化设计统一的编码风格文档规范详细的接口文档架构设计文档变更记录构建系统统一的构建配置自动化构建流程版本控制集成19. 扩展学习资源19.1 官方文档推荐Android NDK官方文档NDK指南JNI规范CMake官方文档CMake教程现代CMake实践19.2 开源项目参考优秀开源项目FFmpegSkiaRealm学习项目Android NDK SamplesJNI by Example19.3 调试工具推荐调试工具LLDBAddressSanitizerSimpleperf性能分析PerfettoSystraceAndroid Profiler静态分析clang-tidycppcheckSonarQube20. 结语与个人建议在长期从事Android JNI开发的过程中我总结了以下几点个人体会渐进式学习从简单的Hello World开始逐步深入理解JNI的各个概念不要试图一次性掌握所有内容。重视基础扎实的C/C基础比掌握各种框架更重要特别是内存管理和指针操作。工具链熟悉花时间熟悉NDK工具链编译器、调试器、分析工具能极大提高开发效率。性能意识从一开始就考虑性能问题避免后期大规模重构。安全第一Native代码的安全问题可能导致严重后果要特别重视输入验证和内存安全。持续更新Android NDK生态发展迅速要保持学习新技术和新工具的习惯。社区参与遇到问题时积极参与开源社区讨论往往能获得意想不到的解决方案。文档习惯详细记录开发过程中的关键决策和解决方案这对团队协作和后期维护非常重要。最后建议初学者从实际项目需求出发边学边做在实践中不断积累经验。JNI开发虽然有一定门槛但掌握后能够极大扩展Android应用的能力边界值得投入时间深入学习。

相关新闻

深入解析TI McSPI三大性能加速器:Turbo模式、FIFO与中断机制

深入解析TI McSPI三大性能加速器:Turbo模式、FIFO与中断机制

1. 项目概述与核心价值在嵌入式开发领域,SPI(Serial Peripheral Interface)几乎是工程师们最熟悉的“老朋友”之一。它简单、高效,是连接传感器、存储器、显示屏等外设的骨干总线。然而,当项目从简单的点对点通信&…

2026/9/24 14:22:22 阅读更多 →
AI Skill生态安全防护:从代码扫描到运行时监控

AI Skill生态安全防护:从代码扫描到运行时监控

1. Skill生态爆发背后的安全隐忧最近GitHub上"同事.skill"项目5天斩获6600星标的现象,标志着AI Agent领域正在经历一场技能包(Skill)的爆发式增长。从"前任.skill"到"老板.skill",各种脑洞大开的技…

2026/9/23 21:12:27 阅读更多 →
Python常用模块实战指南:提升开发效率的关键技巧

Python常用模块实战指南:提升开发效率的关键技巧

1. Python常用模块学习的重要性作为一名从2010年开始使用Python的老程序员,我见证了Python生态系统的蓬勃发展。Python之所以能成为当今最受欢迎的编程语言之一,其丰富的内置模块功不可没。这些模块就像瑞士军刀上的各种工具,让你无需重复造轮…

2026/9/21 21:06:11 阅读更多 →

最新新闻

Word内容粘贴到富文本编辑器样式丢失?一套清洗管线方案彻底解决

Word内容粘贴到富文本编辑器样式丢失?一套清洗管线方案彻底解决

1. 问题根源:为什么Word内容一进浏览器就“变脸”做前端的人,尤其是跟CMS后台、富文本编辑器、协同文档打过交道的,基本都遇到过一个让人抓狂的场景:客户或者运营同事在Word里排版排得漂漂亮亮,标题带色、段落缩进、表…

2026/9/24 20:30:47 阅读更多 →
JavaWeb仓库管理系统:Layui+Layer+Laydate实战

JavaWeb仓库管理系统:Layui+Layer+Laydate实战

简介:这是一套面向JavaWeb初学者与信息系统课程设计者的完整仓库管理系统实战项目,聚焦企业库存管理核心场景,融合传统Web开发与基础AI应用理念。资源包含13个功能模块的可运行代码及配套文档,覆盖登录注册、商品/库存/出入库/订单…

2026/9/24 20:30:47 阅读更多 →
Python电影数据集探索实战:从清洗到可视化全流程

Python电影数据集探索实战:从清洗到可视化全流程

简介:一份基于Python的电影数据分析项目包,面向正在完成课程设计、期末大作业或毕设的计算机、人工智能、大数据、数学、电子信息等相关专业学生,也适合刚入门数据分析的开发者参考。包内共3个文件:一个Python脚本用于完整执行分析…

2026/9/24 20:30:47 阅读更多 →
V100 16GB跑通Qwen 27B:推理速度从4.2到63.8 tok/s的优化实录

V100 16GB跑通Qwen 27B:推理速度从4.2到63.8 tok/s的优化实录

几个月前,我从仓库角落翻出一台配着 Tesla V100 16GB 的老服务器时,同事的第一反应是:这卡还能跑 Qwen 27B 大模型?说实话我自己也没底。Qwen 27B 的 FP16 权重就有 54GB,一块 16GB 显存的 V100 连一半都装不进去&…

2026/9/24 20:30:47 阅读更多 →
5分钟打造你的专属AI助手:Strands Agents零基础入门指南

5分钟打造你的专属AI助手:Strands Agents零基础入门指南

5分钟打造你的专属AI助手:Strands Agents零基础入门指南 【免费下载链接】harness-sdk Build an agent harness and control it end-to-end. Open-source SDK for production AI agents in Python & TypeScript - any model, any cloud. 项目地址: https://gi…

2026/9/24 20:30:47 阅读更多 →
离线知识服务器搭建实战:Kiwix+Ollama实现断网AI问答

离线知识服务器搭建实战:Kiwix+Ollama实现断网AI问答

说实话,这个项目是我被网络逼出来的。去年去一个偏远项目现场,网络差到连搜索都打不开,临时要查一个设备说明,翻遍手机缓存也没找到,最后只能打电话回去让人查了再念给我听。那种憋屈感让我下了一个决心——搞一台完全…

2026/9/24 20:29:46 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →