Android JNI and NDK learning (four): JNI calls Java methods and variables

Overview

I continue to study JNI today. The first few articles have learned some basic content. Today we will practice all of them. This article is only for notes, in case you forget it in the future.

JNI access to the members of the Java object

Let's first look at the APIs that need to be used

Get jclass

jclass GetObjectClass(JNIEnv *env, jobject obj);
  • jobject: object representing java
  • The function returns a class object

Get jfieldID

jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
  • JNIEnv: JNIEnv pointer
  • jclass: class object, which can be passed GetObjectClassor FindClassobtained
  • name: the name of a variable in the class object
  • sig: the type signature of the variable
  • Return a variable of a class object, the type is jfieldID

Get the value of a variable

According to the type of variable, there will be different functions to get the value of the variable. The basic form of the function is as follows:

NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
Function name Return value type
GetBooleanField() jboolean
GetByteField() jbyte
GetCharField() jchar
GetShortField() jshort
GetIntField () jit
GetLongField() jlong
GetFloatField() jfloat
GetDoubleField() jdouble
GetObjectField() jobject

The first eight items are basic data types, and the ninth item is to get all reference data types

For example, get the int variable as follows:

jint GetIntField(JNIEnv * env, jobject obj, jfieldID fieldID);
  • jobject: represents a java object
  • jfieldID: on behalf of a class of variable, by GetFieldIDacquiring
  • Return a jint

Set the value of the variable

According to the type of variable, there are different functions to set the value of the variable

void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,
                    NativeType value);
Function name Parameter Type
SetBooleanField() jboolean
SetByteField() jbyte
SetCharField () jchar
SetShortField() jshort
SetIntField() jit
SetLongField() jlong
SetFloatField() jfloat
SetDoubleField() jdouble
SetObjectField() jobject

The first eight items correspond to basic data types, and the ninth item corresponds to all reference types in java

For example, the function prototype of int

void setIntField(JNIEnv * env, jobject obj, jfieldID fieldID, jint value);
  • value: You want to set the value of int, other parameters are the same as above, so I won’t repeat it

Get jmethodID

jmethodID GetMethodID(JNIEnv *env, jclass clazz,
                        const char *name, const char *sig);
  • name: Get the name of the method in the class object
  • sig: get the signature of the method
  • Return value: If this method exists, return jmethodID, if it does not exist, return NULL
  • If you get the java construction method, the value of const char *name and the value of parameter const char *sig are void(V)

Call object method

According to the return value of the java method, jni has different functions to call the method of the java object. There are three basic forms

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);

The difference between these three methods is different from passing parameters, the most commonly used is the first one, we only talk about the first one

If the method returns an int, then the function prototype is

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

If the return value of the method is a reference data type

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

If the return value type of java is void

void CallVoidMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);
  • jmethodID: method object, by GetMethodIDobtaining
  • …: Variable parameters, the parameters you need to pass to the java method

Let's look at the complete code implemented below

First define a java class, which has a native method, which will be called when the object is created

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);
    }
}

Register this java native method in jni, and then implement it in 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;
    }
}

Dynamic registration was mentioned in native_initthe last article, so I won’t say much here. The main implementation logic lies in the implementation. The general logic is to get the value Studentof the variable namein the object and print it out, then reset namethe value of the variable, and finally call StudentObject printmethod, print out namethe value of the variable

The last is to call, which can be triggered directly by new

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

        Student student = new Student();
    }

Look at the print result

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

JNI access to java static members

Let's take a look at the Api with the belt

FindClass

Get class object

jclass FindClass(JNIEnv *env, const char *name);
  • const char *name: fully qualified class name

GetStaticFieldID

Get the static field of the class object

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

  • name: the name of the static field
  • sig: the type signature of the static field

GetStaticField

Get the value of the static field, according to the type of the static field, jni has different methods to get the value of the static field

Method name return value
GetStaticBooleanField jboolean
GetStaticByteField jbyte
GetStaticCharField jchar
GetStaticShortField jshort
GetStaticIntField jit
GetStaticLongField jlong
GetStaticFloatField jfloat
GetStaticDoubleField jdouble
GetStaticObjectField jobject

Set the value of a static variable

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

  • The last parameter is the value you need to set
Method name NativeType
SetStaticBooleanField jboolean
SetStaticByteField jbyte
SetStaticCharField jchar
SetStaticShortField jshort
SetStaticIntField jit
SetStaticLongField jlong
SetStaticFloatField jfloat
SetStaticDoubleField jdouble
SetStaticObjectField jobject

Call static method

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

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

These two are the same as the normal method used before, but the function name is changed.

Complete code

java code

public class Student {
    
    

    public static String text = "没改过";

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


    native void native_Text();

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

}

c code

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;
    }
}

The main implementation is the native_textmethod, mainly to obtain textthe value of the static variable, modify the value of the static variable text, and call the static methodprintText

transfer

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

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

Print data

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

reference

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

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

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

Guess you like

Origin blog.csdn.net/qq_34760508/article/details/106573373