一、本文的目标
本文将rknn_model_zoo中yolov5的C语言例子迁移到Android应用中,让上层应用通过JNI直接调用C接口完成一张图片的目标识别。
本App上层应用将采用Kotlin编程,yolov5 C语言部分使用NDK编译,中间通过JNI调用。主要功能为在界面选择一张图片,用yolov5识别图片中的目标。
自带的android例子直接调用摄像头动态目标识别,二者略有区别。
二、开发环境说明
- 主机系统:Windows 11
- 目标设备:搭载RK3588芯片的安卓开发板
- 核心工具:Android Studio Koala | 2024.1.1 Patch 2,NDK 27.0
三、NDK简介
原生开发套件 (NDK) 是一套Android开发工具,使开发者能够在 Android 应用中使用 C 和 C++ 代码。
在实践本章内容前需要了解NDK工具的使用,请参考我的上一篇文章《2025年的Android NDK 快速开发入门》。
四、JNI简介
JNI(Java Native Interface)技术是Java平台的重要跨语言交互机制,其核心功能如下:
- 双向互操作:实现Java/Kotlin与C/C++代码的双向调用
- 数据类型映射:自动处理基础类型转换(jint↔int),复杂类型需手动处理(jobject↔C结构体)
- 异常传播:本地代码可抛出Java异常,Java层可捕获处理
双向互操作: - Java → Native:通过 native 关键字声明方法,由 JVM 动态链接到本地函数
- Native → Java:通过 JNIEnv 提供的反射式 API 操作 Java 对象
- 数据桥接:所有跨语言数据需通过 JNI 类型系统转换(如 jint → int)
数据桥接:类型转换被定义在jni.h头文件中,该文件可以在NDK目录中找到:$NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/jni.h。
/* Primitive types that match up with Java equivalents. */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
重要的结构体JNINativeInterface:
JNINativeInterface 结构体用于操作Java对象和类。
typedef struct JNINativeInterface {
jint (*GetVersion)(JNIEnv*);
jclass (*FindClass)(JNIEnv*, const char*);
// 包含367个函数指针的完整结构体
} JNINativeInterface;
对象操作
struct JNINativeInterface {
// 创建对象
jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
// 调用方法
void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
// 访问字段
jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
jint (*GetIntField)(JNIEnv*, jobject, jfieldID);
};
数组和字符串处理
// 操作数组
jintArray (*NewIntArray)(JNIEnv*, jsize);
jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
// 操作字符串
jstring (*NewStringUTF)(JNIEnv*, const char*);
const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
五、应用的简单说明
本应用只完成一个功能,就是选择图片后进行yolov5的目标检测并显示出结果,大致流程如图:
六、设计和开发
0. 新建kotlin项目
创建一个空界面的kotlin项目。MainActivity作为入口,再新建包ui.activity和yolo,并在activity包下创建RKNNYolov5Activity。
1.接口设计
本例中准备在native层暴露三个方法,分别是初始化、识别和释放。
kotlin层先创建接口
在yolo包下创建InferenceWrapper类:
package com.linc.rknn.yolo
import android.graphics.Bitmap
class InferenceWrapper {
companion object {
init {
System.loadLibrary("rknn_yolov5")
}
}
external fun init(modelPath: String, labelListPath: String): Boolean
external fun detect(srtBitmap: Bitmap): Boolean
external fun release(): Boolean
}
native层随后创建对应接口
在src->main下新建cpp目录,并新建一个cpp文件叫native-lib.cpp。
对应按规则直接写Cpp对应的方法,如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
extern <