JNI完全ガイド(十)-JavaVMとJNIEnv

8866 ワード

ラベル(スペース区切り):JNI完全ガイド
  :1
  :   
  :    ,    

公開先:ジョブ部落、CSDNブログ
下一篇:JNI完全ガイド
[toc]
十、JavaVMとJNIEnv
10.1 JNIEnv
JNIEnvタイプは、すべてのJNIメソッドを指すポインタです.このポインタは、作成したスレッドにのみ有効であり、スレッド間で渡すことはできません.次のように宣言されます.
struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif

この部分だけから分かるように,C言語環境とC++言語環境でのJNIEnvの実装は異なる.すなわち,C言語とC++言語ではJNIメソッドの呼び出しに違いがある.C環境でのメソッドの宣言方法は、次のとおりです.
struct JNINativeInterface {
    ...
    jint        (*GetVersion)(JNIEnv *);
    ...
};

そこで、この方法を使用しています.
    jint version = (*env)->GetVersion(env);

C++の場合、ここではパッケージ化され、
struct _JNIEnv {
    const struct JNINativeInterface* functions;

#if defined(__cplusplus)

    jint GetVersion()
    { return functions->GetVersion(this); }
    
    ...
    
#endif /*__cplusplus*/
};

ここで、C++環境の場合、ここでの使用方法は以下の通りであることがわかる.
    jint version = env->GetVersion();

残りの方法はCとC++の実装の違いについて基本的にそうである.現在のJNIバージョンは、次の方法で入手できます.
jint GetVersion(JNIEnv *env);

ここでの戻り値はマクロ定義の定数です.取得した値を次のマクロと一致させて、現在のバージョンを知ることができます.
#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006

10.2 JavaVM
JavaVMは仮想マシンのJNIでの表現であり、1つのJVMに1つのJavaVMオブジェクトしかなく、このオブジェクトはスレッド共有されている.
JNIEnvでは、Java仮想マシンオブジェクトを次のように取得できます.
jint GetJavaVM(JNIEnv *env, JavaVM **vm);
  • vm:取得した仮想マシンのポインタを格納するためのポインタ.
  • return:成功は0を返し、失敗は他を返します.

  • C言語環境におけるJNIにおけるJVMの宣言を見てみましょう.
    /*
     * JNI invocation interface.
     */
    struct JNIInvokeInterface {
        void*       reserved0;
        void*       reserved1;
        void*       reserved2;
    
        jint        (*DestroyJavaVM)(JavaVM*);
        jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
        jint        (*DetachCurrentThread)(JavaVM*);
        jint        (*GetEnv)(JavaVM*, void**, jint);
        jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
    };
    

    JNIでのJVM操作の宣言は次のとおりです.
    jint JNI_GetDefaultJavaVMInitArgs(void*);
    jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
    jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
    
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
    

    10.2.1 JVMの作成
    一般に、JNI_CreateJavaVMを呼び出してJVMを作成するスレッドは、プライマリスレッドと呼ばれる.理論的には、この方法ではユーザ呼び出しは許可されません.
    jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);
  • p_vm:作成した仮想マシンのポインタを保存します.
  • p_Env:取得したJNIEnvオブジェクトのポインタを保存します.
  • vm_Args:初期化パラメータを設定するJavaVMInitArgsタイプのポインタ.
  • return:作成成功JNI_に戻るOK、失敗は他に戻ります.

  • ここでJavaVMInitArgsは、仮想マシンのパラメータを格納する構造体であり、以下のように定義されています.
    typedef struct JavaVMOption {
        const char* optionString;
        void*       extraInfo;
    } JavaVMOption;
    
    typedef struct JavaVMInitArgs {
        jint        version;
        jint        nOptions;
        JavaVMOption* options;
        jboolean    ignoreUnrecognized;
    } JavaVMInitArgs;
    

    次の例では、作成プロセスを示します.
        JavaVMInitArgs vm_args;
        JavaVMOption options[4];
        
        options[0].optionString = "-Djava.compiler=NONE";           /* disable JIT */
        options[1].optionString = "-Djava.class.path=c:\myclasses"; /* user classes */
        options[2].optionString = "-Djava.library.path=c:\mylibs";  /* set native library path */
        options[3].optionString = "-verbose:jni";                   /* print JNI-related messages */
        
        vm_args.version = JNI_VERSION_1_2;
        vm_args.options = options;
        vm_args.nOptions = 4;
        vm_args.ignoreUnrecognized = TRUE;
        
        /* Note that in the JDK/JRE, there is no longer any need to call
         * JNI_GetDefaultJavaVMInitArgs.
         */
        res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
        if (res < 0) ...
    

    10.2.2仮想マシンへのリンク
    JNIEnvポインタは、作成したスレッドでのみ有効です.他のスレッドでJVMにアクセスする必要がある場合は、現在のスレッドをJVMに関連付けるには、AttachCurrentThreadを呼び出してからJNIEnvオブジェクトを取得する必要があります.もちろん、必要に応じてDetachCurrentThreadを呼び出してリンクを解除する必要があります.
    jint AttachCurrentThread(JavaVM* vm , JNIEnv** env , JavaVMAttachArgs* args);
  • vm:仮想マシンオブジェクトポインタ.
  • env:得られたJNIEnvのポインタを保存します.
  • args:リンクパラメータ、パラメータ構造体を以下に示す.
  • return:リンクは正常に0を返し、接続に失敗して他のものを返します.
  • struct JavaVMAttachArgs {
        jint        version;    /* must be >= JNI_VERSION_1_2 */
        const char* name;       /* NULL or name of thread as modified UTF-8 str */
        jobject     group;      /* global ref of a ThreadGroup object, or NULL */
    };
    

    10.2.3仮想マシンとの接続を解除する
    次の関数を使用して、現在のスレッドと仮想マシンのリンクを解除します.
    jint DetachCurrentThread(JavaVM* vm);
    10.2.4仮想マシンのアンインストールJNI_DestroyJavaVM関数を呼び出すと、現在使用されている仮想マシンがアンインストールされます.
    jint DestroyJavaVM(JavaVM* vm);
    10.2.5動的ローディングローカルメソッド
    JNIには特殊な関数のセットがあります.
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
    

    この関数のセットの役割はJavaメソッドとローカルC関数のリンクを担当することです.たとえば、Javaコードでこのようなローカルコードを宣言しました.
    package com.github.cccxm;
    class NativeLib{
        public static native String getName(int number);
    }
    

    一般的には、ローカルソースファイルで次のように宣言する必要があります.
    JNIEXPORT jstring JNICALL Java_com_github_cccxm_NativeLib_getName(JNIEnv *env,jobject thiz,jint number);
    

    では、Javaメソッドとローカル関数のマッピング関係コンパイラが手伝ってくれました.一部のシーンでは、ローカルメソッドを動的にロードする必要があります.たとえば、NativeLibクラスを使用していますが、ローカルコードでは、JNI仕様に従って名前が付けられていないローカル関数を宣言します.
    JNIEXPORT jstring JNICALL getName(JNIEnv *env, jclass clazz);
    

    では、Javaメソッドとローカル関数のマッピングを動的に関連付けて実現する必要があります.コードは次のとおりです.
    extern "C"
    JNIEXPORT jstring JNICALL getName(JNIEnv *env, jobject thiz, int number) {
        ALOGE("number is %d",number);
        return env->NewStringUTF("hello world");
    }
    
    static const char *CLASS_NAME = "com/github/cccxm/NativeLib";//  
    
    static JNINativeMethod method = {//      
            "getName",//Java   
            "(I)Ljava/lang/String;",//    
            (void *) getName //      
    };
    
    static bool
    bindNative(JNIEnv *env) {
        jclass clazz;
        clazz = env->FindClass(CLASS_NAME);
        if (clazz == NULL) {
            return false;
        }
        return env->RegisterNatives(clazz, &method, 1) == 0;
    }
    
    JNIEXPORT jint JNICALL
    JNI_OnLoad(JavaVM *vm, void *reserved) {
        JNIEnv *env = NULL;
        jint result = -1;
    
        if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
            return result;
        }
    
        bool res = bindNative(env);
        ALOGE("bind result is %s",res?"ok":"error");
    
        //   jni   
        return JNI_VERSION_1_6;
    }
    

    注意:JNI_OnLoadメソッドはそれぞれです.soライブラリには1つしか存在しません.
    10.2.6ローカル・メソッドのアンインストール
    上記の例では、ローカルメソッドを動的にロードする方法について説明しました.ロードがあればアンインストールがあります.次に、ローカルメソッドをアンインストールする方法を見てみましょう.JNI_OnLoadメソッドは、ダイナミックライブラリがロードされたときに呼び出され、JNI_OnUnloadは、ローカルライブラリがアンインストールされたときに呼び出されます.したがって、この2つの関数は、ローカルライブラリで最も重要な2つのライフサイクルメソッドです.ローカルライブラリを明示的にアンインストールしていない場合、このメソッドは呼び出されません.使用例を次に示します.
    ...
    
    static bool
    unBindNative(JNIEnv *env) {
        jclass clazz;
        clazz = env->FindClass(CLASS_NAME);
        if (clazz == NULL) {
            return false;
        }
        return env->UnregisterNatives(clazz) == 0;
    }
    
    JNIEXPORT void JNICALL
    JNI_OnUnload(JavaVM *vm, void *reserved) {
        JNIEnv *env = NULL;
        jint result = -1;
    
        if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
            return;
        }
        bool res = unBindNative(env);
        ALOGE("unbind result is %s", res ? "ok" : "error");
    }
    

    10.2.7デフォルトのVM初期化パラメータの取得
    次の関数を使用して、デフォルトのVM初期化パラメータを取得します.
    jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);
  • vm_Args:JavaVMInitArgsタイプのパラメータ.この構造体宣言は10.2.1
  • である.
  • return:取得成功JNI_OK、失敗は他に戻ります.

  • 10.2.8 Java仮想マシンの取得
    作成したJava仮想マシンオブジェクトは、次の方法で取得できます.
    jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);
  • vmBuf:Java仮想マシンを保存するためのバッファ
  • bufLen:バッファ長.
  • nVms:実際に取得したJava仮想マシンの数.
  • return:取得成功JNI_OK、失敗は他に戻ります.

  • 後記
    シリーズ全体の内容は多くないが、途中で何度も諦めたいと思っていたので、シリーズブログを書くのは楽なことではないようだ.
    ここに顔真卿の詩を送り,君と共に励ます
    三更灯火五更鶏は,ちょうど男が本を読む時だ.黒髪は勤勉が早いことを知らず、白首は本を読むのが遅いことを後悔している.
    本シリーズは終了し、読者の皆様のご支援に感謝します.その内容は絶えず補充されます.不当な点があれば,ご批判ご指導をお願いします.
    [1]ORACLE guides for JNI——Chapter 4: JNI Functions