Conheça a função JNI_OnLoad () em * .so

Reimpresso de: https://blog.csdn.net/GaoMatrix/article/details/6681374?locationNum=1&fps=1

Quando a VM Android (Máquina Virtual) executa a função System.loadLibrary () no componente C (ou seja, o arquivo * so), ela primeiro executa a função JNI_OnLoad () no componente C. Tem duas finalidades: 

1. Informe à VM qual versão JNI esse componente C usa. Se o seu arquivo * .so não fornecer a função JNI_OnLoad (), a VM usará como padrão o arquivo * .so para usar a versão JNI 1.1 mais antiga. Como a nova versão do JNI fez muitas extensões, se você precisar usar a nova versão dos recursos do JNI, como o java.nio.ByteBuffer do JNI 1.4, deverá notificar a VM por meio da função JNI_OnLoad ().

2. Conforme a VM executa a função System.loadLibrary (), ela chama imediatamente JNI_OnLoad (), para que o desenvolvedor do componente C possa usar JNI_OnLoad () para definir o valor inicial no componente C (Inicialização).

Por exemplo, no arquivo /system/lib/libmedia_jni.so do Android, a função JNI_OnLoad () é fornecida e o snippet de código é:

//#define LOG_NDEBUG 0

#define LOG_TAG "MediaPlayer-JNI"

………

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

  JNIEnv* env = NULL;

  jint result = -1;

  if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

    LOGE("ERROR: GetEnv failed\n");

    goto bail;
  }

  assert(env != NULL);

  if (register_android_media_MediaPlayer(env) < 0) {

    LOGE("ERROR: MediaPlayer native registration failed\n");

    goto bail;
  }

  if (register_android_media_MediaRecorder(env) < 0) {

    LOGE("ERROR: MediaRecorder native registration failed\n");

    goto bail;
  }

  if (register_android_media_MediaScanner(env) < 0) {

    LOGE("ERROR: MediaScanner native registration failed\n");

    goto bail;
  }


  if (register_android_media_MediaMetadataRetriever(env) < 0) {

    LOGE("ERROR: MediaMetadataRetriever native registration failed\n");

    goto bail;
  }

/* success -- return valid version number */

  result = JNI_VERSION_1_4;

bail:

  return result;
}

// KTHXBYE

Esta função retorna o valor de JNI_VERSION_1_4 para a VM, para que a VM saiba a versão JNI que usa. Além disso, ele também executa algumas ações iniciais (você pode chamar qualquer função local), como instruções:

if (register_android_media_MediaPlayer(env) < 0) {

  LOGE("ERROR: MediaPlayer native registration failed\n");

  goto bail;

}

Registre cada função nativa (Função Nativa) fornecida por este componente na VM para acelerar a eficiência das chamadas subsequentes para a função nativa.

A função JNI_OnUnload () corresponde a JNI_OnLoad (). Ao carregar o componente C, ele chamará imediatamente JNI_OnLoad () para realizar as ações iniciais no componente; e quando a VM liberar o componente C, chamará a função JNI_OnUnload () para realizar a ação de limpeza posterior. Quando a VM chama a função JNI_OnLoad () ou JNI_Unload (), ela passa o ponteiro da VM para eles. Os parâmetros são os seguintes

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

  ………
}


jint JNI_OnUnload(JavaVM* vm, void* reserved) {

  ………
}

Na função JNI_OnLoad (), o valor do indicador de JNIEnv é obtido através do indicador da VM e armazenado na variável do indicador env, como o seguinte comando:

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

  JNIEnv* env = NULL;

  jint result = -1;

  if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

    LOGE("ERROR: GetEnv failed\n");

    goto bail;

}

Porque a VM geralmente é um ambiente de execução multi-threading. Quando cada thread chama JNI_OnLoad (), o valor do índice JNIEnv passado é diferente. Para lidar com este ambiente multi-threaded, ao escrever funções locais, os desenvolvedores de componentes C podem evitar conflitos de dados de thread usando o valor de índice JNIEnv para garantir que as funções locais escritas possam ser executadas com segurança no Android em uma VM multi-threaded . Por este motivo, quando a função do componente C é chamada, o valor do indicador JNIEnv será passado para ele, da seguinte forma:

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

  JNIEnv* env = NULL;

  ……….

  if (register_android_media_MediaPlayer(env) < 0) {

  …….

  }
}

Quando JNI_OnLoad () chama a função register_android_media_MediaPlayer (env), ele passa o valor do indicador env. Desta forma, a função register_android_media_MediaPlayer () pode usar o valor do índice para distinguir diferentes threads a fim de resolver o problema de conflitos de dados.

Por exemplo, na função register_android_media_MediaPlayer (), você pode escrever as seguintes instruções:

if ((*env)->MonitorEnter(env, obj) != JNI_OK) {

  ………

}

 查看是否已经有其他执行绪进入此物件,如果没有,此执行绪就进入该物件里执行了。还有,也可撰写下述指令:

if ((*env)->MonitorExit(env, obj) != JNI_OK) {

  ………

}

Verifique se este thread está rodando neste objeto, se estiver, este thread sairá imediatamente.

PS: Como pode ser visto no artigo acima, na verdade, o arquivo so no Android é como uma DLL no Windows. As funções JNI_OnLoad e JNI_OnUnLoad são como o processo de PROCESS ATTATCH e DEATTATCH na DLL. Você também pode fazer algumas inicializações e ação de desinicialização.

Acho que você gosta

Origin blog.csdn.net/qq_37381177/article/details/111354596
Recomendado
Clasificación