Cómo escribir métodos jni
make
cd bin
.
1 Cómo llamar funciones en C/c++ estándar, tales. como printf (...)
2. Cómo llamar a funciones personalizadas en C/c++
3. Cómo acceder a campos de instancia de objeto en clases java en funciones jni
4. Cómo acceder a campos de instancia estática en clases java en funciones jni
5. Cómo llamar a métodos de objetos java en funciones jni
6. Cómo acceder a instancia estática campos en funciones jni Método de llamar a un objeto java en una función jni
6. Cómo llamar a un objeto java en una función jni
7. La función jni llama al método estático de una clase java
7. Cómo pasar parámetros de tipo de datos básicos en la función jni
8. Cómo pasar parámetros de tipo de objeto en la función jni
9. Cómo usar cadenas en la función jni p>
10. Cómo usar matrices en funciones jni
11. Manejar valores de retorno en funciones jni
12. Implementar la creación de objetos de clase java en jni
2. Pasos básicos:
Después de presentar estos ejemplos, echemos un vistazo a los pasos básicos necesarios para escribir el método jni. Todos estos ejemplos se explican en c. c++ en los ejemplos La diferencia con c en el ejemplo no es grande, solo modifique ligeramente el método jni al final del documento. En cuanto al ejemplo de C++, no es muy diferente del ejemplo de C++. Solo necesita una ligera modificación. Al final del documento, presentaremos estos contenidos:
1. el método jni, lo primero que hay que hacer es declarar este método en java (naturalmente, este proceso de declaración debe realizarse en la clase)
El formato de declaración es el siguiente:
publicnativevoid print(); System.loadLibrary("JNIExamples"); }
Las funciones JNI se declaran utilizando el método nativo de palabra clave.
2. Utilice el comando javac para compilar el archivo fuente de la clase y generar el archivo .class correspondiente.
3. Utilice javah -jni para generar un código auxiliar de conversión para la función entre la llamada de Java y la función C real. El código auxiliar obtiene la información de los parámetros de la pila de la máquina virtual y la pasa a la función C compilada. para implementar la conversión.
4. Cree una biblioteca compartida especial y cargue este código auxiliar desde cualquier lugar de la biblioteca compartida. En el ejemplo anterior, System.loadLibrary se usa para cargar la biblioteca libJNIExamples ***.
3. Configurar el entorno de ejecución:
Antes de escribir una función jni simple, debemos configurar el entorno de ejecución correspondiente. La configuración de jdk no se presenta aquí, el enfoque aquí es la ruta de la biblioteca. Al llamar a System.loadLibrary (...), el compilador buscará la biblioteca en la ruta de la biblioteca establecida por el sistema. Modificar la ruta es básicamente lo mismo que modificar cualquier variable de entorno, simplemente agregue una línea LD_LIBRARY_PATH= al directorio /etc/bash.bashrc. /lib:$(LD_LIBRARY_PATH). También puede exportar
LD_LIBRARY_PATH=. :./lib:$(LD_LIBRARY_PATH)
4. Ejecute un análisis de muestra:
1. Llame a la función printf() que viene con el estándar c:
A continuación se toma el Ejemplo 1 como ejemplo para explicar el proceso detallado de escritura del método jni.
(1) Defina la clase Print.java que contiene la función jni:
{
/*************** * ************************************************* ** *******
* La función print() llamará a la función printf(), que es la función ANSI c
*
************************************************* **** *******************/publicnativevoid
print();
System.loadLibrary("JNIExamples" ); }}
En el ejemplo anterior, el método jni de la clase Print se define utilizando la declaración pública nativa void
print(); Y utilice la instrucción Sysgem.loadLibrary("JNIExamples") para cargar la biblioteca libJNIExamples.so. Nota: La declaración de carga debe declararse en un bloque de código estático con la palabra clave estática para garantizar que la biblioteca siempre esté cargada cuando se hace referencia a la clase.
(2). Clase compilada: javac Print.java. Genere la clase Print.class y luego use javah para generar el archivo de encabezado Print.h: javah Print. El archivo Print.h tiene el siguiente formato:
/* No edite este archivo, está generado por una máquina *//*.
La fuente en negrita es la parte vital de la función JNI a implementar.
(3).Escribir la implementación de la función JNI Print.c
JNIEXPORT void JNICALL Java_Print_print (JNIEnv *env, jobject obj) {
printf("). ejemplo1: en este ejemplo, una función printf() en ANSI C se
llamada\n"); printf("Hola, la salida es generada por la función printf()
en ANSI C \n"); }
Una función JNI simple.
En este archivo se implementa un método Jni simple. Este método llama a la función printf() en ANSI C e imprime dos oraciones.
(4), la función local se compilará en la biblioteca libJNIExamples.so:
Declaración de uso: gcc -fPIC -I/usr/jdk1.5/include -I/ usr /jdk1.5/include/linux -shared -o libJNIExamples.so Print.c.
(5), para que la función Jni esté completamente implementada y pueda llamarse en código java.
Aquí usamos una clase simple para probar la implementación del método jni. La siguiente es la parte del código fuente de PrintTest.java:
publicstaticvoid main(String[] args) {. Print p = new Print(); p.print(); }}
(6), compila y ejecuta PrintTest.java y obtiene los siguientes resultados:
Ejemplo 1: En En este ejemplo, la función printf() en ANSI C
Hola, la salida es generada por la función printf() en ANSI C.
Los pasos de implementación para cada ejemplo que se describe a continuación son los mismos. Por lo tanto, sólo cubriremos las partes clave de la implementación.
2. Ejemplo 2, llamando a una función definida por el usuario en lenguaje C (el programa fuente es: java/Cfunction.java java/C_functionTest.java src/Cfunction.c src/Cfunction.hh) p>
Cuando necesita llamar a una función implementada por una función c en un programa java, debe definir un método jni en la clase que necesita llamar a la función c y llamar a la función c en el método jni, que es equivalente a La función c está encapsulada en un método java para que la llame el programa java.
En el ejemplo 2, simplemente definimos una función printHello(). La función de esta función es solo generar una oración. Si desea llamar a esta función en el programa Java, solo necesita llamar. función en la función jni Eso es todo, no hay diferencia con llamar a la función prinf() que viene con ANSI C.
3. Ejemplo 3, acceda al campo de instancia del objeto de clase java en la función jni (el programa fuente es: java/CommonField.java java/CommonFieldTest.java src/CommonField.c src/CommonField. h)
4. Ejemplo 5, acceda al campo de instancia del objeto de clase java en la función jni. h )
La parte de implementación de la función jni se implementa en lenguaje c. Si desea acceder a los campos de instancia de los objetos de clase definidos en java, debe realizar tres pasos:
(1) Llame a la función GetObjectClass() para obtener la clase del par, que devuelve un valor de tipo jclass.
(2) Llame a la función GetFieldID() para obtener la identificación del campo de instancia al que se accederá en la clase.
(3) Llame a la función GetXXXField() para obtener el valor del campo de instancia al que se accederá. donde XXX corresponde al tipo de campo de instancia al que se accederá.
En jni, la relación correspondiente entre el lenguaje de programación Java y el tipo de datos del lenguaje C es que el tipo de datos original de Java está precedido por 'j', que corresponde al tipo de datos del lenguaje C, como booleano. es jboolean, int es jint, double es jdouble, etc. El tipo correspondiente al tipo de objeto es jobject.
En este ejemplo, puede ver que definimos la clase CommonField en java/CommonField.java, que contiene dos instancias de campo int a e int b
Esperamos obtener CommonField () en la función jni para acceder y modificar estos dos campos. Puede encontrar la implementación de esta función en
src/CommonField.c.
La siguiente declaración accederá al campo (el siguiente código está tomado de: src/CommonField.c):
jclass class_Field = (*env)->GetObjectClass(env,obj ); jfieldID fdA =
(*env) )jint valueA =
(*env )->GetObjectClass(env,obj,fdA); p>(*env )->GetFieldID(env,class_Field, "b", "I"); valor jintA =
(*env )->GetIntField(env,obj,fdA valorB); =
(*env)->GetIntField(env,obj,fdB);
En jni, el puntero env se usa para todas las llamadas a funciones jni y también es el primer parámetro de cada método nativo. Este puntero también es el primer parámetro de cada método local. Es un puntero a la tabla de punteros de función. Por lo tanto, este puntero debe agregarse delante de cada llamada de función jni (*env)->GetObjectClass(env,obj). para devolver el tipo de imagen obj, donde el parámetro obj
indica el tipo de objeto de clase que desea obtener.
jfieldID GetFieldID(JNIEnv *env,jclass cl, const char name[], const char
sig[]) Esta función devuelve el identificador del campo, name representa el nombre de dominio y sig representa la firma del dominio codificado. La llamada firma de codificación es la firma del tipo de codificación. En el ejemplo anterior, el campo de una instancia de una clase es de tipo int, representado por "I". representa bytes y "C" representa caracteres, "D" y "B" representan bytes, "C" representa caracteres, "D" representa doble, "F"
representa flotante, "J" representa largo, "S" representa corto, "V" significa vacío. En resumen, "V" significa nulo, "Z" significa tipo booleano.
GetIntField(env,obj,fdA) se usa para acceder al campo fdA del objeto obj. Si el campo al que se accederá es de tipo doble, use GetDoubleField(env,obj,fdA) para acceder a él. , es decir, el tipo corresponde a GetXXXField XXX en.
Las siguientes funciones se utilizan para modificar los valores de los campos:
(*env)->SetIntField(env,obj,fdA,109); , obj,fdB,145);
Esto es similar a obtener el valor del campo, excepto que la función tiene un parámetro de valor más.
Las funciones relacionadas para acceder al campo de instancia del objeto son las siguientes:
jfieldID GetFieldID(JNIEnv *env, jclass cl, const char name[], const char sig[])
Esta función devuelve el identificador del dominio. El significado de los parámetros es el siguiente:
puntero de interfaz JNI env; objeto de clase cl; nombre nombre de dominio; firma de dominio de codificación sig
XXX GetXXXField(JNIEnv *env, objeto de trabajo, jfieldID id)
p>
Esta función devuelve el valor del dominio. El tipo de campo XXX es Object, Boolean, byte, char, short, int, long, float y double.
Puntero de interfaz JNI del parámetro env; obj es el objeto donde se encuentra el dominio; id es el identificador del dominio.
void SetXXXField(JNIEnv *env,jobject obj, jfieldID id, XXX value)
Esta función se utiliza para establecer el valor del campo. El significado de XXX es el mismo que el anterior.
Los significados de los parámetros env, obj e id son los mismos que los anteriores y el valor es el valor que se establecerá.
4. Ejemplo 4: Acceder a la instancia estática de la clase de campo en la función jni (java/Field.java java/FieldTest.java src/Field.c src/Field.h)
Debido a que la instancia estática de un campo no pertenece a un objeto, sino a una clase, cuando desea acceder a la instancia estática del campo, es diferente de acceder a la instancia de campo del objeto.
Dado que los campos de instancia estáticos no pertenecen a un objeto sino a una clase, cuando se desea acceder a un campo de instancia estático, es diferente a acceder a un campo de instancia de un objeto. La función que llama también lo es. diferente (para ilustrar el caso de uso 4, aquí está el código extraído de src/Field.c):
Los campos de instancia estática no pertenecen a un objeto, sino a una clase, por lo que cuando Al acceder a datos estáticos campos de instancia, es diferente de acceder a los campos de instancia de un objeto. c):
jclass class_Field = (*env)->FindClass(env, "Field"); jfieldID fdA =
(* env)->GetStaticFieldID(env,class_Field, "a", "I"); valor jintA =
(*env)->GetStaticIntField(env,class_Field,fdA); env,class_Field,fdA,111);
Debido a que no hay ningún objeto, debe usar FindClass en lugar de GetObjectClass para obtener la referencia de clase. El segundo parámetro en FindClass () es la firma de codificación de la clase. La firma de codificación de la clase es diferente del tipo básico. Si la clase está en el paquete actual, es directamente el nombre de la clase. no está en el paquete actual, se debe agregar la ruta detallada de la clase: por ejemplo, la clase String está en el paquete java.lang y la firma de codificación de String es "..." en lugar de "..." . Por ejemplo, la clase String está en el paquete java.lang y la firma de String debe escribirse como (
Ljava/lang/String;), donde (L y;) son indispensables, y ( ;) es un carácter de fin de expresión. Las otras tres funciones básicamente no son diferentes de acceder a los campos de datos del objeto.
5. Ejemplo 5: Llame al método del objeto java en la función jni (java/CommonMethod.java java/CommonMethodTest.java src/CommonMethod.c src/CommonMethod.h)
En la función jni, no solo necesitamos acceder a los campos de datos del objeto java, sino que a veces también necesitamos llamar a los métodos que se han implementado en el objeto de clase en java. El ejemplo 5 trata sobre la implementación de este aspecto.
En src/CommonMethod.c, podemos encontrar el siguiente código:
JNIEXPORT void JNICALL Java_CommonMethod_callMethod (JNIEnv *env,
jobject obj, jint a, jstring s) { printf(" ejemplo 5: En este
ejemplo, se llamará al método de un objeto (n"); jclass
class_CommonMethod = (*env)->GetObjectClass (env,obj); jmethodID
md =
(*env)->GetMethodID(env,class_CommonMethod, "print","(ILjava/Lang/String;)V"); p> (*env)->CallVoidMethod(*env)->CallVoidMethod(*env)->GetObjectClass >CallVoidMethod(env,obj,md,a,s);}
Esta parte del código Muestra cómo implementar el proceso de llamar a funciones de objetos de clase Java. En la parte del código anterior, podemos ver que se requieren tres pasos para implementar la llamada, es decir, llamar a tres funciones.
jclass class_CommonMethod = (. *env)-> GetObjectClass(env,obj);
jmethodID md =
(* env)->GetMethodID(env,class_CommonMethod, "print","(ILjava/lang /String;)V ");
(*env)->CallVoidMethod(env,obj,md,a,s);
GetObjectClass(. ...) La función obtiene la clase. La función obtiene la clase requerida del objeto que llama; GetMethodID(...) obtiene el número de identificación del método que se llamará en relación con la clase CallXXXMethod(...) llama al método. >
Al escribir el proceso de llamada, aún debe prestar atención a lo siguiente. Problema de firma de codificación en la función GetMethodID(...) En este ejemplo, busque print(int
a. , String s) método de la clase CommonMethod, que imprimirá el entero a y la cadena s
Straight. En la parte de la firma del código de la función (negrita y subrayada), GetMethodID(env,class_CommonMethod, "print","(ILjava/lang/String;)V");
se puede ver de izquierda a derecha. Bien, el contenido entre paréntesis es el contenido de la parte del parámetro del método que se llamará. I indica que el tipo del primer parámetro es int, "Ljava/lang/String; " indica que el tipo del segundo parámetro es String y V indica que el tipo de valor de retorno es nulo y vacío si el tipo de valor de retorno no es. nulo, utilice la firma de tipo correspondiente. El tipo de valor de retorno está asociado con la función CallXXXMethod(...) que se utilizará para llamar al método. Cuando se asocia con la función CallXXXMethod(...) que se usará para llamar al método, xxx en la función debe reemplazarse con el tipo correspondiente, en este caso void, si el tipo de valor de retorno es tipo int, entonces la función de el método se llama para CallIntMethod(...).
6. Ejemplo 6: Llame al método estático de la clase java (java/Method.java java/MethodTest.java src/Method.h src/Method.c) en la función jni. c)
El ejemplo 5 presenta cómo llamar a métodos de objetos de clase. En este ejemplo, presentaremos cómo llamar a métodos estáticos de una clase Java. En este ejemplo, tenemos un método estático definido en /java/Method.java:
public static void print() {
System.out.println("este es un método estático método de la clase Method");
}
Esta función imprime la cadena "este es un método estático de la clase Method";
Estamos en src/ Hemos implementado una función jni que llama a este método en src/Method.c. Llama al método función jni en src/Method.c:
JNIEXPORT void JNICALL Java_Method_callMethod (JNIEnv *env, jobject
obj) { printf("ejemplo 6: En este ejemplo, el método estático
de la clase será Call/n"); jclass class_Method =
(*env) -> FindClass(env, "Method"); jmethodID md =
(*env)-> GetStaticMethodID(env ,class_Method, "print","()V");
(*env)->CallStaticVoidMethod(env,class_Method,md); }
No tiene nada que ver con "método".
A diferencia del Ejemplo 5, las tres funciones llamadas se convierten en:
FindClass(...) , GetStaticMethodID(...) , CallStaticVoidMethod(...) .
La mecánica implicada es la misma que en el Ejemplo 5. Nuevamente, no es necesario entrar en demasiados detalles.
7. Ejemplo 7: La función jni que pasa parámetros de tipo de datos básicos (java/Basic.java java/BasicTest.java src/Basic.c
src/Basic.h) está en En java/Basic.java, definimos una función pública nativa void riseValue(int
a), que imprimirá el valor que aumenta el valor en a, e imprimirá el valor original y el nuevo valor. .
La implementación de la función jni se proporciona en src/Basic.c.
JNIEXPORT void JNICALL Java_Basic_raiseValue (JNIEnv *env, jobject
obj, jint a) { printf("ejemplo 7: en este ejemplo, se pasará un tipo entero al método jni
Parámetros"); jclass class_Basic =
(*env)->GetObjectClass(env,obj); jfieldID
fd = (*env)-> GetFieldID (env,class_Basic, "valor", "I"); jint v =
(*env)->GetIntField(env,obj,fd);
(*env)->SetIntField(env,obj,fd,v); }
En este ejemplo, usamos "...".
En esta implementación de función, debido a que necesitamos acceder al campo de valor en la clase Básica, llamamos a GetObjectClass(...), GetFieldID(...),
GetIntField( ..) para obtener el valor, los siguientes pasos " = v+a;
" indican que pasar parámetros de tipo primitivo se maneja de la misma manera que se manejan los tipos de datos primitivos en el lenguaje C.