2016-05-24 81 views
5

Bu yüzden java'dan bir C işlevini çağırmak için JNI kullanmam gerekiyor. Farklı veri türlerinde (yerel değişkenleri, başlık dosyasını, paylaşılan kütüphaneyi, blah blah'ı) geçirirken bunu başarılı bir şekilde yapabildim, ancak bir bayt dizisiyle çalışmasını sağlayamadım. İşte benim C işlevi var:Java Yerel Arabirimini, bir bayt dizisini arg * olarak bir char * alan bir C işlevine geçirmek için nasıl kullanabilirim?

#include <stdio.h> 
void encrypt(int size, unsigned char *buffer); 
void decrypt(int size, unsigned char *buffer); 

void encrypt(int size, unsigned char *buffer){ 
    for(int i=0; i<size; i++){ 
     unsigned char c = buffer[i]; 
     printf("%c",c); 
    } 
} 
void decrypt(int size, unsigned char *buffer){ 
    for(int i=0; i<size; i++){ 
     unsigned char c = buffer[i]; 
     printf("%c",c); 
    } 
} 

Ve işte benim java kodu

class Tester{ 
    public native void encrypt(int size, char *buffer); 
    public native void decrypt(int size, char *buffer); 
    static{ 
    System.loadLibrary("buffer"); 
    { 
    public static void main(String[] args){ 
     Tester test = new Tester(); 
     String hello = "hello"; 
     byte[] byteHello = hello.getBytes(); 
     test.encrypt(5,byteHello); 
     test.decrypt(5,byteHello); 
    } 
} 
(bu bir başlık dosyası yaptıktan sonra, ben başlık dosyasına JNI'yı koduyla C işlev açıklamalarını değiştirmek zorunda olduğunu anlıyoruz)

Javada char * tipinin desteklenmediğini anlıyorum ve bu yüzden derlemeye çalışırken bir hata alıyorum. Belki de Java'daki tipi char [] olarak değiştirmeliyim? Her neyse, amacım Java'daki bir bayt dizisini C işlevime geçirebilmek, bayt dizisini yineleyip, her bir öğeyi yazdırabilmektir.

+0

Java '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' byte ''' '' '' 'C'ye geçmek ve daha sonra her öğeyi dönüştürmek daha iyi olacaktır. talep –

+0

"Java Yerel Arabirimini, bir bayt dizisini bir argüman olarak bir char * alan bir C işlevine geçirmek için nasıl kullanabilirim?" - yapamazsınız, ancak bir bayt dizisini argüman olarak bir Java bayt dizisini alan bir C işlevine geçirebilirsiniz. – immibis

cevap

6

Java char ve C char tip muhtemelen C byte[] geçmek ve daha sonra talep üzerine her bir öğeyi dönüştürmek için daha iyi olurdu, uyumsuz. Bu doğrultuda


şey:

Main.java:

//...Code to load library... 

public static void main(String[] args) { 
    passBytes("hello".getBytes()); 
} 

public static native void passBytes(byte[] bytes); 

main.c:

#include "Main.h" // Main.h is generated 

JNIEXPORT void JNICALL Java_Main_passBytes 
    (JNIEnv *env, jclass clazz, jbyteArray array) { 
    unsigned char* buffer = (*env)->GetByteArrayElements(env, array, NULL); 
    jsize size = (*env)->GetArrayLength(env, array); 

    for(int i = 0; i < size; i++) { 
     printf("%c", buffer[i]); 
    } 

    (*env)->ReleaseByteArrayElements(env, array, buffer, JNI_ABORT); 
} 

jbyteArray, bir saplama tipi sonra başka bir şey değildiriçinde tanımlanmış:

struct _jobject; 
typedef struct _jobject *jobject; 
typedef jobject jarray; 
typedef jarray jbyteArray; 

Aslında hiçbir veri içermiyor. Az çok sadece bir hafıza adresi.

Ondan elemanlar almak için GetByteArrayElements'a (tür byte[] olduğu için) geçerek VM'nin bir C stili dizisindeki öğeleri almasını isteyebiliriz. (Bir kopya oluşturabilir veya oluşturmayabilir. Bkz. Belge)

Aynı şey dizi uzunluğu için de yapılır.

Sanal makineye diziyle bittiğini söylemek için. ReleaseArrayElements'u aradık.

Ayrıca jbyte yüzden sadece bu durumda güvenlidir yerine jbyte* bir unsigend char* olarak GetByteArrayElements sonucunu kullanan, signed char olarak tanımlanır.

+0

Biraz daha fazla açıklama, bunu daha iyi bir cevap haline getirecektir. Yorumunuzun bir kopyası iyi bir başlangıç ​​olacaktır, ancak yerel bir yöntemle alınan bir Java dizisinin * bir * C dizisi olmadığı da belirtilmelidir.Ondan gerçek bir C dizisini (gösterdiğiniz gibi) elde etmek için uygun JNI işlevini kullanmanız ve genel yerel yöntem uygulamasına bazı ek gereksinimler getirmeniz gerekir. –

+0

@JohnBollinger Öneriniz için teşekkürler. OP'nin çoğunun bunu zaten biliyor olduğunu varsaydım. Ama bazen başka insanların da bunu okuyacağını unuttum;) –

+1

Sürüm * işlevlerinin önemi, JVM yığındaki nesneleri kaldırmaktır. Dizi öğeleri mutlaka Get * tarafından kopyalanmaz ve bu nedenle JNI_ABORT, JVM dizisini değiştirmekten kaçınmaz; Sadece zaman kazandırır _if_ bir kopya .. –