Android JNI de 0 a 1 Tutorial de introducción (1)

Leí algunos tutoriales introductorios de JNI en Internet, que son muy poco amigables para los principiantes. Las personas que son fáciles de leer están confundidas y deciden escribir un tutorial introductorio de 0 a 1 yo solo.

Con respecto a JNI, Google también proporciona un tutorial introductorio. Para obtener más información, consulte: Tutorial introductorio de NDK

1. Introducción

JNI (Java Native Interface) es un marco de programación y una tecnología proporcionada por el lenguaje de programación Java, que se utiliza para llamar código nativo (normalmente escrito en C/C++) en aplicaciones Java para implementar funciones subyacentes e interactuar con sistemas operativos y hardware. JNI permite a los desarrolladores escribir código escrito en lenguajes nativos como C/C++ y luego interactuar con código Java a través de la interfaz JNI. Por ejemplo, el procesamiento común de audio y video, procesamiento de imágenes, mapas, etc. utilizará JNI.

2. NDK y CMake

El kit de desarrollo nativo (NDK) es un conjunto de herramientas que le permite usar código C y C++ en aplicaciones de Android y proporciona una serie de bibliotecas de plataforma que puede usar para administrar actividades nativas y acceder a componentes físicos del dispositivo, como sensores y toque entrar. .

Puede usar NDK en  Android Studio 2.2 o superior para compilar códigos C y C++ en bibliotecas nativas y luego usar Gradle, el sistema de compilación integrado de Android Studio, para empaquetar bibliotecas nativas en APK. Luego, el código Java puede llamar a las funciones en la biblioteca nativa a través del marco JNI.

La herramienta de compilación predeterminada de Android Studio para compilar bibliotecas nativas es  CMake . Dado que muchos proyectos existentes usan el kit de herramientas de compilación ndk-build, Android Studio también es compatible con  ndk-build . Sin embargo, si está creando nuevas bibliotecas nativas, debe usar CMake. CMake es una herramienta de compilación externa que funciona con Gradle para crear bibliotecas nativas.

Tanto ndk-build como CMake son herramientas para compilar código nativo. A partir de la versión 4.0 del complemento Android Gradle, Google recomienda usar CMake.

3. Android.mk, Aplicación.mk y CMakeLists.txt

CMakeLists.txty Android.mkson archivos de script de compilación que se utilizan para compilar y administrar código nativo en aplicaciones de Android.

CMakeLists.txt : es un archivo de configuración de CMake que describe el proceso de compilación del proyecto y la configuración de compilación requerida. En los proyectos de Android, el archivo CMakeLists.txt generalmente se encuentra appen el directorio raíz del módulo.

Un archivo CMakeLists.txt puede contener lo siguiente:

  • Define la versión mínima de CMake para compilar.
  • Declarar bibliotecas nativas para compilar.
  • Especifica un archivo de código fuente.
  • Configure las opciones de compilación y las bibliotecas de enlaces.
  • Define el nombre y las propiedades de la biblioteca compartida generada.
  • Configure la ruta de salida de compilación, etc.

Con CMake, puede configurar el compilador C/C++, la ruta de la biblioteca, los indicadores de compilación, etc. según las necesidades del proyecto, para generar un archivo de compilación adecuado para la plataforma de destino. Para obtener más métodos de uso, consulte Configuración de CMake

Android.mk : es un archivo de script de compilación en formato Makefile utilizado en aplicaciones de Android. Es un sistema de construcción basado en GNU Make para construir y administrar código nativo en aplicaciones. De forma predeterminada, se encuentra en el directorio del proyecto de la aplicación  jni/Android.mk en .

En los primeros días de las aplicaciones de Android, Android.mkera un archivo de script de compilación estándar que se usaba principalmente para compilar código nativo. Proporciona una forma de describir bibliotecas nativas y configuraciones de compilación.

Android.mkEl archivo suele estar ubicado en el directorio del proyecto Android jni, que contiene el siguiente contenido:

  • Define el nombre de la biblioteca nativa para compilar.
  • Especifica un archivo de código fuente.
  • Configure las opciones de compilación y las bibliotecas de enlaces.
  • Define el nombre y las propiedades de la biblioteca compartida generada.
  • Especifique la ruta de salida de la biblioteca compartida, etc.

Android.mkTambién puede usar variables y funciones predefinidas, así como la sintaxis de Makefile para controlar el proceso de compilación. Para obtener más métodos de uso, consulte Android.mk

Application.mk: especifica la configuración a nivel de proyecto para ndk-build. De forma predeterminada, se encuentra en el directorio del proyecto de la aplicación  jni/Application.mk en . Por lo general, aquí especificamos la versión ABI de la biblioteca nativa compilada, a saber, armeabi-v7a, arm64-v8a, x86, etc. Para obtener más configuraciones, consulte Application.mk

4. Tipo de datos

Como se usa C/C++, debe haber una diferencia en la conversión de tipos de datos. Por favor, aprenda los conocimientos básicos de C/C++ por sí mismo. Esta es la premisa del uso de JNI. Aquí hablaremos sobre la conversión de tipos de datos en JNI.

JNI (Java Native Interface) admite varios tipos de datos para la transferencia de datos y la conversión de tipos entre el código Java y el código nativo. Los siguientes son algunos tipos de datos JNI comunes:

  1. Tipos de datos básicos:

    • jboolean: tipo booleano, correspondiente a Java boolean.
    • jbyte: tipo de byte, correspondiente al de Java byte.
    • jchar: Tipo de carácter, correspondiente a Java char.
    • jshort: Entero corto, correspondiente a Java short.
    • jint: tipo entero, correspondiente al de Java int.
    • jlong: Entero largo, correspondiente a Java long.
    • jfloat: tipo de punto flotante de precisión simple, correspondiente a Java float.
    • jdouble: tipo de coma flotante de precisión doble, correspondiente a Java double.
  2. Tipo de referencia:

    • jobject: tipo de referencia de objeto general, correspondiente a Java Object.
    • jclass: Tipo de referencia de clase, correspondiente a Java Class.
    • jstring: tipo de cadena, correspondiente a Java String.
    • jarray: tipo de matriz, utilizado para representar objetos de matriz en Java.
    • jbooleanArray: tipo de matriz booleana, correspondiente a Java boolean[].
    • jbyteArray: tipo de matriz de bytes, correspondiente a Java byte[].
    • jcharArray: tipo de matriz de caracteres, correspondiente a Java char[].
    • jshortArray: tipo de matriz de enteros cortos, correspondiente a Java short[].
    • jintArray: Tipo de matriz entera, correspondiente a Java int[].
    • jlongArray: tipo de matriz de entero largo, correspondiente a Java long[].
    • jfloatArray: tipo de matriz de punto flotante de precisión simple, correspondiente a Java float[].
    • jdoubleArray: tipo de matriz de punto flotante de doble precisión, correspondiente a Java double[].
  3. Otros tipos:

    • jthrowable: tipo de excepción, utilizado para lanzar excepciones de Java.

En JNI, estos tipos de datos se utilizan para declarar los tipos de parámetros y valores de retorno de los métodos nativos.

V. Definición

1. Nombre del archivo:

Los archivos generados con las herramientas ndk-build o CMake suelen ser .so y la convención de nomenclatura es la siguiente:

lib+nombre de biblioteca.so, por ejemplo: libXXX.so XXX representa el nombre de la biblioteca específica

2. Denominación de funciones:

En JNI, la nomenclatura de funciones sigue reglas específicas para garantizar la correcta asignación e interacción entre el código Java y el código nativo. La convención para la denominación de funciones JNI se basa en el siguiente formato:

Java_package_ClassName_MethodName
  • JavaEl prefijo : indica que se trata de una función JNI.
  • package: indica el nombre del paquete de la clase Java, utilizando guiones bajos en lugar de puntos.
  • ClassName: Indica el nombre de una clase Java.
  • MethodName: Indica el nombre de un método Java.

Para las reglas de nomenclatura de funciones de JNI, hay algunos detalles que necesitan atención:

  • Los guiones bajos en los nombres de funciones de JNI  _ se utilizan para separar diferentes elementos para indicar relaciones de jerarquía y espacio de nombres.
  • Si la función JNI es un método estático, agregue un guión bajo antes del nombre del método  _.
  • Para las funciones JNI, la firma del tipo de valor devuelto y los tipos de parámetros deben coincidir con los del método Java. Las firmas denotan diferentes tipos con caracteres específicos.

Estos son algunos ejemplos que muestran cómo seguir las reglas de nomenclatura de funciones de JNI:

 
//Java 层代码JNIDemo.java
public class JNIDemo {
    static {
        System.loadLibrary("libjni");
    }
 
    public native String showLog();
}
 
 
//Native层代码 jnidemo.cpp
extern "C"
JNIEXPORT jstring JNICALL Java_com_example_jni_JNIDemo_showLog(JNIEnv* env, jobject job) {
    return env->NewStringUTF("hello world");
}

En el ejemplo anterior, asumimos que hay una com.example.jni.JNIDemoclase Java nombrada, que contiene un método llamado showLog , y la función Java_com_example_jni_JNIDemo_showLog de la capa Nativa corresponde al nombre del paquete + nombre de la clase + nombre del método de la capa Java , para mapear correctamente , cuando el nombre no es correcto, se producirá un error en tiempo de compilación.

3. "C" externo  :  Indica la compatibilidad entre el lenguaje C y C++.

4. JNIEXPORT y JNICALL son dos macros definidas en JNI para identificar el propósito de la función Las definiciones de estas dos macros son diferentes en diferentes entornos de sistema. En el entorno Android, se define de la siguiente manera

#define JNIIMPORT
#define JNIEXPORT  __attribute__ ((visibility ("default")))
#define JNICALL

JNIEXPORT se utiliza para indicar si la función se puede exportar (visibilidad de la función). En el lenguaje C ordinario, si desea limitar el uso de funciones o variables al archivo actual, debe agregarle una modificación estática. Pero si desea exponerlo al archivo especificado de la biblioteca compartida, debe controlarlo ocultando y mostrando el símbolo.
Después de GCC4.0, se proporciona la opción de visibilidad del símbolo -fvisibility=vis, vis puede ser el valor predeterminado predeterminado u oculto significa oculto.
El atributo de visibilidad del código correspondiente es __attribute__((visibility("default"))) o __attribute__((visibility("hidden")))
Para simplificar el formulario de salida del símbolo, puede simplificar su escritura a través de EXPORTAR. De la siguiente manera:
  #define EXPORT __attribute__((visibility("default")))
  EXPORT int Func();
Entonces JNIEXPORT puede considerarse como #define JNIEXPORT __attribute__((visibility("default"))) De esta manera, por supuesto, la implementación específica puede ser complicada Algunos, para juzgar diferentes compiladores, etc.

JNIEXPORT y JNICALL son definiciones vacías en el entorno Linux, por lo que estas dos macros se pueden omitir en la declaración de la función JNI en Linux.

5.jstring: el tipo de valor de retorno de la función.

6. JNIEnv* : es el primer parámetro de todas las funciones nativas y es un puntero a la tabla de funciones de JVM. Cada entrada en la tabla de funciones apunta a una función de JNI, y cada función se utiliza para acceder a una estructura de datos específica en la JVM.

7. jobject : representa una clase Java o una instancia de una clase Java que define una función nativa:

  • Si la función nativa es estática, representa la clase Objeto de clase
  • Si la función nativa no es estática, representa el objeto de instancia de la clase

6. Pasos de desarrollo

  1. Entorno de configuración: consulte instalar y configurar NDK y CMake

  2. Configure Android.mk, Application.mk o CMakeLists.txt.

  3. Escriba código nativo: escriba código nativo en C o C++, y estos códigos implementarán las funciones que desea llamar en Java.

  4. Cree un archivo de interfaz JNI: cree un archivo de interfaz JNI correspondiente al código nativo, que describe la interacción entre el código Java y el código nativo..h格式的头文件

  5. Implementar el método JNI: defina el método local que se llamará en el archivo de interfaz JNI. Debe implementar estos métodos y conectarlos con código nativo.

  6. Generar biblioteca nativa: use herramientas de desarrollo locales (como GCC, Clang, etc.) para compilar código nativo en una biblioteca compartida (como .soun archivo).

  7. System.loadLibrary("your-library-name")Cargar biblioteca nativa en Java: use el método para cargar la biblioteca nativa generada en código Java .

  8. Llame al método nativo: declare el método nativo en el código Java y llame al código nativo a través de la interfaz JNI

Presentaré ejemplos prácticos específicos en " Android JNI from 0 to 1 Getting Started Tutorial (2) "

Supongo que te gusta

Origin blog.csdn.net/gs12software/article/details/131529052
Recomendado
Clasificación