其他

NDK学习之JNI中关于Object一些列操作

2017-10-30  本文已影响68人  肚皮怪_Sun

今天我们学习的是在JNI中对Object的操作,如果使用java,我们可以通过创建一个对象,通过一个引用来接收,比如父类引用指向子类对象,然后拿到这个对象引用可以对其进行一些列的操作。在JNI中我们也可以创建并对其Object做一些列的操作。
在这之前需要引入几个概念JNIEnv、jobject,因为在后面我们会频繁的使用这两个类型。

  1. JNIEnv:指向一个结构体,可以理解为java和C/C++之间的翻译官。
  2. jobject:如果native方法不是static的话,这个obj就代表这个native方法的类实
    例如果native方法是static的话,这个obj就代表这个native方法的类的class对象实例。

了解了这两个类型以后我们开始真正的本文的学习——JNI对Object的操作。

首先我们先介绍通过分配的方式获取一个对象——也可以理解成复制。
按例先创建一个Test类

public class Test { 
  public void temp() {
      Log.e("TAG", "我是text类中的temp方法");
  }
}

然后我们在MainActivity中创建一个native方法

public class MainActivity extends AppCompatActivity {
Test test = new Test();

// Used to load the 'native-lib' library on application startup.
static {
    System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Example of a call to a native method
    TextView tv = (TextView) findViewById(R.id.sample_text);
    tv.setText(stringFromJNI());
     testObject(test);//调用native方法
}

public native String stringFromJNI();

public native void testObject(Test test);
}

在testObject报红处我们在native-lib.cpp文件生成native方法,进行我们操作

extern "C"
JNIEXPORT void JNICALL
Java_com_captain_wudongsheng_day3ndk_MainActivity_testObject(JNIEnv  *env,  /*native方法class对象,也就是我们的MainActivity*/ jobject instance,
                                                       /*传入的对象*/  jobject test) {
  /*分配一个对java象*/
  jclass testclass = env->FindClass("com/captain/wudongsheng/day3ndk/Test");//通过反射的机制拿到Test类,注意通过这种方法不会调用构造函数
  jobject testobject = env->AllocObject(testclass);

  /*jmethodID GetMethodID(jclass clazz /*类*/, const char* name/*方法名*/, const char* sig/*方法签名*/)*/
  jmethodID temmid = env->GetMethodID(testclass, "temp", "()V");//获取方法methodID
  env->CallVoidMethod(testobject, temmid);//指向Call方法

}

运行以后 我们可以顺利在控制台看到日志输出,执行了temp()方法.

10-30 16:53:00.501 2524-2524/com.captain.wudongsheng.day3ndk E/TAG: 我是text类中的temp方法

有一点需要注意:
当Local Reference(局部引用)不再使用的时候 最好调用void DeleteLocalRef(jobject localRef),原因是创建了过多的 Local Reference,从而导致 out of memory。实际上,nativeMethod 在运行中创建了越来越多的 JNI Local Reference,而不是看似的始终只有一个。过多的 Local Reference,导致了 JNI 内部的 JNI Local Reference 表内存溢出,有兴趣的同学可以for循环试一下。

还有种方式创建一个java对象,通过NewObjec(jclass clazz, jmethodID methodID, ...)
其中第二个参数是构造函数名ID,固定的写法是<init>
,第三个参数方法的参数列表。

 /*创建一个java对象*/
jmethodID testmid = env->GetMethodID(testclass,/*构造方法的函数名的固定方法*/"<init>","(I)V");
jobject newobject = env->NewObject(testclass, /*构造方法的ID*/testmid);

通过上边两种方法获取的对象是什么类型呢,可以用 JNI 1.6引进的方法
jobjectRefType GetObjectRefType(jobject obj)获取对象引用的类型。

 /*返回obj参数对象的引用的类型*/
jobjectRefType allcotype = env->GetObjectRefType(allocobject);
jobjectRefType newtype = env->GetObjectRefType(newobject);
LOGE("allcotype=%d newtype=%d",allcotype,newtype);

那么jobjectRefType是个什么东西呢,进入jni.h头文件中可以看到,其实就是定义了一个枚举

typedef enum jobjectRefType {
  JNIInvalidRefType = 0//obj参数不是一个有效的引用
  JNILocalRefType = 1,//obj参数是一个局部引用
  JNIGlobalRefType = 2, //obj参数是一个全局引用
  JNIWeakGlobalRefType = 3. //obj参数是一个弱全局引用
} jobjectRefType;

通过Log打印,也能看出是个局部引用

  10-30 17:53:16.316 3316-3316/com.captain.wudongsheng.day3ndk E/JNI: (/Users/wudongsheng/Downloads/Day3NDK/app/src/main/cpp/native-lib.cpp:78) void Java_com_captain_wudongsheng_day3ndk_MainActivity_testObject(JNIEnv *, jobject, jobject): allcotype=1 newtype=1

接下来我们学习的是Object是否可以强转成clazz对象,这时候我们需要一个
jboolean IsInstanceOf(jobject obj, jclass clazz)方法,返回值是一个jboolean

jboolean allj = env->IsInstanceOf(allocobject, testclass);
jboolean newJ = env->IsInstanceOf(newobject, testclass);

jboolean allj1 = env->IsInstanceOf(allocobject, clazz);//clazz是我们通过native方法传进来的对象
jboolean newJ1 = env->IsInstanceOf(newobject, clazz);
//PS:如果一个对象是NULL,可以强转成任何class对象,
LOGE("%d %d %d %d",allj,newJ,allj1,newJ1);

查看log日志:输出的是1 1 0 0,你复制的对象和new出来的对象肯定和传入的对象不一样

10-30 17:53:16.316 3316-3316/com.captain.wudongsheng.day3ndk E/JNI: (/Users/wudongsheng/Downloads/Day3NDK/app/src/main/cpp/native-lib.cpp:96) void Java_com_captain_wudongsheng_day3ndk_MainActivity_testObject(JNIEnv *, jobject, jobject): 1 1 0 0

最后还有一个知识点,检查两个引用是否引用相同的java对象
jboolean IsSameObject(jobject ref1, jobject ref2)

jfieldID jtestID = env->GetFieldID(clazz, "test", "Lcom/captain/wudongsheng/day3ndk/Test;");//获取fieldID
jobject testJobject = env->GetObjectField(instance, jtestID);//获取ObjectField
//test 是传入的对象 testJobject通过ID拿到的对象
jboolean jsameObject1 = env->IsSameObject(test, testJobject);//
jboolean jsameObject2 = env->IsSameObject(test, allocobject);//复制对象
jboolean jsameObject3 = env->IsSameObject(test, newobject);//创建新对象
jboolean jsameObject4 = env->IsSameObject(allocobject, testJobject);
jboolean jsameObject5 = env->IsSameObject(allocobject, newobject);
jboolean jsameObject6 =  env->IsSameObject(testJobject, newobject);

LOGE("%d %d %d %d %d %d",jsameObject1,jsameObject2,jsameObject3,jsameObject4,jsameObject5,jsameObject6);

最后日志输出的结果是:

10-30 17:53:16.316 3316-3316/com.captain.wudongsheng.day3ndk E/JNI: (/Users/wudongsheng/Downloads/Day3NDK/app/src/main/cpp/native-lib.cpp:110) void Java_com_captain_wudongsheng_day3ndk_MainActivity_testObject(JNIEnv *, jobject, jobject): 1 0 0 0 0 0

可以看出只有第一个是相同的java对象,test是从MainActivity中传入的,而testJobject是通过GetObjectField从MainActivity中拿到的,显然是同一个对象。
最后多出了两个方法:

  1. jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
  2. jobject GetObjectField(jobject obj, jfieldID fieldID)

这也是下一篇博客要学习的内容,哪今天这篇博客就到这了。

NDK开发从入门到——??

上一篇下一篇

猜你喜欢

热点阅读