如何通过JNI实现Java类对接C++抽象类及NumberManager?
问题描述
我刚接触JNI和Java,了解基本的本地代码调用流程,但不清楚如何通过JNI让Java类对接C的抽象类(比如NumberInterface)并实现扩展。另外,我需要让Java侧能和C的NumberManagerInterface及实现类NumberManager交互,完成模块注册、数字处理等操作。
相关C++代码如下:
class NumberInterface { public: using Numbers = std::unordered_set<unsigned int>; virtual Numbers getSupportedNumbers() = 0; virtual bool handleNumber(unsigned int number) = 0; virtual bool cancelNumber(unsigned int number) = 0; };
原生侧可以链接这个共享库并实现具体类:
class Number : public NumberInterface { public: Numbers getSupportedNumbers() override; bool handleNumber(unsigned int number) override; bool cancelNumber(unsigned int number) override; // ... 其他实现代码 };
我想实现Java侧的类对接这个NumberInterface,比如:
class JavaNumber extends JavaNumberInterfaceEquivalent { // ... Java侧实现 }
补充的C++工具类代码:
/// 处理数字的NumberManagerInterface class NumberManagerInterface { public: /** * 注册处理数字的模块 * @return 成功返回true,否则返回false */ virtual bool registerNumberModule(const std::shared_ptr<NumberInterface>& module) = 0; virtual bool handleNumber(const unsigned int number) = 0; virtual bool cancelNumber(const unsigned int number) = 0; }; /** * 示例NumberManager类 */ class NumberManager : public NumberManagerInterface{ public: static std::shared_ptr<NumberManagerInterface> createNumberManagerInstance(...); bool registerNumberModule(const std::shared_ptr<NumberInterface>& module) override; bool handleNumber(const unsigned int number) override; bool cancelNumber(const unsigned int number) override; // ... 其他成员 private: std::vector<std::shared_ptr<NumberInterface>> m_numberHandlers; // ... 其他私有成员 }; bool NumberManager::registerNumberModule(const std::shared_ptr<NumberInterface>& module){ // 执行验证逻辑 m_numberHandlers.push_back(module); // ... 其他逻辑 return true; } bool NumberManager::handleNumber(const unsigned int number){ // 从m_numberHandlers中找到合适的处理器 // 调用对应处理器的handleNumber方法 return handler->handleNumber(number); } void NumberManager::cancelNumber(const unsigned int number){ // ... 实现逻辑 }
解决方案
要实现Java类对接C抽象类,并与NumberManager交互,需要构建JNI桥接层,核心思路是用C类作为中间层,连接Java接口和C++抽象类的调用。
1. 定义Java侧的等价接口
先在Java中定义与C++NumberInterface一一对应的接口,确保方法签名匹配:
public interface JavaNumberInterface { // 对应C++的getSupportedNumbers(),返回Integer集合 Set<Integer> getSupportedNumbers(); boolean handleNumber(int number); boolean cancelNumber(int number); }
同时定义Java侧的NumberManager交互类,封装JNI调用:
public class NumberManagerJni { // 加载原生库 static { System.loadLibrary("number-jni"); } // 初始化NumberManager实例 public native long initNumberManager(); // 注册Java实现的数字模块 public native boolean registerNumberModule(long managerPtr, JavaNumberInterface module); // 调用NumberManager处理数字 public native boolean handleNumber(long managerPtr, int number); // 调用NumberManager取消数字处理 public native boolean cancelNumber(long managerPtr, int number); // 释放NumberManager资源 public native void releaseNumberManager(long managerPtr); }
2. 实现JNI桥接C++类
创建一个C桥接类,继承自NumberInterface,内部持有Java对象的引用,将C的虚函数调用转发到Java侧:
#include <jni.h> #include <memory> #include <unordered_set> #include <vector> #include "NumberInterface.h" #include "NumberManagerInterface.h" // 桥接类:将C++调用转发到Java class JavaNumberBridge : public NumberInterface { public: JavaNumberBridge(JNIEnv* env, jobject javaModule) : m_env(env), m_javaModule(env->NewGlobalRef(javaModule)) {} ~JavaNumberBridge() { m_env->DeleteGlobalRef(m_javaModule); } Numbers getSupportedNumbers() override { jclass cls = m_env->GetObjectClass(m_javaModule); jmethodID methodId = m_env->GetMethodID(cls, "getSupportedNumbers", "()Ljava/util/Set;"); jobject jSet = m_env->CallObjectMethod(m_javaModule, methodId); // 将Java的Set转换为C++的unordered_set<unsigned int> Numbers result; jclass setCls = m_env->GetObjectClass(jSet); jmethodID iteratorId = m_env->GetMethodID(setCls, "iterator", "()Ljava/util/Iterator;"); jobject iterator = m_env->CallObjectMethod(jSet, iteratorId); jclass iterCls = m_env->GetObjectClass(iterator); jmethodID hasNextId = m_env->GetMethodID(iterCls, "hasNext", "()Z"); jmethodID nextId = m_env->GetMethodID(iterCls, "next", "()Ljava/lang/Object;"); while (m_env->CallBooleanMethod(iterator, hasNextId)) { jobject obj = m_env->CallObjectMethod(iterator, nextId); jint num = m_env->CallIntMethod(obj, m_env->GetMethodID(m_env->GetObjectClass(obj), "intValue", "()I")); result.insert(static_cast<unsigned int>(num)); m_env->DeleteLocalRef(obj); } // 释放本地引用 m_env->DeleteLocalRef(iterator); m_env->DeleteLocalRef(iterCls); m_env->DeleteLocalRef(setCls); m_env->DeleteLocalRef(jSet); m_env->DeleteLocalRef(cls); return result; } bool handleNumber(unsigned int number) override { jclass cls = m_env->GetObjectClass(m_javaModule); jmethodID methodId = m_env->GetMethodID(cls, "handleNumber", "(I)Z"); jboolean result = m_env->CallBooleanMethod(m_javaModule, methodId, static_cast<jint>(number)); m_env->DeleteLocalRef(cls); return static_cast<bool>(result); } bool cancelNumber(unsigned int number) override { jclass cls = m_env->GetObjectClass(m_javaModule); jmethodID methodId = m_env->GetMethodID(cls, "cancelNumber", "(I)Z"); jboolean result = m_env->CallBooleanMethod(m_javaModule, methodId, static_cast<jint>(number)); m_env->DeleteLocalRef(cls); return static_cast<bool>(result); } private: JNIEnv* m_env; jobject m_javaModule; // 全局引用,避免GC回收 };
3. 实现JNI方法
编写JNI函数,处理Java侧的调用,完成NumberManager的初始化、模块注册等操作:
// 存储全局的JNIEnv引用(单线程示例,多线程需调整) static JNIEnv* g_env; // 初始化NumberManager extern "C" JNIEXPORT jlong JNICALL Java_com_example_NumberManagerJni_initNumberManager(JNIEnv* env, jobject thiz) { g_env = env; std::shared_ptr<NumberManagerInterface> manager = NumberManager::createNumberManagerInstance(); // 将智能指针转换为void*,传递给Java侧作为句柄 return reinterpret_cast<jlong>(manager.get()); } // 注册Java模块 extern "C" JNIEXPORT jboolean JNICALL Java_com_example_NumberManagerJni_registerNumberModule(JNIEnv* env, jobject thiz, jlong managerPtr, jobject module) { auto manager = reinterpret_cast<NumberManagerInterface*>(managerPtr); std::shared_ptr<NumberInterface> bridge = std::make_shared<JavaNumberBridge>(env, module); return static_cast<jboolean>(manager->registerNumberModule(bridge)); } // 处理数字 extern "C" JNIEXPORT jboolean JNICALL Java_com_example_NumberManagerJni_handleNumber(JNIEnv* env, jobject thiz, jlong managerPtr, jint number) { auto manager = reinterpret_cast<NumberManagerInterface*>(managerPtr); return static_cast<jboolean>(manager->handleNumber(static_cast<unsigned int>(number))); } // 取消数字处理 extern "C" JNIEXPORT jboolean JNICALL Java_com_example_NumberManagerJni_cancelNumber(JNIEnv* env, jobject thiz, jlong managerPtr, jint number) { auto manager = reinterpret_cast<NumberManagerInterface*>(managerPtr); return static_cast<jboolean>(manager->cancelNumber(static_cast<unsigned int>(number))); } // 释放NumberManager资源 extern "C" JNIEXPORT void JNICALL Java_com_example_NumberManagerJni_releaseNumberManager(JNIEnv* env, jobject thiz, jlong managerPtr) { auto manager = reinterpret_cast<NumberManagerInterface*>(managerPtr); // 根据NumberManager的内存管理方式释放,若用智能指针需确保引用计数归零 delete manager; }
4. Java侧实现与使用
实现Java侧的JavaNumberInterface,并通过NumberManagerJni与原生交互:
import java.util.HashSet; import java.util.Set; import java.util.Arrays; public class JavaNumberImpl implements JavaNumberInterface { @Override public Set<Integer> getSupportedNumbers() { // 返回Java侧支持的数字集合 return new HashSet<>(Arrays.asList(1, 3, 5)); } @Override public boolean handleNumber(int number) { System.out.println("Java处理数字:" + number); return true; } @Override public boolean cancelNumber(int number) { System.out.println("Java取消数字处理:" + number); return true; } } // 使用示例 public class Main { public static void main(String[] args) { NumberManagerJni managerJni = new NumberManagerJni(); long managerPtr = managerJni.initNumberManager(); JavaNumberImpl numberModule = new JavaNumberImpl(); managerJni.registerNumberModule(managerPtr, numberModule); // 调用处理数字 managerJni.handleNumber(managerPtr, 3); // 取消处理 managerJni.cancelNumber(managerPtr, 3); // 释放资源 managerJni.releaseNumberManager(managerPtr); } }
关键注意事项
- 全局引用与本地引用:JNI中持有Java对象时,必须使用
NewGlobalRef避免对象被GC回收,使用完后要调用DeleteGlobalRef释放。 - 类型转换:注意Java与C++的类型映射(如
jint对应int,jboolean对应bool),集合类需要手动转换。 - 多线程安全:如果是Android或多线程场景,不能直接存储全局
JNIEnv,需要通过AttachCurrentThread获取当前线程的JNIEnv。 - 内存管理:确保C++侧的智能指针和Java侧的对象引用正确释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者bourne
相关产品推荐
相关产品推荐

