1. 程式人生 > >android開發 在JNI函式中引用Java類,將cxx複雜資料轉換成jobjectArray,實現從JNI到Java的資料傳輸

android開發 在JNI函式中引用Java類,將cxx複雜資料轉換成jobjectArray,實現從JNI到Java的資料傳輸

引言:

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} )