Entenda o uso de mais uma palavra reservada de Java: native. Veja como Java pode utilizar funções implementadas em outras linguagens de programação.
A Coluna Java deste mês tenta esclarecer os "segredos" de mais uma palavra chave não tão utilizada pelos programadores Java. Trata-se da palavra reservada native. Essa palavra é usada na declaração de métodos que não são implementados em nenhuma classe Java. Ela serve para indicar à Java Virtual Machine (JVM) que, quando o método for chamado, o código que se deseja executar está em uma biblioteca, implementada com linguagem de programação nativa de um sistema, normalmente C e C++.
O uso da palavra reservada native faz parte, portanto, da camada de interface da plataforma Java com bibliotecas nativas, a Java Native Interface (JNI). A JNI é utilizada para realizar comunicação com sistemas legados, quando não existem bindings (ligação/conexão de mapeamento) de bibliotecas para a linguagem Java e quando há necessidade de realizar operações com melhor desempenho.
A seguir, veja um exemplo de como utilizar a JNI para executar operações em um código Java, utilizando uma biblioteca escrita em C++.
HelloWorldJNI.java
package petnews.colunajava;
public class HelloWorldJNI {
private String javaString;
HelloWorldJNI() {
javaString = "Eu fui instanciada no codigo Java!";
}
// Declaracaoo dos metodos nativos
/**
* Metodo implementado na linguagem C++ que retorna uma String dado um valor inteiro passado como parametro.
*/
public native String mensagem(int i);
/**
* Metodo implementado na linguagem C++ que modifica o valor da variavel 'javaString'.
*/
public native void mudarCampoJavaString();
public String getJavaString() {
return this.javaString;
}
static {
System.loadLibrary("helloworldnativo");
}
public static void main(String[] args) {
HelloWorldJNI hello = new HelloWorldJNI();
System.out.println(hello.getJavaString());
System.out.println(hello.mensagem(1));
System.out.println(hello.mensagem(2));
System.out.println(hello.mensagem(3));
hello.mudarCampoJavaString();
System.out.println(hello.getJavaString());
}
}
Além do uso da palavra reservada native, na declaração dos métodos, observe a necessidade de um bloco que abra a biblioteca que utilizaremos. Nesse caso, os métodos String mensagem(int) e void mudarCampoJavaString() possuem suas implementações na biblioteca helloworldnativo. O próximo passo consiste em extrair um cabeçalho que fará o mapeamento entre as assinaturas dos métodos Java nativos e a sua implementação em C/C++. Para isso, utilizaremos a ferramenta javah, disponível com o Java Development Kit (JDK).
No terminal, acesse o diretório raiz do seu código-fonte e utilize o comando javah para gerar o cabeçalho da sua biblioteca nativa. Veja o exemplo a seguir.
$ javah -classpath bin/ petnews.colunajava.HelloWorldJNI
O cabeçalho gerado tem o nome petnews_colunajava_HelloWorldJNI.h e o seguinte código:
petnews_colunajava_HelloWorldJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class petnews_colunajava_HelloWorldJNI */
#ifndef _Included_petnews_colunajava_HelloWorldJNI
#define _Included_petnews_colunajava_HelloWorldJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: petnews_colunajava_HelloWorldJNI
* Method: mensagem
* Signature: (I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_petnews_colunajava_HelloWorldJNI_mensagem
(JNIEnv *, jobject, jint);
/*
* Class: petnews_colunajava_HelloWorldJNI
* Method: mudarCampoJavaString
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_petnews_colunajava_HelloWorldJNI_mudarCampoJavaString
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
O cabeçalho acima apresenta o mapeamento dos tipos utilizados no programa Java para os tipos presentes na linguagem C/C++, definidos no cabeçalho jni.h.
De posse da assinatura das funções que devem ser implementadas, o próximo passo consiste em escrever a sua implementação.
helloWorldnativo.cpp
#include <stdio.h>
#include "petnews_colunajava_HelloWorldJNI.h"
#include <jni.h>
JNIEXPORT jstring JNICALL Java_petnews_colunajava_HelloWorldJNI_mensagem
(JNIEnv *env, jobject obj, jint ji) {
printf("%s\n", "Passei pela funcao nativa Java_petnews_colunajava_HelloWorldJNI_mensagem!");
// Cria um jstring (definido em jni.h)
jstring result;
switch (ji) {
case 1:
result = env->NewStringUTF("Um!");
break;
case 2:
result = env->NewStringUTF("Dois!");
break;
case 3:
result = env->NewStringUTF("Tres!");
break;
default:
result = env->NewStringUTF("Sei lah!");
break;
}
return result; //retorna o jstring
}
JNIEXPORT void JNICALL Java_petnews_colunajava_HelloWorldJNI_mudarCampoJavaString
(JNIEnv *env, jobject obj) {
// jclass obtem uma referencia para a classe Java do obj => HelloWorldJNI
jclass ref_class = env->GetObjectClass(obj);
// jfield obtem uma referencia para o atributo "javaString", usando a referencia anterior.
jfieldID fid = env->GetFieldID(ref_class, "javaString", "Ljava/lang/String;");
// Criamos um jstring com o novo valor que sera atribuido
jstring novo_valor = env->NewStringUTF("Eu fui instanciada no codigo C++");
// O codigo abaixo altera o valor do atributo javaString para "Eu fui instanciada no codigo C++".
env->SetObjectField(obj, fid, novo_valor);
}
As funções são bem simples. A primeira recebe um inteiro e retorna o número, por extenso, se ele for 1, 2, ou 3. Nos demais casos, a string retornada é "Sei lah!". A segunda função faz uma operação mais interessante e curiosa. Ela modifica o valor da variável javaString que é de acesso privado na classe Java HelloWorldJNI.
Agora, quando o código de helloworldnativo.cpp for compilado em uma biblioteca nativa, ele estará pronto e será utilizado quando o programa Java executar.
Veja, a seguir, o exemplo de saída obtido quando executa-se o main da classe HelloWorldJNI
Eu fui instanciada no codigo Java!
Passei pela funcao nativa Java_petnews_colunajava_HelloWorldJNI_mensagem!
Um!
Passei pela funcao nativa Java_petnews_colunajava_HelloWorldJNI_mensagem!
Dois!
Passei pela funcao nativa Java_petnews_colunajava_HelloWorldJNI_mensagem!
Tres!
Eu fui instanciada no codigo C++
Você executou o código, não modificou nada e sua saída foi diferente? Não se preocupe. O uso do stream de escrita é compartilhado para a execução do código java e do código presente na biblioteca. Daí, as impressões na tela podem ter ordens diferentes.
O uso da JNI se mostra necessário, em alguns casos, como mencionado anteriormente. Vale lembrar, que o seu uso traz algumas desvantagens, tais como: perda de portabilidade da aplicação e a necessidade do aprendizado de outra linguagem de programação.
Jornal PETNews - Edição: Jeymisson Oliveira - Revisão: Savyo Nóbrega e Joseana Fechine
Grupo PET Computação UFCG, 2012. All rights reserved.