天天看点

深入浅出Android NDK之jni对象操作

目录

上一篇 深入浅出Android NDK之JNI数组操作

对象操作包括以内容:

创建对象

调用对象函数

获取/设置对象属性

不管哪种操作,首先需要获得类对象jclass。

jni提供了两个API可以获得jclass对象:

jclass FindClass(const char* name);
jclass GetObjectClass(jobject obj);
           

大部分情况下我们都会通过FindClass来获得jclass对象:比如:

env->FindClass("java/lang/String");
env->FindClass("android/graphics/Bitmap");
env->FindClass("android/view/Button");
           

FindClass的name参数其实就是包名加类名,然后把.用/代替。

在已经拥有jobject的情况下,我们可以直接调用GetObjectClass来得到其类对象。

得到jclass对像有什么用呢?jclass主要可以用来获得方法ID和成员变量ID。

jmethodID GetMethodID(jclass clazz, const char* name, const char* sig);
 jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig);
 jfieldID GetFieldID(jclass clazz, const char* name, const char* sig);
 jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig);
           

下面我们看一个例子,假有如下Java类:

package com.example.objtest;

public class ObjTest {
    private int mID;
    private static int sCount;
    public int getID() {
        return mID;
    }
    public void setID(int id) {
        mID = id;
    }
    public static int getCount() {
        return sCount;
    }
    public static void setCount(int count) {
        sCount = count;
    }
}
           

我们可以:

jclass cls = env->FindClass("com/example/objtest/ObjTest");

jmethodID mid1 = env->GetMethodID(cls, "<init>","()V");//默认构造函数
jmethodID mid2 = env->GetMethodID(cls, "<init>","(I)V");//构造函数public ObjTest(int id)

jfieldID fid1 = env->GetFieldID(cls, "mID","I");
jmethodID mid3 = env->GetMethodID(cls, "getID","()I");
jmethodID mid4 = env->GetMethodID(cls, "setID","(I)V");

jfieldID fid2 = env->GetStaticFieldID(cls, "sCount","I");
jmethodID mid5 = env->GetStaticMethodID(cls, "getCount","()I");
jmethodID mid6 = env->GetStaticMethodID(cls, "setCount","(I)V");
           

不管是方法ID还是成变变量ID,除了函数名称以外,我们还需要传入签名,签名的用法之前我们已经讲过,不清楚的可以看之前的文章:

深入浅出Android NDK之使用RegisterNatives函数动态注册native函数

特别需要注意的是对于构造函数,函数的名称固定为,返回值为void。所以构造函数的签名的返回值类型都是V。

有了方法ID和成员变量ID,之后的事情就好办多了,我们看以下例子:

jclass cls;
    jmethodID mid;
    jfieldID fid;
    int id;

    cls = env->FindClass("com/example/objtest/ObjTest");

    /*ObjTest test = new ObjTest(1);*/
    mid = env->GetMethodID(cls, "<init>","(I)V");
    jobject test = env->NewObject(cls,  mid, 1);

    /*id = test.getID()*/;
    mid = env->GetMethodID(cls, "getID","()I");
    id = env->CallIntMethod(test, mid);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "test.getID() return %d", id);

    /*test.setID(2);*/
    mid = env->GetMethodID(cls, "setID", "(I)V");
    env->CallVoidMethod(test, mid, 2);


    /*test.mID = 3;*/
    fid = env->GetFieldID(cls, "mID", "I");
    env->SetIntField(test, fid, 3);

    /*id = test.mID*/
    id = env->GetIntField(test, fid);
    __android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "test.mID is %d", id);
           

我们可以调用NewObject函数创建java对象,构建的时候需要传入构造方法ID和参数:

对象创建完成之后我们就可以调用对象的函数了,在调用函数之前我们首先要确定函数的返回值类型,根据返回值类型来确定调用CallMethod的哪个变体,共有以下变体:

jboolean CallVoidMethod(jobject, jmethodID, ...);
jboolean CallBooleanMethod(jobject, jmethodID, ...);
jbyte    CallByteMethod(jobject, jmethodID, ...);
jchar    CallCharMethod(jobject, jmethodID, ...);
jshort   CallShortMethod(jobject, jmethodID, ...);
jint     CallIntMethod(jobject, jmethodID, ...);
jlong    CallLongMethod(jobject, jmethodID, ...);
jobject  CallObjectMethod(jobject, jmethodID, ...);
           

成员变量也类似,确定了成员变量的类型后,再调用

void SetField( jobject, jfieldID, );

GetField( jobject, jfieldID);

来存/取相应的成员变量。

对于静态函数和静态成员变量的调用也类似,对于静态函数和静态成员变量需要调用staitct版本的函数,且因为是静态,所以不需要对象,直接通过jclass调用即可。

jclass cls;
jmethodID mid;
jfieldID fid;
int count;

cls = env->FindClass("com/example/objtest/ObjTest");

/*count = ObjTest.getCount()*/;
mid = env->GetStaticMethodID(cls, "getCount","()I");
count = env->CallStaticIntMethod(cls, mid);
__android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "ObjTest.getCount() return %d", count);

/*test.setCount(2);*/
mid = env->GetStaticMethodID(cls, "setCount", "(I)V");
env->CallStaticVoidMethod(cls, mid, 2);

/*test.sCount = 3;*/
fid = env->GetStaticFieldID(cls, "sCount", "I");
env->SetStaticIntField(cls, fid, 3);

/*int count = test.sCount*/
count = env->GetStaticIntField(cls, fid);
__android_log_print(ANDROID_LOG_DEBUG, "MD_DEBUG", "test.sCount is %d", count);
           

下面我们总结一下,创建对象步骤:

  • env->FindClass得到jclass
  • env->GetMethodID得到构造方法的jmethodID
  • env->NewObject(jclass,jmethodID,参数)

调用实例方法的步骤:

  • env->FindClass得到jclass
  • env->GetMethodID得到构造方法的jmethodID
  • env->CallXXXMehtod(jobject, jmethodID,参数)

设置成员变量步骤:

  • env->FindClass得到jclass
  • env->GetFieldID得到成员变量的jfieldID
  • env->SetXXXFileld(jobject, jfieldID,值)

获得成员变量步骤:

  • env->FindClass得到jclass
  • env->GetFieldID得到成员变量的jfieldID
  • env->GetXXXFileld(jobject, jfieldID)

总结:

  • 得到jclass
  • 从jclass中得到ID
  • 确定类型,调用相应函数

下一篇 深入浅出Android NDK之异常处理