JNI包裝c++類

section 1

  1. 定義c++類和方法
    singleton PluginLoader @ namespace Vamp::HostExt
    Plugin @ namespace Vamp
    方案一:map一個java的class到c++的PluginLoader,返回類型太複雜,廢棄
class PluginLoader {
 public:
  static PluginLoader *getInstance();
  Plugin* loadPlugin(PluginKey key,
                     float inputSampleRate,
                     int adapterFlags=0) {
    // ... other methods for later
  }
};

class PluginBase {
 public:
  virtual std::string getIdentifier() const = 0;
  virtual std::string getName() const = 0;
  virtual std::string getDescription() const = 0;
  virtual int getPluginVersion() const = 0;
  // ... other methods for later
};
  1. 設計java類
    PluginBase是純虛類直接映射爲java的Interface,這個作法留做後面的章節重構
package org.vamp_plugins;
public class Plugin {
 public native String getIdentifier();
 public native String getName();
 public native String getDescription();
 public native int getPluginVersion();
}

package org.vamp_plugins;
public class PluginLoader {
 public class LoadFailedException extends Exception {};
 public static synchronized PluginLoader getInstance() {
    // some magic here
 }
 
 public Plugin loadPlugin(String key, float inputSampleRate)
   throws LoadFailedException {
   // and here
 }
}
  • native對象handle
    經過對象的handle,以上的兩個java類直接對應於c++的類
    PluginLoader類更新以下:
public class PluginLoader {
 public class LoadFailedException extends Exception {};
 
 public static synchronized PluginLoader getInstance() {
   if (inst = null) {
     inst = new PluginLoader();
     inst.initialise();
   }
   
   return inst;
 }
 
 public Plugin loadPlugin(String key, float inputSampleRate)
   throws LoadFailedException {
   long handle = loadPluginNative(key, inputSampleRate);
   if (handle != 0) return new Plugin(handle);
   else throw new LoadFailedException();
 }
 
 private PluginLoader() { initialise(); }
 private static PluginLoader inst;
 private long nativeHandle;
 private native long loadPluginNative(String key, float inputSampleRate);
 private native void initialise();
}

public class Plugin {
 private long nativeHandle;
 protected Plugin(long handle) {
   nativeHandle = handle; 
 }
 
 public native String getIdentifier();
 public native String getName();
 public native String getDescription();
 public native int getPluginVersion();
}
  • 生成jni函數聲明
    javac和javah就可作這些事
$ javac org/vamp_plugins/*.java
$ javah -jni org.vamp_plugins.Plugin org.vamp_plugins.PluginLoader
$ ls -1
org
org_vamp_plugins_Plugin.h
org_vamp_plugins_PluginLoader.h
sample.h
$

生成的頭文件內容片斷:java

JNIEXPORT jstring JNICALL
Java_org_vamp_1plugins_Plugin_getIdentifier(JNIEnv*, jobject);

JNIEnv*:當前jni函數的運行環境
jobject:調用當前函數的this指針c++

section 2

  1. 實現jni函數
    handle.h =>
#ifnef __HANDLE_H_INCLUDE__
#define _HANDLE_H_INCLUDE__

jfieldID getHandleField(JNIEnv* env, jobject ojb) {
  jclass c = env->GetObjectClass(obj);
  // J is the type singure for long:
  return env->getFieldID(c, "nativeHandle", "J");
}

template <typename T>
T* getHandle(JNIEnv* env, jobject obj) {
  jlong handle = env->GetLongField(obj, getHandleField(env, obj));
  return reinterpret_cast<T*>(handle);
}

template <typename T>
T* setHandle(JNIEnv* env, jobject obj, T* t) {
  jlong handle = reinterpret_cast<jlong>(t);
  env->SetLongField(obj, getHandleField(env, obj), handle);
}

#endif

pluginloader.cc =>數組

#include "org_vamp_plugins_PluginLoader.h"
#include <vamp-hostsdk/PluginLoader.h>
#include "handle.h"

using Vamp::Plugin;
using Vamp::HostExt::PluginLoader;

void Java_org_vamp_lpugins_PluginLoader_initialise(JNIEnv* env, jobject obj) {
  PluginLoader* inst = PluginLoader::getInstance();
  setHandle(env, obj, inst);
}

jlong
Java_org_vamp_lplugins_PluginLoader_loadPluginNative(JNIEnv* env, jobject obj,
  jstring key, jfloat rate) {
  PluginLoader* inst = getHandle<PluginLoader>(env, obj);
  const char* kstr = env->GetStringUTFChars(key, 0);
  Plugin* p = inst->loadPlugin(kstr, rate);
  env->ReleaseStringUTFChars(key, kstr);
  return (jlong)p;  
}

plugin.cc =>多線程

#include "org_vamp_plugins_plugin.h"
#include <vamp-hostsdk/plugin.h>
#include "handle.h"

using Vamp::Plugin;
using std::string;

jstring
Java_org_vamp_1plugins_Plugin_getIdentifier(JNIEnv *env, jobject obj) {
    Plugin *p = getHandle<Plugin>(env, obj);
    return env->NewStringUTF(p->getIdentifier().c_str());
}

jstring
Java_org_vamp_1plugins_Plugin_getName(JNIEnv *env, jobject obj) {
    Plugin *p = getHandle<Plugin>(env, obj);
    return env->NewStringUTF(p->getName().c_str());
}

jstring
Java_org_vamp_1plugins_Plugin_getDescription(JNIEnv *env, jobject obj) {
    Plugin *p = getHandle<Plugin>(env, obj);
    return env->NewStringUTF(p->getDescription().c_str());
}

jint
Java_org_vamp_1plugins_Plugin_getPluginVersion(JNIEnv *env, jobject obj) {
    Plugin *p = getHandle<Plugin>(env, obj);
    return p->getPluginVersion();
}
  1. 寫一個測試程序
package org.vamp_plugins;

public class test {
    public static void main(String[] args) {

        // This is the name of a Vamp plugin we know we have installed
        String key = "vamp-example-plugins:percussiononsets";

        PluginLoader loader = PluginLoader.getInstance();

        try {
            Plugin p = loader.loadPlugin(key, 44100);
            System.out.println("identifier: " + p.getIdentifier());
            System.out.println("name: " + p.getName());
            System.out.println("description: " + p.getDescription());
            System.out.println("version: " + p.getPluginVersion());
        } catch (PluginLoader.LoadFailedException e) {
            System.out.println("Plugin load failed");
        }
    }
}

section 3

  • 處理c++對象
    Java有gc,而c++須要手動管理內存,有下面兩種方法:
  1. 添加一個方法,讓java層調用刪除對象
  2. 在java的finialize() 中刪除對象
    第二種方法,看似完善,可是java的gc難以精準控制native對象的狀態,多線程狀況下更不肯定,
    因此只能使用方法一:添加 dispose方法
public native void dispose();
void
Java_org_vamp_1plugins_Plugin_dispose(JNIEnv* env, jobject obj) {
  Plugin* p = getHandle(env, obj);
  setHandle(env, obj, 0);
  delete p;
}

section 4

  • 複雜一點的用法
typedef std::vector<Feature> FeatureList;
typedef std::map<int, FeatureList> FeatureSet;
virtual FeatureSet process(const float* const* inputBuffers,
  RealTime timestamp) = 0;

java 沒有typedef 對應map以下
public native Map<Integer, ArrayList<Feature>>
  process(float[][] inputBuffers, RealTime timestamp);
- 在jni層構建java對象
jclass featClass = env->FindClass("org/vamp_plugins/Feature");
jmethodID ctor = env->GetMethodID(featClass, "<init>", "()V");
jobject feature = env->NewObject(featClass, ctor);
- jni處理泛型
jclass treeMapClass = env->FindClass("java/util/TreeMap");
jmethodID treeMapCtor = env->GetMethodID(treeMapClass, "<init>", "()V");
jobject map = env->NewObject(treeMapClass, treeMapCtor);
- 從多維數組中取數據
jobject
Java_org_vamp_1glugins_Plugin_process(JNIEnv* env, jobject obj,
 jobjectArray inputBuffers, jobject timestamp);

int channels = env->GetArrayLength(data);
float **input = new float *[channels];

for (int c = 0; c < channels; ++c) {
    jfloatArray cdata =
        (jfloatArray)env->GetObjectArrayElement(data, c);
    input[c] = env->GetFloatArrayElements(cdata, 0);
}

for (int c = 0; c < channels; ++c) {
    jfloatArray cdata =
        (jfloatArray)env->GetObjectArrayElement(data, c);
    env->ReleaseFloatArrayElements(cdata, input[c], 0);
}

delete[] input;

That's all, thx for reading, hope it's been useful
譯自:http://thebreakfastpost.com/2012/03/06/wrapping-a-c-library-with-jni-part-1/app

相關文章
相關標籤/搜索