J'utilise Ubuntu 10.10
Voilà donc ce que j'ai fait.
Bonjour.Java:
class Hello {
public native void sayHello();
static { System.loadLibrary("hellolib"); }
public static void main(String[] args){
Hello h = new Hello();
h.sayHello();
}
}
Ensuite, j'ai exécuté les commandes suivantes:
dierre@cox:~/Scrivania/provajni$ javac Hello.Java
dierre@cox:~/Scrivania/provajni$ javah -jni Hello
J'ai obtenu Hello.class
Et Hello.h
.
Bonjour.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */
#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Hello
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Hello_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Ensuite, j'ai créé Hello.cpp:
#include <jni.h>
#include "Hello.h"
#include <iostream>
using namespace std;
JNIEXPORT void JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) {
cout << "Hello World!" << endl;
return;
}
Et maintenant, la partie où je pense que j'ai foiré. J'étais inspiré par ceci guide ( Compiler la bibliothèque d'objets dynamiques ou partagés section) :
dierre@cox:~/Scrivania/provajni$ gcc -I"/usr/lib/jvm/Java-6-Sun/include" -I"/usr/lib/jvm/Java-6-Sun/include/linux" -o hellolib.so -shared -Wl,-soname,hello.so Hello.cpp -static -lc
qui génère le fichier hellolib.so
Mais quand j'essaye de l'exécuter avec Java Hello
J'ai cette erreur:
Exception in thread "main" Java.lang.UnsatisfiedLinkError: no hellolib in Java.library.path
at Java.lang.ClassLoader.loadLibrary(ClassLoader.Java:1734)
at Java.lang.Runtime.loadLibrary0(Runtime.Java:823)
at Java.lang.System.loadLibrary(System.Java:1028)
at Hello.<clinit>(Hello.Java:4)
Could not find the main class: Hello. Program will exit.
J'ai même essayé ceci:
LD_LIBRARY_PATH=`pwd`
export LD_LIBRARY_PATH
sans résultat.
Je sais que je fais quelque chose d'extrêmement stupide mais je n'arrive pas à comprendre ce que c'est. La bibliothèque dynamique est générée avec l'option -shared, n'est-ce pas?
Mise à jour # 1
J'ai essayé static { System.load("/home/dierre/Scrivania/provajni/hellolib.so"); }
pour voir si cela fonctionnait mais maintenant:
Exception in thread "main" Java.lang.UnsatisfiedLinkError: /home/dierre/Scrivania/provajni/hello.so: /home/dierre/Scrivania/provajni/hello.so: undefined symbol: _ZSt4cout
at Java.lang.ClassLoader$NativeLibrary.load(Native Method)
at Java.lang.ClassLoader.loadLibrary0(ClassLoader.Java:1803)
at Java.lang.ClassLoader.loadLibrary(ClassLoader.Java:1699)
at Java.lang.Runtime.load0(Runtime.Java:770)
at Java.lang.System.load(System.Java:1003)
at Hello.<clinit>(Hello.Java:4)
Mise à jour # 2 Ok, pour résoudre le problème Update # 1 j'ai dû utiliser g++
Insted de gcc
, évidemment. J'ai toujours du mal à utiliser la méthode load
. Je n'arrive pas à lui dire le bon chemin.
La bibliothèque native peut être chargée par loadLibrary avec un nom valide. Par exemple, lib [~ # ~] xxxx [~ # ~] . Donc pour la famille linux, votre hellolib.so devrait renommer libhello.so. Par ailleurs, je développe Java avec jni, je séparerai l'implémentation et l'interface native (.c ou .cpp).
static {
System.loadLibrary("hello"); // will load libhello.so
}
L'en-tête d'implémentation (HelloImpl.h):
#ifndef _HELLO_IMPL_H
#define _HELLO_IMPL_H
#ifdef __cplusplus
extern "C" {
#endif
void sayHello ();
#ifdef __cplusplus
}
#endif
#endif
HelloImpl.cpp:
#include "HelloImpl.h"
#include <iostream>
using namespace std;
void sayHello () {
cout << "Hello World!" << endl;
return;
}
Bonjour.c (je préfère compiler jni en c):
#include <jni.h>
#include "Hello.h"
#include "HelloImpl.h"
JNIEXPORT void JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) {
sayHello();
return;
}
Enfin, nous pouvons les compiler en quelques étapes:
g ++ -c -I "/ opt/Java/include" -I "/ opt/Java/include/linux" HelloImpl.cpp
g ++ -I "/ opt/Java/include" -I "/ opt/Java/include/linux" -o libhello.so -shared -Wl, -soname, hello.so Hello.c HelloImpl.o -static -lc
à l'étape 2, nous utilisons g ++ pour le compiler. C'est très important. vous pouvez voir Comment mélanger C et C++
Après la compilation, vous pouvez vérifier le nom de la fonction avec nm:
$ nm libhello.so |grep say
00000708 T Java_Hello_sayHello
00000784 t _GLOBAL__I_sayHello
00000718 T sayHello
Il y a un Java_Hello_sayHello marqué T. Il devrait être exactement égal au nom de votre méthode native. Si tout va bien. vous pouvez l'exécuter:
$ Java -Djava.library.path=. Hello
Hello World!
Enfin mon code fonctionne. C'est bonjour Java
public class hello {
public native void sayHello(int length) ;
public static void main (String args[]) {
String str = "I am a good boy" ;
hello h = new hello () ;
h.sayHello (str.length() ) ;
}
static {
System.loadLibrary ( "hello" ) ;
}
}
Vous devez le compiler comme:
$ javac hello.Java
Pour créer un fichier .h, vous devez exécuter cette commande:
$ javah -jni hello
C'est hello.h
:
JNIEXPORT void JNICALL Java_hello_sayHello
(JNIEnv *, jobject, jint);
Voici hello.c
:
#include<stdio.h>
#include<jni.h>
#include "hello.h"
JNIEXPORT void JNICALL Java_hello_sayHello
(JNIEnv *env, jobject object, jint len) {
printf ( "\nLength is %d", len ); }
Pour compiler cela et créer une bibliothèque partagée, nous devons exécuter cette commande:
$ gcc -I/usr/lib/jvm/Java-6-openjdk/include -o libhello.so -shared hello.c
Enfin, exécutez celui-ci:
$ Java -Djava.library.path=. hello
Cela se plaint que les symboles C++ ne soient pas disponibles. Je semble me souvenir, quand j'utilisais pour faire des trucs JNI tout le temps qu'il y avait des problèmes de liaison dans les bibliothèques C++ et que nous nous en tenions toujours au vieux C
Si vous modifiez votre code pour qu'il soit en C standard (et renommez le fichier):
#include <jni.h>
#include "Hello.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) {
printf("Hello World");
return;
}
Et compilez-le
gcc -I/usr/lib/jvm/Java-6-openjdk/include -o libhellolib.so -shared Hello.c
Ça marche
Java -Djava.library.path=`pwd` Hello
Hello World