[Android -- JNI e NDK] Conheça o JNI

prefácio

  • No ecossistema Android, existem principalmente três linguagens: C/C++, Java e Kotlin, cuja relação não é de substituição, mas de complementaridade. Dentre eles, o contexto do C/C++ é algoritmo e alto desempenho, o contexto do Java é independente de plataforma e gerenciamento de memória, e o Kotlin combina as excelentes características de múltiplas linguagens, trazendo um método de programação mais moderno;

  • JNI é um recurso que realiza a interação entre o código Java e o código C/C++. Pense sobre uma questão: como a máquina virtual Java realiza a interação entre duas linguagens irrelevantes? Hoje, vamos resumir de forma abrangente a estrutura de conhecimento de desenvolvimento JNI e estabelecer as bases para o desenvolvimento do NDK.

Conheça o JNI

1. Por que usar JNI?

JNI (Java Native Interface, Java Local Interface) é um recurso do ecossistema Java, que estende os recursos da máquina virtual Java, permitindo que o código Java interaja com o código C/C++. Por meio da interface JNI, o código Java pode chamar o código C/C++ e o código C/C++ também pode chamar o código Java.

2. O processo básico de desenvolvimento do JNI

Um processo de desenvolvimento JNI padrão inclui principalmente as seguintes etapas:

  • 1. Crie HelloWorld.javae declare o método nativo sayHi();
  • 2. Use o comando javac para compilar o arquivo fonte e gerar um arquivo HelloWorld.classbytecode ;
  • 3. Use o comando javah para exportar HelloWorld.ho arquivo de cabeçalho (o arquivo de cabeçalho contém o protótipo da função do método local);
  • 4. Implemente o protótipo da função HelloWorld.cppno ;
  • 5. Compilar código local e gerar arquivos Hello-World.sodinâmicos de biblioteca nativa;
  • 6. Chame System.loadLibrary(…) no código Java para carregar o arquivo so;
  • 7. Use o comando Java para executar o programa HelloWorld.

código de modelo JNI

JNIEXPORT void JNICALL Java_com_xurui_hellojni_HelloWorld_sayHi (JNIEnv *, jobject);

1. Nome da função JNI

Por que o nome da função JNI Java_com_xurui_HelloWorld_sayHiadota o método de nomenclatura? —— Esta é a regra de nomenclatura de função da convenção de registro estático de função JNI. Há um relacionamento de mapeamento um-para-um entre métodos nativos Java e funções JNI, e há dois métodos de registro para estabelecer esse relacionamento de mapeamento: registro estático + registro dinâmico.

Entre eles, o registro estático é um relacionamento de mapeamento estabelecido com base em convenções de nomenclatura, e a função JNI correspondente a um método nativo Java usará o nome de função acordado, ou seja, Java_[类的全限定名 (带下划线)]_[方法名].

2. Palavras-chave JNIEXPORT

JNIEXPORTÉ uma definição de macro, indicando que uma função precisa ser exposta ao uso externo da biblioteca compartilhada. JNIEXPORT é definido de forma diferente no Windows e no Linux:

// Windows 平台 :
#define JNIEXPORT __declspec(dllexport)
#define JNIIMPORT __declspec(dllimport)

// Linux 平台:
#define JNIIMPORT
#define JNIEXPORT  __attribute__ ((visibility ("default")))

3. Palavra-chave JNICALL

JNICALLÉ uma definição de macro, indicando que uma função é uma função JNI. JNICALL é definido de forma diferente no Windows e no Linux:

// Windows 平台 :
#define JNICALL __stdcall // __stdcall 是一种函数调用参数的约定 ,表示函数的调用参数是从右往左。

// Linux 平台:
#define JNICALL

4. O parâmetro jobject

O tipo jobject é a representação da camada JNI do objeto de tipo de aplicativo da camada Java. Todo método nativo chamado de Java passa uma referência ao objeto atual na função JNI. Distinguem-se dois casos:

  • 1. Método nativo estático : O segundo parâmetro é o tipo jclass, apontando para o objeto Class da classe onde o método nativo está localizado;
  • 2. Método nativo da instância : O segundo parâmetro é o tipo jobject, apontando para o objeto que chama o método nativo.

5. O papel de JavaVM e JNIEnv

JavaVMe JNIEnvsão as duas estruturas de dados mais críticas definidas no arquivo de cabeçalho jni.h:

  • JavaVM : representa a máquina virtual Java.Cada processo Java possui e possui apenas um objeto JavaVM global, e o JavaVM pode ser compartilhado entre threads;
  • JNIEnv : representa o ambiente de tempo de execução Java, cada encadeamento Java tem seu próprio objeto JNIEnv independente e JNIEnv não pode ser compartilhado entre encadeamentos.

As definições de tipo de JavaVM e JNIEnv são ligeiramente diferentes em C e C++, mas essencialmente as mesmas, consistindo internamente em uma série de ponteiros de função apontando para o interior da máquina virtual.

exemplo

1. Crie HelloWorld.javae declare o método nativo hello_world();

public class JNIExample {
    
    

    static {
    
    
        // 函数System.loadLibrary()是加载dll(windows)或so(Linux)库,只需名称即可,
        // 无需加入文件名后缀(.dll或.so)
        System.loadLibrary("JNIExample");
        init_native();
    }

    private static native void init_native();

    public static native void hello_world();

    public static void main(String...args) {
    
    
        JNIExample.hello_world();
    }
}

2. Use o comando javac para compilar o arquivo fonte e gerar um arquivo HelloWorld.classbytecode ;

javac com/kevin/jni/JNIExample.java
javah com.kevin.jni.JNIExample

3. Use o comando javah para exportar HelloWorld.ho arquivo de cabeçalho (o arquivo de cabeçalho contém o protótipo da função do método local);

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_kevin_jni_JNIExample */

#ifndef _Included_com_kevin_jni_JNIExample
#define _Included_com_kevin_jni_JNIExample
#ifdef __cplusplus
extern "C" {
    
    
#endif
/*
 * Class:     com_kevin_jni_JNIExample
 * Method:    init_native
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_kevin_jni_JNIExample_init_1native
  (JNIEnv *, jclass);

/*
 * Class:     com_kevin_jni_JNIExample
 * Method:    hello_world
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_kevin_jni_JNIExample_hello_1world
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

4. Implemente o protótipo da função HelloWorld.cppno ;

void init_native(JNIEnv *env, jobject thiz) {
    
    
    printf("native_init\n");
    return;
}

void hello_world(JNIEnv *env, jobject thiz) {
    
    
    printf("Hello World!");
    return;
}

static const JNINativeMethod gMethods[] = {
    
    
        {
    
    "init_native", "()V", (void*)init_native},
        {
    
    "hello_world", "()V", (void*)hello_world}
};

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    
    
    __android_log_print(ANDROID_LOG_INFO, "native", "Jni_OnLoad");
    JNIEnv* env = NULL;
    if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) // 从 JavaVM 获取JNIEnv,一般使用 1.4 的版本
        return -1;
    jclass clazz = env->FindClass("me/shouheng/jni/JNIExample");
    if (!clazz){
    
    
        __android_log_print(ANDROID_LOG_INFO, "native", "cannot get class: com/example/efan/jni_learn2/MainActivity");
        return -1;
    }
    if(env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])))
    {
    
    
        __android_log_print(ANDROID_LOG_INFO, "native", "register native method failed!\n");
        return -1;
    }
    return JNI_VERSION_1_4;
}

5. Compilar código local e gerar arquivos Hello-World.sodinâmicos de biblioteca nativa;

gcc -c -I"E:\JDK\include" -I"E:\JDK\include\win32" jni/JNIExample.c

6. Chame System.loadLibrary(…) no código Java para carregar o arquivo so;

gcc -Wl,--add-stdcall-alias -shared -o JNIExample.dll JNIExample.o

7. Use o comando Java para executar o programa HelloWorld.

Acho que você gosta

Origin blog.csdn.net/duoduo_11011/article/details/131299772
Recomendado
Clasificación