Android中C++类的JNI封装:创建与回调实现
通过JNI将面向对象的C++代码集成到Android应用中,需要将其转换为C函数,同时存储对象的指针。C++对象的地址以long类型保存在Java类中,从而实现生命周期管理和多实例支持。
考虑一个基础的C++类:
class NativeClass {
public:
NativeClass() {}
~NativeClass() {}
void fun() {
__android_log_write(ANDROID_LOG_ERROR, log_tag, "Hello from native");
}
};
用于操作该类的JNI函数:
- 初始化:
jlong Java_com_myprog_example_Native_initNativeClass(JNIEnv* env, jobject thiz)返回(jlong) new NativeClass(); - 方法调用:
void Java_com_myprog_example_Native_NativeClassFun(JNIEnv env, jobject thiz, jlong cppClass)调用((NativeClass) cppClass)->fun(); - 销毁:
void Java_com_myprog_example_Native_destroyNativeClass(JNIEnv env, jobject thiz, jlong cppClass)执行delete (NativeClass) cppClass;
Java封装类:
public class Native {
static { System.loadLibrary("Native"); }
public static native long initNativeClass();
public static native void destroyNativeClass(long cppClass);
public static native void NativeClassFun(long cppClass);
}
public class NativeClass {
long cppClass;
NativeClass() {
this.cppClass = Native.initNativeClass();
}
@Override protected void finalize() {
free();
}
private void free() {
Native.destroyNativeClass(cppClass);
}
public void fun() {
Native.NativeClassFun(cppClass);
}
}
实现从原生代码的回调
对于异步场景,如网络扫描,原生代码需要在操作期间调用Java方法。使用C++中的Listener接口和ThreadPool在单一线程中序列化调用。
扩展支持回调的C++类:
class NativeClass {
public:
class Listener {
public:
virtual void print(const char* str) = 0;
virtual ~Listener() {}
};
private:
bool started = false;
Listener* listener = nullptr;
static void worker(NativeClass* nc) {
while(nc->started) {
if(nc->listener != nullptr) {
nc->listener->print("From native");
}
sleep(1);
}
}
public:
NativeClass() {}
~NativeClass() {
if(listener != nullptr) delete listener;
}
void setListener(Listener* listener) {
this->listener = listener;
}
void scan() {
started = true;
std::thread thread = std::thread(worker, this);
thread.join();
started = false;
}
void stop() {
started = false;
}
};
Java接口和封装类:
public interface NativeListener {
void print(String str);
}
public class NativeClass {
long cppClass;
// 构造函数、finalize、free方法同上
public void scan() { Native.NativeClassScan(cppClass); }
public void stop() { Native.NativeClassStop(cppClass); }
public void setListener(NativeListener listener) {
Native.NativeClassSetListener(cppClass, listener);
}
}
关键组件——使用JNI在C++中实现Listener:
class Listener : public NativeClass::Listener {
private:
JavaVM* jvm;
JNIEnv* env;
jweak listenerRef;
jmethodID printMethod;
public:
Listener(JavaVM* jvm, JNIEnv* env, jobject listener) {
this->jvm = jvm;
listenerRef = env->NewWeakGlobalRef(listener);
ThreadPool::execute({
jvm->AttachCurrentThread(&env, NULL);
if(env != NULL) {
jobject localRef = env->NewLocalRef(listenerRef);
if(localRef != NULL) {
jclass clazz = env->GetObjectClass(localRef);
printMethod = env->GetMethodID(clazz, "print", "(Ljava/lang/String;)V");
env->DeleteLocalRef(localRef);
}
}
});
}
~Listener() {
ThreadPool::execute({
env->DeleteWeakGlobalRef(listenerRef);
jvm->DetachCurrentThread();
});
}
void print(const char* msg) {
ThreadPool::execute({
jobject localRef = env->NewLocalRef(listenerRef);
if(localRef != NULL) {
jstring jMsg = env->NewStringUTF(msg);
env->CallVoidMethod(localRef, printMethod, jMsg);
env->DeleteLocalRef(jMsg);
env->DeleteLocalRef(localRef);
}
});
}
};
设置监听器的JNI函数:
JNIEXPORT void JNICALL Java_com_myprog_example_Native_NativeClassSetListener(
JNIEnv* env, jobject thiz, jlong cppClass, jobject listener) {
((NativeClass*) cppClass)->setListener(new Listener(jvm, env, listener));
}
实现建议
- 单线程ThreadPool:确保从固定线程进行顺序JNI调用。
- 弱引用(jweak):防止内存泄漏;仅在调用期间创建强本地引用。
- 生命周期管理:Listener析构函数通过ThreadPool清理资源。
关键实践列表:
- 使用本地引用前始终检查是否为NULL。
- 对长期引用使用
NewWeakGlobalRef并及时删除。 - 仅在ThreadPool内调用
AttachCurrentThread/DetachCurrentThread。 - 对于同步回调,添加条件变量。
- 避免在UI线程上执行阻塞操作,如
scan()。
核心要点
- 将C++对象指针以long类型存储在Java中,提供完整的对象语义。
- 单线程ThreadPool对于从原生线程进行稳定的JNI调用至关重要。
- 弱全局引用(jweak)+本地引用最小化内存泄漏风险。
- Java封装类的finalize方法自动清理原生资源。
- 异步回调适用于网络扫描等实时任务。
— Editorial Team
暂无评论。