Introducción a JNI y NDK en Android (1) La primera comprensión de NDK y JNI

1 NDK

El nombre completo de NDK es Native Develop Kit, traducido como un kit de desarrollo nativo. Le permite usar el código C / C ++ para Android para implementar funciones de aplicación. En otras palabras, además del SDK de Android, existe una herramienta llamada NDK para el desarrollo de C / C ++. En general, C / C ++ se compila en un archivo .co con la herramienta NDK y luego se llama en Java. NDK puede no ser adecuado para la mayoría de los principiantes de programación de Android, estos principiantes solo necesitan usar código Java y API de marco para desarrollar aplicaciones. Sin embargo, si necesita completar uno o más de los siguientes, entonces el NDK puede ser útil:

       Mejore la seguridad del código, porque el código de capa de Java es más fácil de descompilar, y la biblioteca es más difícil de descompilar.

       Reutilice las bibliotecas C / C ++ existentes.

       La biblioteca dinámica realizada por C / C ++ se puede trasplantar fácilmente entre otras plataformas

       Mejore aún más el rendimiento del dispositivo para lograr baja latencia o ejecutar aplicaciones computacionalmente intensivas como juegos o simulación física.

2 JNI

JNI significa Java Native Interface, o Java Native Interface. JNI es una característica de Java que llama lenguaje nativo, es una capa de interfaz encapsulada para la conveniencia de llamar a código local como Java y C / C ++.

3 Configurar el entorno NDK

El primer paso es descargar el NDK

Verifique el elemento NDK en las "Preferencias predeterminadas" de Android Studio para descargar el NDK. Después de que la descarga sea exitosa, puede ver el directorio NDK en la "Estructura del proyecto".

El segundo paso es configurar las variables de entorno.

Abra el "terminal" e ingrese el comando: echo $ PATH para ver las variables de entorno actuales

 Ingrese: sudo vi ~ / .bash_profile, presione Enter para ingresar la contraseña y luego use vi para abrir el archivo bash_profile en el directorio del usuario. Debe usar sudo, de lo contrario no tiene permiso para guardar archivos 

Presione i para comenzar a editar y complete la ruta NDK en su computadora hacia atrás

Después de editar, presione la tecla ESC, ingrese: wq, puede guardar y salir, si no desea guardar, ingrese: q

4 Hola mundo

Después de completar el trabajo de preparación, comencemos a crear una demostración JNI. La demostración es muy simple, es decir, Java normalmente se puede llamar en código C ++. Después de crear el proyecto, en circunstancias normales, verá el directorio donde se especifica ndk en el archivo local.properties; de lo contrario, debe agregar manualmente:

ndk.dir=/Users/liyizhi/Library/Android/sdk/ndk-bundle
sdk.dir=/Users/liyizhi/Library/Android/sdk

El primer paso es crear la clase JNIUtils, que ejecutará la carga del archivo de biblioteca y definirá un método nativo getInfo, y llamará al método getInfo en MainActivity:

JNIUtils.java

public class JNIUtils {
    static {
        System.loadLibrary("jni-demo");
    }
    public static native String getInfo();
}

MainActivity.java

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String info = JNIUtils.getInfo();
        TextView tv = findViewById(R.id.tv1);
        tv.setText(info);
    }
}

El segundo paso es ejecutar el comando y generar el archivo de encabezado .h. Como se muestra en la figura a continuación, se ejecutaron dos comandos, primero a través del comando: cd app / src / main / java salta a la carpeta java en el proyecto, y luego usa el comando: javac -h. Com / zyx / jnidemo / JNIUtils.java generado Archivo .h correspondiente. Aquí uso jdk1.8, si es la versión anterior de jdk, debe usar el comando: javah -jni com.zyx.jnidemo.JNIUtils .

El código de archivo de encabezado .h anterior debe explicarse

JINEXPORT y JNICALL : las macros definidas en JNI se pueden encontrar en el archivo de encabezado jni.h

Jstring : representa el tipo de retorno de cadena del método getInfo

Java_com_zyx_jnidemo_JNIUtils_getInfo : es el nombre de la función, el formato sigue las siguientes reglas: Java_package name_class name_method name

JNIEnv * : representa un puntero al entorno JNI, a través del cual puede acceder a los métodos de interfaz proporcionados por JNI

jclass : representa esto en un objeto Java

El tercer paso es crear una carpeta JNI y mover el archivo de encabezado com_zyx_jnidemo_JNIUtils.h a esta carpeta

El cuarto paso es crear 2 archivos en la carpeta jni recién creada: JNIUtils.cpp y Android.mk, su implementación es la siguiente:

JNIUtils.cpp

#include "com_zyx_jnidemo_JNIUtils.h"
#include <stdio.h>
#include <android/log.h>

JNIEXPORT jstring JNICALL Java_com_zyx_jnidemo_JNIUtils_getInfo(JNIEnv * env, jclass thiz) {
    __android_log_print(ANDROID_LOG_DEBUG, "zyx", "Hello world from JNI !");
    return env->NewStringUTF("Hello world from JNI !");
}

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := jni-demo
LOCAL_SRC_FILES := JNIUtils.cpp

LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

En el código anterior, LOCAL_MODULE indica el nombre del módulo, LOCAL_SRC_FILES indica el archivo fuente que necesita participar en la compilación y LOCAL_LDLIBS indica que la salida del registro es compatible con el código C ++.

El quinto paso es modificar build.gradle en el directorio de la aplicación, como se muestra en el siguiente código, consulte las notas para obtener instrucciones:

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.zyx.jnidemo"
        minSdkVersion 22
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        ndk {
            moduleName "jni-demo"                // so库的名称
            abiFilters "armeabi-v7a", "x86"      // 支持的cpu构架平台类型,all表示编译所有cpu平台
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        externalNativeBuild {
            ndkBuild {
                path 'src/main/jni/Android.mk'    // 指向Android.mk文件
            }
        }
    }
}

El sexto paso es ejecutar la compilación y la ejecución. En este momento, se generarán dos tipos de archivos en el directorio app / build / intermedtes / ndkBuild / debug / obj / local, donde los dos tipos corresponden al tipo de plataforma de arquitectura de CPU configurada en gradle. Luego ejecute el programa en el teléfono y verá que Java llamó con éxito al código C ++ y devolvió el resultado.

5 Referencia a la biblioteca externa

En el proceso de desarrollo real, el proyecto C ++ a menudo se separa del proyecto de Android, o el proyecto de Android hace referencia directa al archivo de biblioteca listo para usar provisto externamente. Ahora simulemos que esto está sucediendo.

Primero agregue otro archivo Application.mk en la carpeta jni, el código es el siguiente:

Application.mk

APP_ABI := armeabi-v7a,x86

Luego cambie al directorio padre del directorio jni y luego compile manualmente la biblioteca so mediante el comando: ndk-build. En este momento, el NDK creará un directorio libs que está al mismo nivel que el directorio jni. Las bibliotecas almacenan la biblioteca so.

Luego modifique el archivo Gradle:

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.zyx.jnidemo"
        minSdkVersion 22
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//        ndk {
//            moduleName "jni-demo"               // so库的名称
//            abiFilters "armeabi-v7a", "x86"     // 支持的cpu构架平台类型,all表示编译所有cpu平台
//        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
//        externalNativeBuild {
//            ndkBuild {
//                path 'src/main/jni/Android.mk'  // 指向Android.mk文件
//            }
//        }
        sourceSets.main {
            jni.srcDirs = []                      // 禁用自动NDK生成调用
            jniLibs.srcDirs = ['src/main/libs']   // so库存放目录
        }
    }
}

Permítanme explicar aquí, porque el código JNI debe colocarse en la carpeta jni creada anteriormente. Si no desea utilizar el nombre jni, puede especificar la ruta del código JNI a través de jin.srcDirs en Gradle. Debido a que el código JNI en el ejemplo anterior se coloca en la carpeta jni creada de manera predeterminada, debe agregar jni.srcDirs = [], de lo contrario no se compilará.

Finalmente , compile y ejecute nuevamente, puede ver que el programa que se ejecuta en el teléfono nuevamente puede mostrar el mismo resultado

Además, si hay un problema de compatibilidad con la versión anterior en el proceso de desarrollo real, puede intentar agregar la siguiente línea de código al archivo gradle.properties:

Android.useDeprecatedNdk=true 

 

 

Haga clic para descargar el ejemplo

 

 

 

106 artículos originales publicados · elogiados 37 · 80,000 visitas

Supongo que te gusta

Origin blog.csdn.net/lyz_zyx/article/details/88669229
Recomendado
Clasificación