在 Android JNI 开发中,Java层使用一个long
型变量来保存JNI层的C++对象指针是一个经典的常用技巧。在Android Framework层源码中也被广泛使用,这似乎成了一种默认的套路准则。
Framework中的相关源码示例详细可参考Bitmap.java
和MessageQueue.java
以及其对应JNI层实现:
下面简单总结一下源码中是如何做的:
第一步:java
层定义long
型成员变量存储native
层对应的指针:
public final class Bitmap implements Parcelable {
private final long mNativePtr;
}
第二步:native
层在初始化时,创建C++
对象指针,通过 reinterpret_cast
转成long
型设置给java
层:
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
...
return reinterpret_cast<jlong>(nativeMessageQueue);
}
jobject createBitmap(JNIEnv* env, Bitmap* bitmap,
int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, int density) {
...
BitmapWrapper* bitmapWrapper = new BitmapWrapper(bitmap);
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
reinterpret_cast<jlong>(bitmapWrapper), bitmap->width(), bitmap->height(), density,
isMutable, isPremultiplied, ninePatchChunk, ninePatchInsets);
...
return obj;
}
第三步:具体使用的时候,Java
层调用JNI
方法时将long
型指针句柄传给native
层,native
层再次通过reinterpret_cast
将long
型指针句柄强转成对应的C++
对象指针进行方法调用:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
我们看两眼Bitmap的源码:
注意:Bitmap的构造函数是由JNI层主动调用的,开发者没有办法调用,它的注释上也写着 “called from JNI”。
对于这个用法,本人深有体会,在Android开发的远古时代(估计很多人都没有经历过),那个时候手机还都是
32
位的,因此开发当中都是使用int
类型来存储C对象指针的,随着技术飞速发展,到了后来手机芯片也提升到了64
位的,对象指针变为使用64
位来存储,就需要做32
位到64
位的适配工作,记得那个时候为了适配64
位对象指针还特意满工程的到处搜索查找int
型的指针,然后将其替换为long
型的。
了解了这个套路,如果我们在开发中遇到要将C层对象保存到Java层的问题,就可以依葫芦画瓢自己动手写一个类似的来使用了。下面是在另一篇文章中看到的代码例子,可以直接拿来用:
新建一个java类:
public class Teacher {
public long mNativePtr = 0;
/**
* 初始化一个学生,并将指针地址保存在mNativePtr变量中
*/
public native void initStudent();
/**
* 根据学生的指针地址发起个性化教育
* @param studentPtr
*/
public native void educationStudent(long studentPtr);
/**
* 释放学生对象,很关键的一步,否则就是可怕的内存泄漏
* @param studentPtr
*/
public native void releaseStudent(long studentPtr);
}
新建C++对象类:
Student.h
#ifndef GROWING_STUDENT_H
#define GROWING_STUDENT_H
class Student {
public:
char *name = nullptr;
int age = 0;
Student();
~Student();
};
#endif //GROWING_STUDENT_H
Student.cpp
#include "Student.h"
Student::Student() {
}
Student::~Student() {
}
native-lib.cpp
extern "C"
JNIEXPORT void JNICALL
Java_com_flyer_bspatchupdate_jni_Teacher_initStudent(JNIEnv *env, jobject thiz) {
Student *student = new Student;
student->age = 18;
student->name = "我是一名学生,我叫小明";
// JNI 设置Java变量
jclass jc = env->GetObjectClass(thiz);
jfieldID jf = env->GetFieldID(jc, "mNativePtr", "J");
env->SetLongField(thiz, jf, (jlong)student);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_flyer_bspatchupdate_jni_Teacher_educationStudent(JNIEnv *env, jobject thiz, jlong student_ptr) {
if (0 != student_ptr) {
Student *student = (Student*)student_ptr;
student->age = student->age + 1;
student->name;
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_flyer_bspatchupdate_jni_Teacher_releaseStudent(JNIEnv *env, jobject thiz, jlong student_ptr) {
if (0 != student_ptr) {
Student *student = (Student*)student_ptr;
delete student;
}
}
测试 Activity 类:
public class JNIActivity extends AppCompatActivity {
private Teacher teacher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_j_n_i);
teacher = new Teacher();
teacher.initStudent();
teacher.educationStudent(teacher.mNativePtr);
}
@Override
protected void onDestroy() {
if(null != teacher){
teacher.releaseStudent(teacher.mNativePtr);
teacher = null;
}
super.onDestroy();
}
}