Android JNIおよびNDK学習(4):JNIはJavaメソッドと変数を呼び出します

概要概要

今日もJNIの勉強を続けています。最初のいくつかの記事でいくつかの基本的な内容を学びました。今日はそれらすべてを練習します。この記事は、将来忘れてしまった場合に備えて、メモにすぎません。

JavaオブジェクトのメンバーへのJNIアクセス

まず、使用する必要のあるAPIを見てみましょう。

jclassを取得する

jclass GetObjectClass(JNIEnv *env, jobject obj);
  • jobject:Javaを表すオブジェクト
  • この関数はクラスオブジェクトを返します

jfieldIDを取得する

jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
  • JNIEnv:JNIEnvポインター
  • jclass:受け渡しGetObjectClassまたはFindClass取得できるクラスオブジェクト
  • name:クラスオブジェクト内の変数の名前
  • sig:変数の型シグネチャ
  • クラスオブジェクトの変数を返します。タイプはjfieldIDです。

変数の値を取得する

変数の種類に応じて、変数の値を取得するためのさまざまな関数があります。関数の基本的な形式は次のとおりです。

NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
関数名 戻り値のタイプ
GetBooleanField() jboolean
GetByteField() jbyte
GetCharField() jchar
GetShortField() jshort
GetIntField() jit
GetLongField() jlong
GetFloatField() jfloat
GetDoubleField() jdouble
GetObjectField() ジョブジェクト

最初の8つの項目は基本データ型であり、9番目の項目はすべての参照データ型を取得することです。

たとえば、次のようにint変数を取得します。

jint GetIntField(JNIEnv * env, jobject obj, jfieldID fieldID);
  • jobject:Javaオブジェクトを表します
  • jfieldID:変数のクラスに代わって、GetFieldID取得することにより
  • ジントを返す

変数の値を設定します

変数の種類に応じて、変数の値を設定するためのさまざまな関数があります

void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,
                    NativeType value);
関数名 パラメータタイプ
SetBooleanField() jboolean
SetByteField() jbyte
SetCharField() jchar
SetShortField() jshort
SetIntField() jit
SetLongField() jlong
SetFloatField() jfloat
SetDoubleField() jdouble
SetObjectField() ジョブジェクト

最初の8つの項目は基本的なデータ型に対応し、9番目の項目はJavaのすべての参照型に対応します。

たとえば、intの関数プロトタイプ

void setIntField(JNIEnv * env, jobject obj, jfieldID fieldID, jint value);
  • value:intの値を設定したいのですが、他のパラメータは上記と同じなので、繰り返しません。

jmethodIDを取得します

jmethodID GetMethodID(JNIEnv *env, jclass clazz,
                        const char *name, const char *sig);
  • name:クラスオブジェクトのメソッドの名前を取得します
  • sig:メソッドのシグネチャを取得します
  • 戻り値:このメソッドが存在する場合はjmethodIDを返し、存在しない場合はNULLを返します。
  • Java構築メソッドを取得した場合、const char * nameの値とパラメーターconstchar * sigの値はvoid(V)です。

オブジェクトメソッドを呼び出す

javaメソッドの戻り値に応じて、jniにはjavaオブジェクトのメソッドを呼び出すためのさまざまな関数があります。3つの基本的な形式があります。

NativeType Call<type>Method(JNIEnv *env, jobject obj,
jmethodID methodID, ...);

NativeType Call<type>MethodA(JNIEnv *env, jobject obj,
jmethodID methodID, const jvalue *args);

NativeType Call<type>MethodV(JNIEnv *env, jobject obj,
jmethodID methodID, va_list args);

これらの3つの方法の違いは、パラメーターの受け渡しとは異なります。最も一般的に使用されるのは最初の方法です。最初の方法についてのみ説明します。

メソッドがintを返す場合、関数プロトタイプは次のようになります。

jint CallIntMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);

メソッドの戻り値が参照データ型の場合

jobject CallObjectMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);

javaの戻り値タイプがvoidの場合

void CallVoidMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);
  • jmethodID:メソッドオブジェクト、GetMethodID取得することにより
  • …:変数パラメーター、javaメソッドに渡す必要のあるパラメーター

以下に実装されている完全なコードを見てみましょう

最初に、オブジェクトの作成時に呼び出されるネイティブメソッドを持つJavaクラスを定義します

public class Student {
    
    
    public String name = "小红";

    static {
    
    
        System.loadLibrary("studentlib");
    }

    native void native_init();

    public Student() {
    
    
        native_init();
    }


    public void print() {
    
    
        Log.d("mmm", name);
    }
}

このJavaネイティブメソッドをjniに登録してから、Cで実装します。

#include <jni.h>
#include <iostream>
#include <android/log.h>

static void native_init(JNIEnv *env, jobject jobject1) {
    
    

    //1 获取java对象的变量的值,并重新为他设置新的值


    //获取class对象
    jclass jclass_student = env->GetObjectClass(jobject1);
    //从class中获取变量
    jfieldID jfieldId = env->GetFieldID(jclass_student, "name", "Ljava/lang/String;");
    //从java对象obj中获取name变量的值
    jstring name = static_cast<jstring>(env->GetObjectField(jobject1, jfieldId));
    const char *char_name = env->GetStringUTFChars(name, JNI_FALSE);
    //打印出原来的name变量的值
    __android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__,
                        char_name);
    const char native_name[] = "小明";
    jstring jstring_name = env->NewStringUTF(native_name);
    //通过jni重新设置变量name的值
    env->SetObjectField(jobject1, jfieldId, jstring_name);


    //2 调用java对象中的方法

    //获取class对象中的print方法
    jmethodID print = env->GetMethodID(jclass_student, "print", "()V");
    //调用java对象中的print方法
    env->CallVoidMethod(jobject1, print);

}

static const JNINativeMethod nativeMethod[] = {
    
    
        {
    
    "native_init", "()V", (void *) native_init},
};

static int registNativeMethod(JNIEnv *env) {
    
    
    int result = -1;

    jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.Student");
    if (env->RegisterNatives(class_text, nativeMethod,
                             sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
    
    
        result = 0;
    }
    return result;
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    
    
    JNIEnv *env = NULL;
    int result = -1;

    if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
    
    
        if (registNativeMethod(env) == JNI_OK) {
    
    
            result = JNI_VERSION_1_6;
        }
        return result;
    }
}

動的登録についてnative_init前の記事で説明したので、ここではあまり説明しません。主な実装ロジックは実装にあります。一般的なロジックはStudentオブジェクトname内の変数の値を取得して出力し、値リセットnameすることです。変数の値を出力し最後にStudentObjectprintメソッドを呼び出してname変数の値を出力します。

最後は呼び出すことです。これは、newによって直接トリガーできます。

  protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Student student = new Student();
    }

印刷結果を見てください

/mmm: method = native_init, msg = 小红
/mmm: 小明

Java静的メンバーへのJNIアクセス

ベルト付きのApiを見てみましょう

FindClass

クラスオブジェクトを取得する

jclass FindClass(JNIEnv *env, const char *name);
  • const char * name:完全修飾クラス名

GetStaticFieldID

クラスオブジェクトの静的フィールドを取得します

jfieldID GetStaticFieldID (JNIEnv *env, 
                            jclass clazz, 
                            const char *name, 
                            const char *sig);

  • name:静的フィールドの名前
  • sig:静的フィールドの型シグネチャ

GetStaticField

静的フィールドの値を取得します。静的フィールドのタイプに応じて、jniには静的フィールドの値を取得するためのさまざまなメソッドがあります。

メソッド名 戻り値
GetStaticBooleanField jboolean
GetStaticByteField jbyte
GetStaticCharField jchar
GetStaticShortField jshort
GetStaticIntField jit
GetStaticLongField jlong
GetStaticFloatField jfloat
GetStaticDoubleField jdouble
GetStaticObjectField ジョブジェクト

静的変数の値を設定します

void SetStatic<type>Field(JNIEnv *env, 
                        jclass clazz,
                        jfieldID fieldID, 
                        NativeType value);

  • 最後のパラメータは、設定する必要のある値です
メソッド名 NativeType
SetStaticBooleanField jboolean
SetStaticByteField jbyte
SetStaticCharField jchar
SetStaticShortField jshort
SetStaticIntField jit
SetStaticLongField jlong
SetStaticFloatField jfloat
SetStaticDoubleField jdouble
SetStaticObjectField ジョブジェクト

静的メソッドを呼び出す

jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)

void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)

これら2つは以前に使用された通常の方法と同じですが、関数名が変更されています。

完全なコード

Javaコード

public class Student {
    
    

    public static String text = "没改过";

    static {
    
    
        System.loadLibrary("studentlib");
    }


    native void native_Text();

    public static void printText() {
    
    
        Log.d("mmm", text);
    }

}

cコード

static void native_text(JNIEnv *env, jobject jobject1) {
    
    

    //获取class
    jclass jclass_student = env->FindClass("com.taobao.alinnkit.ndk1.Student");
    //获取静态变量text
    jfieldID jfieldId_text = env->GetStaticFieldID(jclass_student, "text", "Ljava/lang/String;");
    //获取静态变量text的值
    jstring text = (jstring) env->GetStaticObjectField(jclass_student, jfieldId_text);
    const char *char_name = env->GetStringUTFChars(text, JNI_FALSE);
    __android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__,
                        char_name);
    jstring jstring1 = env->NewStringUTF("改过了");
    //修改静态变量text的值
    env->SetStaticObjectField(jclass_student, jfieldId_text, jstring1);
    //获取静态方法printText
    jmethodID jmethodId = env->GetStaticMethodID(jclass_student, "printText", "()V");
    //调用静态方法printText
    env->CallStaticVoidMethod(jclass_student, jmethodId);
}

static const JNINativeMethod nativeMethod[] = {
    
    
        {
    
    "native_Text", "()V", (void *) native_text}

};

static int registNativeMethod(JNIEnv *env) {
    
    
    int result = -1;

    jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.Student");
    if (env->RegisterNatives(class_text, nativeMethod,
                             sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
    
    
        result = 0;
    }
    return result;
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    
    
    JNIEnv *env = NULL;
    int result = -1;

    if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
    
    
        if (registNativeMethod(env) == JNI_OK) {
    
    
            result = JNI_VERSION_1_6;
        }
        return result;
    }
}

主な実装はnative_text、主にtext静的変数の値を取得し、静的変数の値を変更しtext、静的メソッドを呼び出すメソッドです。printText

転送

 protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Student student = new Student();
        student.native_Text();
    }

データを印刷する

/mmm: method = native_text, msg = 没改过
/mmm: 改过了

参照

https://juejin.im/post/5d23fb066fb9a07eb15d7b29

https://blog.csdn.net/afei__/article/details/81016413

https://www.jianshu.com/p/67081d9b0a9c

おすすめ

転載: blog.csdn.net/qq_34760508/article/details/106573373