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.