Acessando os métodos de um objeto
Com JNI alem de podermos acessar os atributos de um objeto Java podemos também acessar seus métodos, este acesso é conhecido como callback, pois o Java executa um método nativo e em seguida o método nativo executa um método Java.
Figura 2: Estrutura de Callback
Existem três tipos de métodos Java:
• Métodos de instancia
• Métodos de classe
• Construtores
Vamos abordar o procedimento a ser realizado em cada caso.
Acessando métodos de instancia
O acesso aos métodos de uma classe é extremamente parecido com o acesso aos atributos e para acessá-los também é preciso seguir dois passos:
1.O primeiro passo é obter o methodID do método usando a função:
GetStaticMethodID
Protótipo
jmethodID GetStaticMethodID(JNIEnv *env,jclass clazz, const char *name, const char *sig);
2.E em seguida usamos as funções:
Call
Protótipo:
<tipoNativo> Call<tipo>Method(JNIEnv *env,jobject obj, jmethodID methodID, ...);
| Call<Tipo>Method | <TipoNativo> |
| CallVoidMethodV | void |
| CallObjectMethodV | Jobject |
| CallBooleanMethodV | Jboolean |
| CallByteMethodV | Jbyte |
| CallCharMethodV | Jchar |
| CallShortMethodV | Jshort |
| CallIntMethodV | Jint |
| CallLongMethodV | Jlong |
| CallFloatMethodV | Jfloat |
| CallDoubleMethodV | Jdouble |
Devemos usar a função de acordo com o tipo retornado. Os parâmetros são passados por uma lista de parâmetros variáveis que esta no final da função (...)
CallObjectMethod() é usado tanto para métodos que retornam objetos como para métodos que retornam arrays, para os métodos que retornam tipos primitivos devemos usar a função correspondente de acordo com a tabela acima.
Como exemplo, na listagem 7, faremos um método nativo chamado somaC() que chama um método Java chamado somaJava(), conforme listagem 6.
public class Contador { private int atributo = 0; private native void incrementa(); private int somaJava(int a, int b){ return a + b; } private native void somaC(); Contador contador = new Contador(); o atributo vale:"+contador.atributo); contador.incrementa(); o atributo vale:"+contador.atributo); contador.somaC(); } static { } }
Listagem 6: Contador.java, implementação do método somaJava()
E a implementação da função nativa correspondente:
/* contador.cpp */ (...) JNIEXPORT void JNICALL Java_Contador_somaC(JNIEnv *env, jobject obj){ int a=5,b=4,c; //Obter o methodID jclass class = (*env)->FindClass(env,"Contador"); jmethodID methodID = (*env)->GetMethodID(env, class, "somaJava", "(II)I"); //Executando o metodo c=(*env)->CallIntMethod(env, obj, methodID, a,b); } }
Listagem 7: Contador.cpp, implementação da função somaC()
Acessando métodos de classe
O processo é similar ao de execução de método de instancia, só que agora usamos outras funções.
1.Para obter o methodID usamos:
GetStaticMethodID
Protótipo:
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
2.Para executar os métodos usamos:
CallStatic
Protótipo:
<tipoNativo> CallStatic<tipo>MethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
| CallStatic<Tipo>Method | <TipoNativo> |
| CallStaticVoidMethodV | void |
| CallStaticObjectMethodV | Jobject |
| CallStaticBooleanMethodV | Jboolean |
| CallStaticByteMethodV | Jbyte |
| CallStaticCharMethodV | Jchar |
| CallStaticShortMethodV | Jshort |
| CallStaticIntMethodV | Jint |
| CallStaticLongMethodV | Jlong |
| CallStaticFloatMethodV | Jfloat |
| CallStaticDoubleMethodV | Jdouble |
Que são idênticas as executadas pelos métodos de instância, porem recebem como parâmetro um jclass em vez de um jobject.
Acessando métodos de instancia de uma superclasse
Em JNI podemos obter a classe base de uma classe usando:
Jclass GetSuperClass(HNIEnv* env, jclass class);
Esta função retorna uma classe ou NULL se a classe for um java.lang.Object ou uma interface.
Se executarmos um método que foi redefinido sobre uma referencia do tipo base usando as funções Call
Para executar um método de base teremos que usar as funções:
CallNonvirtual
Protótipo:
<nativeType> CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args);
| CallNonvirtual<Tipo>Method | <TipoNativo> |
| CallNonvirtualVoidMethodV | void |
| CallNonvirtualObjectMethodV | Jobject |
| CallNonvirtualBooleanMethodV | Jboolean |
| CallNonvirtualByteMethodV | Jbyte |
| CallNonvirtualCharMethodV | Jchar |
| CallNonvirtualShortMethodV | Jshort |
| CallNonvirtualIntMethodV | Jint |
| CallNonvirtualLongMethodV | Jlong |
| CallNonvirtualFloatMethodV | Jfloat |
| CallNonvirtualDoubleMethodV | Jdouble |
Observe o exemplo das classes Java das listagens 8 e 9 e a implementação das funções nativas na listagem 10.
/* Base.java */ public class Base { return "Base"; } public native void metodoC(); Base base = new Derivada(); base.metodoC(); } static{ } }
Listagem 8: Código da classe Base
/* Derivada.java */ public class Derivada extends Base{ return "Derivada"; } }
Listagem 9: Código da classe Derivada
Observe que metodoJava() foi redefinido. Vamos provar chamando o objeto do tipo Derivada, usando a função CallObjectMethod() e CallNonvirtualObjectMethod().
/* base.cpp */ (...) JNIEXPORT void JNICALL Java_Base_metodoC(JNIEnv* env, jobject object){ //Primeiro passo obter o methodID da classe Base jclass class = (*env)->FindClass(env,"Base"); jmethodID methodID = (env*)->GetMethodID(env, class, "metodoJava","()Ljava/lang/String;"); //Agora executamos o metodo sobre o object que é uma referencia do tipo Base //que aponta para um objeto Derivada e executa o metodo Derivada jstring mensagem=(jstring)(*env)->CallObjectMethod(env, object, methodID); const jbyte* mensagem_c = (*env)->GetStringUTFChars(env, mensagem, NULL); (*env)->ReleaseStringUTFChars(env,mensagem, mensagem_c); //Agora vamos executar o método da Base mensagem = (jstring)(*env)->CallNonvirtualObjectMethod(env, object,class, methodID); mensagem_c = (*env)->GetStringUTFChars(env, mensagem, NULL); (*env)->ReleaseStringUTFChars(env,mensagem, mensagem_c); }
Listagem 10: Implementação da função nativa, que executa o método da classe Base
Obteremos como saída
A mensagem do metodoJava() é: Derivada
A mensagem do metodoJava() é: Base
Observe que é similar ao uso de super em Java, porem não é igual, pois em Java o super permite chamar os métodos de uma classe imediatamente da base, enquanto que com CallNonvirtual
Ex.: se temos:
Figura 3: Exemplo de hierarquia de classe
Tanto em JNI como em Java podemos chamar a partir de B os métodos de A, porem a partir de C não podemos executar os métodos de A usando super porem, podemos fazê-lo usando JNI.
Outra diferença é que podemos executar os métodos da classe base fora do objeto, coisa que não é possível fazer em Java usando super.
Resumindo em Java não podemos fazer:
A a = new B(); a.super.metodo();
Acessando um construtor
Acessar um construtor em JNI é igual ao acesso a qualquer outro método de instância a única diferença é que para acessarmos o construtor usamos “
E uma vez que tenhamos o methodID do construtor podemos passa-lo para a função:
NewObject
Protótipo:
jobject NewObject(JNIEnv *env, jclass clazz,jmethodID methodID, ...);
Que instância o objeto e executa o construtor especificado.
Existe outra forma de criar objetos Java, que consiste em usar a seguinte função:
AllocObject
Protótipo:
jobject AllocObject(JNIEnv *env, jclass clazz);
Esta função cria um objeto sem iniciá-lo e em seguida usa-se uma função CallNovirtual
Esta é uma forma menos elegante, pois obteríamos o objeto sem iniciá-lo e não poderíamos esquecer de chamar CallNovirtual
Como exemplo na listagem 11, vamos fazer um método nativo que cria um objeto Ponto e o retorna com os valores passados como parâmetros ao método nativo.
/* Ponto.java */ public class Ponto { private int x; private int y; public Ponto(int x, int y) { this.x = x; this.y = y; } @Override return "["+x+","+y+"]"; } public static native Ponto criaPonto1(int x, int y); public static native Ponto criaPonto2(int x, int y); } static{ } }
Listagem 11: Código da classe Ponto.java
Agora, na listagem 12, vamos implementá-lo com NewObject() e também com AllocObject().
/* ponto.cpp */ JNIEXPORT void JNICALL Java_PontocriaPonto1(JNIEnv* env, jclass class, jint x, jint y){ jmethodID methodID = (env*)->GetMethodID(env, class,"<init>","(II)V"); if(jmethodID==NULL) return NULL; return (*env)->NewObject(env,class,"<init>","(II)V"); } JNIEXPORT void JNICALL Java_PontocriaPonto2(JNIEnv* env, jclass class, jint x, jint y){ jobject obj; jmethodID methodID = (env*)->GetMethodID(env, class,"<init>","(II)V"); if(jmethodID==NULL) return NULL; obj = (*en)->AllocObject(env,class); (*env)->CallNonvirtualVoidMethod(env,obj,class, methodID,x,y); return obj; }
1 commentListagem 12: Código nativo ponto.cpp
1 Comment so far






Parabéns pelo material publicado.