引言:
JNI是Java可以调用高效的c++代码,但是在JNI函数中传递复杂数据类型十分的繁琐,一般情况下需要jobject作为中转,一维数组int[],float[]以及二维数组String[]可以实现方便的参数传入/传出。其他复杂的二、三维数组理论上可以使用jobjectarray实现参数传入/传出,可是实现过程中出现问题,可能是内存上的错误。
下面介绍一种通用方法,在JNI函数中引用Java类,将cxx复杂数据转换成jobjectArray,实现从JNI到Java的数据传输。
Step1:定义Java类接口
这里定义了一个目标检测的通用接口类。创建”OneDetection.java”如下:
package com.example.cuizhou.test01;
import android.graphics.Bitmap;
/**
* Created by cuizhou on 18-1-11.
*/
public class OneDetection {
private String mLabel;
private float mConfidence;
private int mLeft;
private int mTop;
private int mRight;
private int mBottom;
public OneDetection() {
}
/**
* @param label Label name
* @param confidence A confidence factor between 0 and 1. This indicates how certain what has been found is actually the label.
* @param l The X coordinate of the left side of the result
* @param t The Y coordinate of the top of the result
* @param r The X coordinate of the right side of the result
* @param b The Y coordinate of the bottom of the result
*/
public OneDetection(String label, float confidence, int l, int t, int r, int b) {
mLabel = label;
mLeft = l;
mTop = t;
mRight = r;
mBottom = b;
mConfidence = confidence;
}
/**
* @return The X coordinate of the left side of the result
*/
public int getLeft() {
return mLeft;
}
/**
* @return The Y coordinate of the top of the result
*/
public int getTop() {
return mTop;
}
/**
* @return The X coordinate of the right side of the result
*/
public int getRight() {
return mRight;
}
/**
* @return The Y coordinate of the bottom of the result
*/
public int getBottom() {
return mBottom;
}
/**
* @return A confidence factor between 0 and 1. This indicates how certain what has been found is actually the label.
*/
public float getConfidence() {
return mConfidence;
}
/**
* @return The label of the result
*/
public String getLabel() {
return mLabel;
}
public Bitmap getDetBitmap(Bitmap resBitmap){
//Bitmap dstBitmap=Bitmap.createBitmap(10,10,Bitmap.Config.ARGB_8888);
// todo: cropBitmap 不安全 //这里创建了一个10*10的黑色bitmap,后面改成更加合理的方案。
Bitmap dstBitmap = BitmapProcess.cropBitmap(resBitmap, mLeft, mTop,mRight-mLeft,mBottom-mTop);
return dstBitmap;
}
}
Step2:使用FindClass定义对应的c++接口
创建”jniTest.h”如下:
//
// Created by cuizhou on 18-1-15.
//
#ifndef TEST01_JNITEST_H
#define TEST01_JNITEST_H
#include <jni.h>
#include <android/log.h>
#include <string>
#define CLASSNAME_VISION_DET_RET "com/example/cuizhou/test01/OneDetection"
#define CONSTSIG_VISION_DET_RET "()V"
class JNI_VisionDetRet {
public:
JNI_VisionDetRet(JNIEnv* env) {
jclass detRetClass = env->FindClass(CLASSNAME_VISION_DET_RET);
jID_label = env->GetFieldID(detRetClass, "mLabel", "Ljava/lang/String;");
jID_confidence = env->GetFieldID(detRetClass, "mConfidence", "F");
jID_left = env->GetFieldID(detRetClass, "mLeft", "I");
jID_top = env->GetFieldID(detRetClass, "mTop", "I");
jID_right = env->GetFieldID(detRetClass, "mRight", "I");
jID_bottom = env->GetFieldID(detRetClass, "mBottom", "I");
}
void setLabel(JNIEnv* env, jobject& jDetRet, const std::string& label) {
jstring jstr = (jstring)(env->NewStringUTF(label.c_str()));
env->SetObjectField(jDetRet, jID_label, (jobject)jstr);
}
void setRect(JNIEnv* env, jobject& jDetRet, const int& left, const int& top,
const int& right, const int& bottom) {
env->SetIntField(jDetRet, jID_left, left);
env->SetIntField(jDetRet, jID_top, top);
env->SetIntField(jDetRet, jID_right, right);
env->SetIntField(jDetRet, jID_bottom, bottom);
}
static jobject createJObject(JNIEnv* env) {
jclass detRetClass = env->FindClass(CLASSNAME_VISION_DET_RET);
jmethodID mid =
env->GetMethodID(detRetClass, "<init>", CONSTSIG_VISION_DET_RET);
return env->NewObject(detRetClass, mid);
}
static jobjectArray createJObjectArray(JNIEnv* env, const int& size) {
jclass detRetClass = env->FindClass(CLASSNAME_VISION_DET_RET);
return (jobjectArray)env->NewObjectArray(size, detRetClass, NULL);
}
private:
jfieldID jID_label;
jfieldID jID_confidence;
jfieldID jID_left;
jfieldID jID_top;
jfieldID jID_right;
jfieldID jID_bottom;
};
#endif //TEST01_JNITEST_H
Step3: 初始化C++接口类JNI_VisionDetRet
创建”jni_utils.cpp”,首先定义了一个
JNI_VisionDetRet* g_pJNI_VisionDetRet;
(作用未知)然后使用
JNI_OnLoad
方法初始化JNI_VisionDetRet
,后面在c++函数中就可以调用JNI_VisionDetRet
中的方法。
#include <string>
#include "jniTest.h"
#include <sstream>
#include <unistd.h>
// Java Integer/Float
JNI_VisionDetRet* g_pJNI_VisionDetRet;
JavaVM* g_javaVM = NULL;
//初始化类g_pJNI_VisionDetRet
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
g_javaVM = vm;
JNIEnv* env;
vm->GetEnv((void**)&env, JNI_VERSION_1_6);
g_pJNI_VisionDetRet = new JNI_VisionDetRet(env);
return JNI_VERSION_1_6;
}
void JNI_OnUnload(JavaVM* vm, void* reserved) {
g_javaVM = NULL;
delete g_pJNI_VisionDetRet;
}
Step4:在JNI函数中通过接口类将c++数据转化成jobjectArray,实现数据传输。
创建native-lib.cpp
#include <jni.h>
#include <string>
#include "../jni/jniCommon/jniTest.h"
extern JNI_VisionDetRet* g_pJNI_VisionDetRet; //extern 作用未知
extern "C"
JNIEXPORT jobjectArray
JNICALL
Java_com_example_cuizhou_test01_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
jobjectArray jDetRetArray = JNI_VisionDetRet::createJObjectArray(env, 4);
for(int ind=0;ind<4;ind++){
jobject jDetRet = JNI_VisionDetRet::createJObject(env);
env->SetObjectArrayElement(jDetRetArray, ind, jDetRet);//在这里吧jDetRet放入了jDetRetArray
g_pJNI_VisionDetRet->setRect(env, jDetRet, 11,12,
123, 345);//向jDetRet添加rect
g_pJNI_VisionDetRet->setLabel(env, jDetRet, "car");//向jDetRet添加label
}
return jDetRetArray;
}
相应的Java中的JNI函数:
public native OneDetection[] getDetection();
Step5: CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
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).
src/main/cpp/native-lib.cpp
src/main/jni/jniCommon/jni_utils.cpp)
find_library( log-lib
log )
target_link_libraries( native-lib
${log-lib} )
代码可见我的github
项目地址:https://github.com/Steven9402/