Java Native Interface (JNI) – interface umożliwiający wywoływanie natywnego kodu z biblioteki napisanej w c/c++ w środowisku maszyny wirtualnej Javy.

Co trzeba zrobić żeby wykorzystać JNI w swoim projekcie:

Tworzymy sobie w projekcie klasę, np: Monster i dodajemy do niej metodę z sygnaturą native, np:

public class Monster {

    private String name;
    private int strength;
    private int dexterity;
    private int hp;
    private int defence;
    private Monster monster;

    public native static byte[] toByteArray (Monster monster);

    public native static Monster toObject (byte[] array);
}

Jak widać mamy kilka pól w naszej klasie oraz dwie natywne metody toByteArray i toObject

Następny krok to wykonanie komendy javac:

javac -h . Monster.java

-h – oznacza gdzie ma być wygenerowany natywny nagłówek dla naszej klasy

w rezultacie dostajemy:

nerull@anvil ~/D/A/r/j/s/m/j/e/craftsoft> ls -all
razem 32
drwxr-xr-x 2 nerull nerull 4096 04-17 20:19 ./
drwxr-xr-x 3 nerull nerull 4096 02-03 13:43 ../
-rw-r--r-- 1 nerull nerull 1677 04-17 20:20 eu_craftsoft_Monster.h
-rw-r--r-- 1 nerull nerull 2164 04-17 20:20 Monster.class
-rw-r--r-- 1 nerull nerull 2179 04-17 20:19 Monster.java
nerull@anvil ~/D/A/r/j/s/m/j/e/craftsoft> 

Następnym krokiem jest stworzenie projektu c++

Można go stworzyć za pomocą meson:

mkdir monster-native
cd monster-native
CXX=clang++ meson init -l cpp --builddir build -b

do projektu dodajemy zależności jako include z katalogu $JAVA_HOME/include

Następnie przenosimy lub kopiujemy nasz plik .h do projektu c++

przykładowy plik .h wygląda tak:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class eu_craftsoft_Monster */

#ifndef _Included_eu_craftsoft_Monster
#define _Included_eu_craftsoft_Monster
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     eu_craftsoft_Monster
 * Method:    toByteArray
 * Signature: (Leu/craftsoft/Monster;)[B
 */
JNIEXPORT jbyteArray JNICALL Java_eu_craftsoft_Monster_toByteArray
  (JNIEnv *, jclass, jobject);

/*
 * Class:     eu_craftsoft_Monster
 * Method:    toObject
 * Signature: ([B)Leu/craftsoft/Monster;
 */
JNIEXPORT jobject JNICALL Java_eu_craftsoft_Monster_toObject___3B
  (JNIEnv *, jclass, jbyteArray);

#ifdef __cplusplus
}
#endif
#endif

następnie dodajmy do projektu plik z kodem c++ np. monster.cpp i w nim mamy zawartą implementację logiki metod, w naszym przypadku jest to serializacja i deserializacja obiektu

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
#include "eu_craftsoft_Monster.h"
#include "monsters_generated.h"
#include <string>
#include <iostream>


using namespace std;

inline string stringOfObject (JNIEnv * env, jclass& clazz, jobject& ob, string&& fieldName) {
    jfieldID field = env->GetFieldID(clazz, fieldName.data(), "Ljava/lang/String;");
    jstring result = (jstring) env->GetObjectField(ob, field);
    const char *cstr = env->GetStringUTFChars(result, nullptr);
    std::string str = std::string(cstr);
    env->ReleaseStringUTFChars(result, cstr);
    return str;
}

inline const char* charOfObject (JNIEnv * env, jclass& clazz, jobject& ob, string&& fieldName) {
    jfieldID field = env->GetFieldID(clazz, fieldName.data(), "Ljava/lang/String;");
    jstring result = (jstring) env->GetObjectField(ob, field);
    return env->GetStringUTFChars(result, nullptr);
}

inline int intOfObject (JNIEnv * env, jclass& clazz, jobject& ob, string&& fieldName) {
    jfieldID field = env->GetFieldID(clazz, fieldName.data(), "I");
    jint result = env->GetIntField(ob, field);
    return result;
}


inline void intToObject (JNIEnv * env, jclass& clazz, jobject& ob, string&& fieldName, int&& value) {
    jfieldID field = env->GetFieldID(clazz, fieldName.data(), "I");
    env->SetIntField(ob, field, value);
}

/*
 * Class:     eu_craftsoft_Monster
 * Method:    toByteArray
 * Signature: (Leu/craftsoft/Monster;)[B
 */
JNIEXPORT jbyteArray JNICALL Java_eu_craftsoft_Monster_toByteArray (JNIEnv * env, jclass clazz, jobject ob) {
    const char * name = charOfObject(env, clazz, ob, "name");
    int hp = intOfObject(env, clazz, ob, "hp");
    int strength = intOfObject(env, clazz, ob, "strength");
    int dexterity = intOfObject(env, clazz, ob, "dexterity");
    int defence = intOfObject(env, clazz, ob, "defence");

    flatbuffers::FlatBufferBuilder builder(1024);
    auto monster = craftsoft::CreateMonsterDirect(builder, name, strength, dexterity, hp, defence);
        
    builder.Finish(monster);
    uint8_t * buf = builder.GetBufferPointer();
    int size = builder.GetSize();
    
    jbyteArray data = env->NewByteArray(size);
    env->SetByteArrayRegion(data, 0, size, (jbyte *)buf);  
    return data;
}

/*
 * Class:     eu_craftsoft_Monster
 * Method:    toObject
 * Signature: ([B)Leu/craftsoft/Monster;
 */
JNIEXPORT jobject JNICALL Java_eu_craftsoft_Monster_toObject___3B (JNIEnv * env, jclass clazz, jbyteArray bArray) {
    jobject monster = env->AllocObject(clazz);
    
    jbyte * data = env->GetByteArrayElements(bArray, nullptr);
    uint8_t * buf = (uint8_t* ) data;
    flatbuffers::FlatBufferBuilder builder(1024);
    
    auto flatcMonster = craftsoft::GetMonster(buf);

    intToObject(env, clazz, monster, "hp", flatcMonster->hp());
    intToObject(env, clazz, monster, "strength", flatcMonster->strength());
    intToObject(env, clazz, monster, "dexterity", flatcMonster->dexterity());
    intToObject(env, clazz, monster, "defence", flatcMonster->defence());
    
    delete[] data;
    return monster;
}

Projekt c++ musi być zdefiniowany jako biblioteka, np za pomocą meson definiujemy tak:

lib = shared_library('monster-native', ['src/eu_craftsoft_Monster.cpp'],dependencies: [flatbuffers_dep])

Kompilujemy:

cd monster-native
ninja -C build

po skompilowaniu się mamy w katalogu build plik: libmonster-native.so

Następnym krokiem jest załadowanie naszej biblioteki do aplikacji:

static {
        System.load(LIB_PATH + "/libmonster-native.so");
}

Wszystko co pozostaje to już klasyczne wywołanie metod w kodzie Java

3 komentarze

  1. Excellent goods from you, man. I have understand your stuff previous to and you are just too
    excellent. I actually like what you’ve acquired here, certainly like what you are stating and the way
    in which you say it. You make it entertaining
    and you still take care of to keep it wise.
    I cant wait to read far more from you. This is actually a wonderful site.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *