2、开发环境搭建与第一个NNAPI应用
说实话,每次开始一个新项目,最磨人的往往不是算法本身,而是环境搭建。我记得刚接触NNAPI那会儿,光是NDK和CMake的配置就折腾了大半天。今天咱们就把这些基础工作一次性搞定,然后跑起第一个模型——一个简单的加法模型。
本章核心目标:
- 配置Android Studio的NDK与CMake环境
- 创建第一个支持NNAPI的Android项目
- 加载并运行一个简单的加法模型
2.1 配置NDK与CMake
Android Studio从3.2版本开始,对NDK的支持已经很完善了。我个人习惯用SDK Manager来安装,这样版本不容易搞混。
安装步骤:
- 打开Android Studio,进入
SDK Manager → SDK Tools - 勾选
NDK (Side by side)和CMake - 点击Apply,等待下载完成
💡 我的小建议:NDK版本建议选21.x以上,对NNAPI的支持更稳定。我之前用NDK 18踩过坑,有些API不支持,折腾了半天才发现是版本问题。
安装完成后,在项目的 local.properties 文件中,会自动生成NDK路径。你也可以手动指定:
ndk.dir=/Users/你的用户名/Library/Android/sdk/ndk/21.4.7075529
cmake.dir=/Users/你的用户名/Library/Android/sdk/cmake/3.18.1
2.2 创建第一个NNAPI项目
新建项目时,记得勾选 Include C++ support。这个选项在Android Studio的向导里,别漏了。
项目创建后,你会看到 app/src/main/cpp 目录,里面有个 CMakeLists.txt 文件。嗯,这里就是咱们的主战场。
CMakeLists.txt 配置示例:
cmake_minimum_required(VERSION 3.18.1)
project("nnapi_demo")
add_library(
nnapi_demo
SHARED
src/main/cpp/nnapi_bridge.cpp
)
find_library(
log-lib
log
)
target_link_libraries(
nnapi_demo
${log-lib}
android
-lneuralnetworks
)
⚠️ 注意:链接 -lneuralnetworks 这一步很关键。我刚开始做的时候忘了加,结果编译通过但运行时一直报 UnsatisfiedLinkError,查了半天才发现是链接库没加。
2.3 编写Native代码:加载加法模型
咱们先做一个最简单的加法模型。说白了,就是输入两个数,输出它们的和。虽然简单,但NNAPI的完整流程一个不少。
nnapi_bridge.cpp 核心代码:
#include <jni.h>
#include <android/neuralnetworks.h>
#include <android/log.h>
#define LOG_TAG "NNAPI_Demo"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
extern "C" JNIEXPORT jfloat JNICALL
Java_com_example_nnapidemo_MainActivity_addWithNNAPI(
JNIEnv* env, jobject thiz, jfloat a, jfloat b) {
// 1. 创建ANeuralNetworksMemory
ANeuralNetworksMemory* memory = nullptr;
float input_data[2] = {a, b};
float output_data[1] = {0};
// 2. 创建模型
ANeuralNetworksModel* model = nullptr;
ANeuralNetworksModel_create(&model);
// 3. 定义操作数类型(两个float输入,一个float输出)
ANeuralNetworksOperandType float32Type;
float32Type.type = ANEURALNETWORKS_TENSOR_FLOAT32;
float32Type.scale = 0.0f;
float32Type.zeroPoint = 0;
uint32_t input_dimensions[1] = {1};
float32Type.dimensions = input_dimensions;
float32Type.dimensionCount = 1;
// 4. 添加操作数
int32_t input1_idx, input2_idx, output_idx;
ANeuralNetworksModel_addOperand(model, &float32Type);
ANeuralNetworksModel_addOperand(model, &float32Type);
ANeuralNetworksModel_addOperand(model, &float32Type);
// 5. 添加加法操作(ANEURALNETWORKS_ADD)
ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3,
new uint32_t[3]{0, 1, 2}, 1, new uint32_t[1]{2});
// 6. 标识输入输出
ANeuralNetworksModel_identifyInputsAndOutputs(model, 2,
new uint32_t[2]{0, 1}, 1, new uint32_t[1]{2});
// 7. 编译模型
ANeuralNetworksCompilation* compilation = nullptr;
ANeuralNetworksCompilation_create(model, &compilation);
ANeuralNetworksCompilation_finish(compilation);
// 8. 创建执行实例
ANeuralNetworksExecution* execution = nullptr;
ANeuralNetworksExecution_create(compilation, &execution);
// 9. 设置输入数据
ANeuralNetworksExecution_setInput(execution, 0, nullptr, &float32Type,
input_data, sizeof(float));
ANeuralNetworksExecution_setInput(execution, 1, nullptr, &float32Type,
input_data + 1, sizeof(float));
// 10. 设置输出数据
ANeuralNetworksExecution_setOutput(execution, 0, nullptr, &float32Type,
output_data, sizeof(float));
// 11. 执行推理
ANeuralNetworksExecution_startCompute(execution, nullptr);
// 12. 释放资源
ANeuralNetworksExecution_free(execution);
ANeuralNetworksCompilation_free(compilation);
ANeuralNetworksModel_free(model);
return output_data[0];
}
🔑 关键点解析:
ANEURALNETWORKS_ADD是NNAPI内置的加法操作,不需要训练模型- 操作数索引从0开始,输入0、输入1、输出2
- 执行推理时,第二个参数传
nullptr表示同步执行
2.4 Java层调用
在 MainActivity.java 中,加载Native库并调用:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("nnapi_demo");
}
private native float addWithNNAPI(float a, float b);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
float result = addWithNNAPI(3.5f, 2.7f);
Log.d("NNAPI_Demo", "3.5 + 2.7 = " + result);
// 输出:3.5 + 2.7 = 6.2
}
}
💡 避坑指南:我曾经在真机上测试时,发现某些老款手机不支持NNAPI。建议在运行前检查设备兼容性:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
// 可以安全使用NNAPI
} else {
// 回退到CPU计算
}
2.5 运行与调试
连接真机或启动模拟器(API 28+),点击Run。你会在Logcat中看到输出结果。
常见问题排查:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| UnsatisfiedLinkError | Native库未正确加载 | 检查CMakeLists.txt中的库名和Java中的loadLibrary是否一致 |
| ANEURALNETWORKS_BAD_DATA | 操作数类型或维度不匹配 | 检查float32Type的dimensionCount和dimensions设置 |
| ANEURALNETWORKS_OP_FAILED | 设备不支持该操作 | 使用ANEURALNETWORKS_PREFER_LOW_POWER或回退到CPU |
说实话,第一次跑通NNAPI的时候,虽然只是个加法,但那种成就感还是很实在的。你想想看,从底层硬件加速到上层应用,整个链路都打通了。后面咱们就可以在这个基础上,加载真正的AI模型了。
📌 本章小结:
- NDK和CMake是NNAPI开发的基础,版本要匹配
- NNAPI的API流程:创建模型 → 添加操作数 → 添加操作 → 编译 → 执行
- 加法模型虽然简单,但涵盖了NNAPI的完整生命周期
- 真机调试时注意API版本和硬件兼容性
公众号:蓝海资料掘金营,微信deep3321