UNIVERSIDADE FEDERAL DE MINAS GERAIS
DEPARTAMENTO DE CIÊNCIA DA COMPUTAÇÃO

Métodos Nativos em Java
Roberto da Silva Bigonha
Mário Celso Candian Lobato

Web Analytics Made Easy -
StatCounter

 

Introdução

Vamos usar um exemplo para mostrar passo a passo como construir em C++ métodos native do Java.  

    O exemplo consiste em um programa que possui um menu chamado Native com 2 itens, Native Method 1 e Native Method 2. Esses itens chamam as funções escritas em C++.

 

Passo 1: Escrever o código Java

O código a seguir implementa a classe Main.  Essa classe cria a interface gráfica do aplicativo e chama os métodos Native da classe MetNative.

//Classe Main: Cria a interface gráfica do programa
//             Chama os métodos native da classe MetNative
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Main extends JFrame {
  //a variável mn usada para chamar o metodos native
  private MetNative mn;
  public Main() {
  
    super("Main");
    
    mn = new MetNative();
    
    JMenuBar barra = new JMenuBar();
    setJMenuBar(barra);
    
    JMenu nativeMenu = new JMenu("Native");
    nativeMenu.setMnemonic('N');    
    
    JMenuItem nItem1 = new JMenuItem("Method Native 1");
    nItem1.setMnemonic('M');
    nItem1.addActionListener(
      new ActionListener() {
      	public void actionPerformed(ActionEvent e) {
      	  //Chama o método native method1 da classe MetNative
      	  JOptionPane.showMessageDialog(null, 
      	                         mn.method1(mn.getString()));
      	}
      }
    );
    
    JMenuItem nItem2 = new JMenuItem("Method Native 2");
    nItem2.setMnemonic('e');
    nItem2.addActionListener(
      new ActionListener() {
      	public void actionPerformed(ActionEvent e) {
      	  //Chama o método native method2 da classe MetNative
      	  mn.method2();
      	  JOptionPane.showMessageDialog(null, mn.getString());
      	}
      }
    );    
    
    
    nativeMenu.add(nItem1);
    nativeMenu.add(nItem2);
    
    barra.add(nativeMenu);
    
    
    setSize(500, 300);
    show();
    
  }
  
  public static void main (String args[]) {
    
    Main m = new Main();
    
    m.addWindowListener (
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      }
    );
    
  }
  
}

 

Main.java

 

A classe MetNative declara os métodos Native e carrega a biblioteca dinâmica em que se encontra a implementação em C++ dos métodos declarados.

//Classe MetNative: Declara os métodos Native
//                  Carrega a biblioteca dinâmica onde os  
//                  métodos foram implementados (em C++)
class MetNative {
  private String str;
  
  MetNative() {
    str = new String("Entrou no Java");
  }
  
  //Declara os métodos Native
  public native String method1(String s);
  public native void method2();
  public String getString() {
    return str;
  }
  
  //Carrega a biblioteca dinâmica 
  static {
    System.loadLibrary("native");
  }
  
}

 

MetNative.java

 

Passo 2: Compila o código Java

javac Main.java

 

Passo 3: Criar o arquivo .h

O arquivo cabeçalho providencia assinatura das funções em que serão implementados os métodos Native.

   Para criar o aquivo .h deve usar o comando javah da seguinte forma:

javah <nome da classe onde os métodos native foram declarados>

    No nosso exemplo temos:

javah MetNative

 

   O arquivo gerado foi o seguinte:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MetNative */
#ifndef _Included_MetNative
#define _Included_MetNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     MetNative
 * Method:    method1
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_MetNative_method1
  (JNIEnv *, jobject, jstring);
/*
 * Class:     MetNative
 * Method:    method2
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_MetNative_method2
  (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

 

MetNative.h

 

    JNIEXPORT e JNICALL são sempre incluídos nas assinaturas dos métodos Natives. JNIEXPORT e JNICALL garantem que o código fonte compilado em plataformas, por exemplo Windows,  que requer especiais palavras chaves para exportar funções de bibliotecas de ligação dinâmicas.

    Cada método Native possui dois parâmetros a mais em relação aos parâmetros que você declarou no lado de Java. O primeiro parâmetro, JNIEnv *, é um ponteiro para interface JNI. A interface JNI possui varias funções que serão usadas para acessar os parâmetros e objetos passados pelo aplicativo Java. O segundo parâmetro é um jobject, que referência o corrente objeto. Você pode pensar que o jobject parâmetro com a variável this do Java. No exemplo o parâmetro jobject referência ao objeto do tipo MetNative declarado e instanciado na classe Main com o nome de mn.

    Em nosso exemplo, o método Native method1 possui uma string como argumento e retorna uma outra string.

public native String method1(String s);

    Seu correspondente Native implementação tem o tipo jstring (veja a seção Mapeamento entre Java e Native tipos) para ambos, o argumento e o valor de retorno.

JNIEXPORT jstring JNICALL Java_MetNative_method1
(JNIEnv *, jobject, jstring);

Graficamente:

 

 

Passo 4: Escrever a implementação dos métodos Natives em C++:

Ao escrever a implementação dos métodos Natives é necessário incluir os seguintes arquivos cabeçalhos:

//native.cpp : Arquivo em C++ que implementa os métodos native.

#include <jni.h> 
#include "MetNative.h" //arquivo gerado com o javah
#include <string>
using namespace std;

JNIEXPORT jstring JNICALL Java_MetNative_method1 
  (JNIEnv *env, jobject obj, jstring js){
  
  //cria e inicializa uma string do C++ com o 
  //valor da string passada por parâmetro
  string cppstr (env->GetStringUTFChars(js, 0));
  
  //cria um jstring (tipo definido em jni.h)
  jstring jstr;
  
  cppstr = cppstr.substr(0, 9) + " metodo 1 do C++";
  
  //altera o valor de jstr
  jstr = env->NewStringUTF(cppstr.c_str()); 
  
  return jstr; //retorna o jstring
  
}
JNIEXPORT void JNICALL Java_MetNative_method2
  (JNIEnv *env, jobject obj){
  string cstr("Entrou no metodo 2 do C++");
  
  //recebe uma referência para o objeto do tipo MetNativo
  jclass cla = env->GetObjectClass(obj);
  
  //tipo definido no jni.h para acessar os campos dos objetos
  jfieldID fid;
  
  jstring str = env->NewStringUTF(cstr.c_str()); 
  
  //recebe um referência para o campo str do objeto MetNativo
  fid = env->GetFieldID(cla, "str", "Ljava/lang/String;");
  
  //altera o campo str do objeto MetNativo.
  env->SetObjectField(obj, fid, str);
  
}
 

native.cpp

 

 

 

Passo 5: Compilar o código C++

g++ -o native.dll -shared -IvotreRep native.cpp

 

  • Entre no menu Arquivo, depois no submenu Novo e clique no item Projeto
  • Selecione o tipo de projeto DLL
  • Coloque o nome do projeto como native
  • Remova os arquivos que o proprio Dev-C++ inclui no projeto
  • Adicione os arquivos: native.cpp e MetNative.h 
  • Entre no menu Ferramentas e clique no item Opções do Compilador
  • Clique em Diretórios
  • Clique em C++ Includes
  • Adicione os sequintes diretórios: 
    • c:\j2sdk1.x.x\include
    • c:\j2sdk1.x.x\include\win32
  • Compila o projeto

 

 

g++ native.cpp -I $JAVA_HOME/include -I $JAVA_HOME/include/linux\ -shared -fpic -o libnative.so

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

 

OBS: $JAVA_HOME é o caminho onde o sdk foi instalado.

 

Passo 6: Rodar o programa:

java Main

 

Mapeamento entre Java e Native tipos

Nessa seção, você vai aprender como fazer referência Java tipos em seu Native método. Isso será importante quando:

 

Java Tipos Primitivos

O método Native pode diretamente acessar Java primitivos tipos como booleanos, inteiros, etc, que são passados do aplicativo Java. Por exemplo, o tipo boolean do Java mapeia para o C++ como o tipo jboolean, enquanto o tipo float do Java mapeia para o jfloat em C++. A seguinte tabela descreve o mapeamento dos tipos primitivos do Java para os tipos Native.

Tipos Java

Tipos Native

Tamanho em bits

boolean

jboolean

8, unsigned

byte

jbyte

8

char

jchar

16, unsigned

short

jshort

16

int

jint

32

long

jlong

64

float

jfloat

32

double

jdouble

64

void

void

n/a

 

Java tipo Object

Objetos Java são passados por referência. Todas as referências de objetos Java tem o tipo jobjetct. Por conveniência e para evitar erro de programação, o JNI implementou um conjunto de tipos que serão todos subclasses de jobject. Na figura abaixo podemos ver esses tipos: