Introdução
O JNI (Java Native Interface) é uma interface que viabiliza interagir a JVM com bibliotecas nativas do SO subjacente. É utilizada principalmente para tarefas de mais baixo nível que a API Java não oferece ou, ainda, quando necessita-se de uma performance maior sem o "overhead" da JVM no caminho.
É importante ressaltar que a utilização de métodos nativos torna sua aplicação dependente de plataforma. Em Linux trabalharemos com arquivos de extensão SO (Shared Objects), já em Windows trabalha-se com DLLs (Dynamic Linked Libraries). O JNI, no entanto, não se limita a somente Linux e Windows, é possível utilizá-lo em outras plataformas (Solaris, AIX, etc) de acordo com a implementação JVM.
Esse tutorial tem o objetivo de mostrar um simples exemplo de utilização do JNI. Diga-se de passagem, a documentação da Sun sobre o assunto é muito rica e os links se encontram nas referências.
Primeiro passo: criar a classe que conterá o(s) método(s) nativo(s).
Crie um diretório "Carnival" (apenas por questão de organização), uma classe e um método com o modificador "native" sem implementação para trabalharmos no exemplo.
public class Carnival
{
// Carregamento da biblioteca que será criada.
static {
System.loadLibrary("Carnival");
}
// Método marcado com modificador "native".
public native void dance(String verse);
public static void main(String[] args) {
new Carnival().dance("...pode chorar, mas chora...");
}
}
Para que a JVM possa "linkar" a chamada do método da classe Carnival com uma biblioteca compartilhada nativa, ela precisará saber o nome. Aqui carregamos a biblioteca assim que a classe Carnival é carregada pela JVM:
static {
System.loadLibrary("Carnival");
}
Logo em seguida declaramos o método "dance()" que será chamado pelo método "main()". Como a implementação de um método "native" será escrita em outra linguagem (C ou C++), não iremos fornecer implementação e terminaremos a declaração com ponto-e-vírgula.
Segundo passo: compilar a classe.
javac Carnival.java
Ela será compilada normalmente, mesmo que as bibliotecas nativas ainda não estejam acessíveis. Você só não conseguirá executar ainda.
Terceiro passo: utilizar o "javah" para gerar os headers em C/C++.
javah -jni Carnival
* Certifique-se que a classe esteja em seu "classpath".
Isso gerará um arquivo Carnival.h que será usado ao criar a implementação do método.
Se quiser, examine o Carnival.h. Você encontrará os protótipos das funções que implementará em seguida.
Quarto passo: implementar o método nativo.
Crie um arquivo chamado Carnival.c e dentro dele virá a implementação do método "dance()".
#include <jni.h>
#include "Carnival.h"
#include <stdio.h>
JNIEXPORT void JNICALL
Java_Carnival_dance(JNIEnv * env, jobject obj, jstring verse)
{
const char * vstr = (*env)->GetStringUTFChars(env, verse, NULL);
printf("%s\n", vstr);
return;
}
Quinto passo: compilar a implementação.
Neste tutorial utilizaremos o GCC. No meu desktop utilizei um Makefile para poupar o trabalho, mas aqui vai o comando direto para compilar.
Antes criaremos um diretório "lib" que irá conter o arquivo "Shared Object" gerado pelo GCC:
mkdir lib
E então compilaremos:
gcc -o lib/libCarnival.so -shared -Wl,-soname,libCarnival.so \
-I/opt/jdk/include \
-I/opt/jdk/include/linux Carnival.c \
-static -lc
É importante lembrar que a biblioteca precisa estar no PATH de bibliotecas do Linux, então defina a variável de ambiente LD_LIBRARY_PATH para o diretório "lib" criado:
LD_LIBRARY_PATH="$(pwd)/lib"
export LD_LIBRARY_PATH
Com isso, ao executar a classe, a "JVM" conseguirá encontrar a biblioteca libCarnival.so.
Sexto (e último) passo: executar.
java Carnival
Espera-se, aqui, que seja impressa a mensagem que foi enviada através do parâmetro "verse" (String) pelo método "dance()" da classe Java Carnival.
Referências:
http://java.sun.com/docs/books/jni/html/start.html#26346
http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html
http://www.ibm.com/developerworks/aix/library/au-JNI_AIX_PAPER.html