1.基本思想:
1.1 使对象对应
每出现一个C++类就写一个JAVA类, JAVA类的行为完全模仿C++类的行为. 而且要保证C++的对象的生存期至少比JAVA长, 而且能够保证使所有动态分配出来的C++对象有机会得到析构。
实现的方法是在每个JAVA类中添加一个int 类型的变量 ptr(参见表2.1中的part4),ptr指向该JAVA类所对应的C++对象。ptr并不在JAVA中使用,它只在本地方法中通过转换成相应的C++对象,来实现对C++调用(请参考表3.1 示例)。
1.2 关于C++对象和JAVA对象的生存期问题的解决:
对于C++对象的生成有两种情况
1.2.1第一种情况,新的C++对象在原C++系统内部管理,在JAVA中我们不关心它的创建和释放
C++对象是由原系统(C++)内部生成,然后返回给外部调用最后再由原C++系统释放。在这种情况下,我们新建一个对应的JAVA类,然后把这个C++对象的地址赋给该JAVA类的内部成员变量ptr(通过带有参数(FromReturn ,int exestPtr)的构造函数实现,其中FromReturn是一个空类用于标注不要创建C++对象,参见表2.1)。
(按目前的分析,这种情况仅限于在原系统中返回值是C++指针的情况)
1.2.2第二种情况,新的C++对象在外部创建
是除第一种情况之外的(动态创建和静态创建都包括)其它所有情况
(1)若原C++类提供了构造函数,则在相应的JAVA类中效仿C++中的构造函数将它改造成JAVA格式。然后在此JAVA构造函数中调用Part3中的服务接口函数,该服务接口函数(JNI)在相应的本地方法中创建新的C++对象(并将它放进全局的对象管理类, 见本条末)。 在finallize的接口服务函数(JNI)的本地方法中释放这个C++对象,但是JAVA并不保证finallize一定执行,我们通过在JNI的本地实现中声明一个全局的对象管理类来解决,当新分配一个C++对象时我们把它放进这个管理类,当finallize 执行时我们告诉这个管理类让它释放掉这个类。最后我们在这个管理类的析构函数中释放掉所有未能被释放的C++对象。(该类参见表2.2CObjectManager类)
(2)若原C++类提没有供了构造函数则我们在JAVA端提供一个空参数的构造函数(示例3.1中的JA),其余同(1)
注:每个JAVA类都有4个部分Part1, Part2, P art3, Part4
1.3 关于关于多态问题的解决:
当C++中的函数返回类型为基类类型的指针而实际返回的是C++子类的对象时,相应的转到JAVA后也要返回的是JAVA基类类型但是要返回实际的JAVA子类对象,这时需要在C++端的基类中提供一个GetType函数在JAVA端根据不同的类型返回不同的JAVA子对象
2.约定
2.1类名
JAVA系统内类名与C++类的类名一致,方法格式一致
2.2关于类的创建:
参见1.2
2.3每个JAVA类都有四个部分.如表2.1所示。(此处以类JA为例)
其中用到一个全局的类
public class FromReturn(){}
是一个空类,用在传给JAVA类的构造函数的参数,用于标注不要在构造函数中创建C++对象。这种情况适用于在原C++系统中返回一个类的情况。
表2.1对JAVA类的约定 对JAVA类的约定(以类JA为例),共分四个部分Parts1和 Part2中的方法除 JA(FromReturn ,int exestPtr)这个构造函数外,其余的函数都要在Part3中的函数来实现,即Par3为Part1和Part2提供服务,而Part3通过JNI调用本地方法 public class JA { //Part 1 construct与finallize,必须提供================================================= protected JA(FromReturn ,int exestPtr){//所有的类必须提供这个构造方法,除了类名不一样外其余部分完 ptr =existPtr; //全一样这个构造函数并不需要JNI,仅供内部使用,除这个构造函数外其 isFromReturn=true; //它在本部分的方法都需要JNI,该方法内容十分固定,所有类必须遵守 } public JA(){ptr=nJA(); isFromReturn =false;}// public synchronized finalize(){if(! isFromReturn )nfinallize(ptr);} //Part 2必须提供,与c++中暴露的方法一至,但要对应到JAVA格式======================== public SomeClass SomeMethod(){//有返回,对象类型 int ptrB; ptrB = nSomeMethod (ptr); SomeClass b=SomeClass (new FromReturn(), ptrB); Return b; } public int SomeMethod2(){return nSomeMethod2( ptr); }//有返回,基本类型(以int为例) public void SomeMethod3(){ nSomeMethod3( ptr); }//无返回 //以上几个示例函数只是针对在原C++系统中没有传入参数的情况,对于在原C++系统中有传入参数的情 //况,根据JNI的特性直接传即可。只是对在原C++系统中传入的参数是基本类型的引用或是基本类型的指针 //指针的情况有所不同(因为这些参数有可能被改变),解决的办法是将每个参数放进容量等于1的数组里 //再传 //Part 3访问权限一至protected native提供本类供part1和part2中方法调用的本地代码的声明注意本部分不对//外暴露任何东西,命名的方法是在 part1和part2暴露方法的的名字前加一个n,当返回的是对象或结构时,//要返回它的在C++中的指针,在JAVA端用int表示。每个方法声明的第一个参数都是一至的,都本类的prt属性(服务于构造函数的JNI声明除外——在本地端动态获得这个ptr,并把新分配的C++对象指针赋于它) //=========================================================================== //针对part1 private native int nJA();//注:这里没有传入ptr,返回的值被赋给ptr。在本地C++端nJA new一个新的C++ //对象,并将其指针放入CObjectManager中 private native void nfinallize(int ptr); //针对 part2 ,在part2中有什么样的函数这里就有相对应的服务函数声明 private native int nSomeMethod (int ptr );//有返回,对象类型 private native int nSomeMethod2 (int ptr );//有返回,基本类型(以int为例) private native void nSomeMethod3 (int ptr );//无返回 //Part 4 ================================================= publicintptr;//指向对应的C++对象的指针 privatebooleanisFromReturn;//参见第4部分修改说明 }
表2.2本地全局管理类CObjectManager
CObjectManager //注:在真实环境下使用时,Add方法要包含类的信息,在析构时也要根据类的信息把VOID*类型强制转换成目标类型后再析构 class CObjectManager{ public: Add(void * cppObj);//添加类地址到objs中 Remove(void* cppObj);//释放cppObj对象,并将其在objs中移除 CObjectManager();//构造函数,初始化objs ~ CObjectManager();//析构函数,若objs不空,则释放objs中的所有对象,并将objs清空 private: map<int , void*>objs;//键和值为同一值 }
3 示例
表3.1. 示例
JAVA JNI C++ public class JA { //Part 1 construct与finallize protected JA(FromReturn,int exestPtr){ ptr =existPtr; isFromReturn=true; } public JA(){ ptr=nJA(); isFromReturn=false; }// Public synchronized finalize() {if(!isFromReturn)nfinallize(ptr);} //Part 2 必须提供,与c++中暴露的方法一至,但要对应到JAVA格式 public JB GetB(){ int ptrB; ptrB=nGetB(ptr); JB b=new JB(new FromReturn(),ptrB); return b; } //Part 3访问权限一至protected native 提供本类供part1和part2中方法调用的JNI的声明注意本部分不对外暴露任何东西 //针对part1 private native int nJA(); private native void nfinallize(int ptr); //针对 part2 private native int nGetB(ptr); //Part 4 指向对应的C++对象的指针 protected int ptr; privatebooleanisFromReturn; } extern CObjectManager g_om; JNIEXPORT void JNICALL Java_mypack_myTest_ nGetB (JNIEnv * env, jobject obj,jint jPtr ) { A* ptrA=(A*)jPtr; B* ptrB=ptrA->GetB(); return (int)ptrB; }
//如果返回的B对象不是指针类型而是对象本身(在右边一栏的函数声明类似这样B GetB();)则以上函数中的代码应如下所示 A* ptrA=(A*)jPtr; B *ptrB=new B(); (*ptrB) = ptrA ->GetB(); g_om.Add(ptrB); return (int)ptrB; //且nfinallize要做如下实现 JNIEXPORT void JNICALL Java_mypack_myTest_ nfinallize (JNIEnv * env, jobject obj,jint jPtr ) { g_om.Remove(jPtr); } //其它函数略 .h文件 class A{ public: B* GetB(); }; .cpp文件 B* A::GetB() { return new B(); }//这个地方新分配了一个对象而且返回了它的指针,我们在JAVA调用的时候不必关心它的释放问题,因为只要我们JAVA端完全仿效C++运行流程的话,C++系统会在自己内部释放该对象 public class JB { //Part 1 //略 //Part 2 public void TestB(){ nTestB(ptr); } //Part 3 private native void nTestB(int ptr); //其它略 //Part 4 protected int ptr; } extern CObjectManager g_om; JNIEXPORT void JNICALL Java_mypack_myTest_ nTestB (JNIEnv * env, jobject obj,jintjPtr) { B* ptr=(B*)jPtr; ptr->TestB(); } .h文件 class B{ public: void TestB(); }; // .cpp文件 void B::TestB() { cout<<”this is B in C++”<<endl; } 结果测试: 在JAVA中这样运行 假设原C++系统中这样运行 JB b=a.GetB(); b.TestB(); B* pb=a->GetB();//这个地方的”->”也可能是”.” pb->TestB(); 输出: this is B in C++ this is B in C++
4 修改说明
1. 在每个JAVA类的PART4中添加 protected Boolean isFromReturn; 这个变量只在PART1中使用,而且在所有构造函数及finalize中使用。
2. finalize会存在同步问题。所以要加上synchronized
下面为一个例子
// part1
public CAnalyseRead(FromReturn obj,int exptr){
ptr = exptr;
isFromReturn=true;
}
public CAnalyseRead(){
ptr = nCAnalyseRead();
isFromReturn=false;
}
publicsynchronizedvoid finalize(){
//System.out.println("CAnalyseRead.finalize");
if(!isFromReturn)
nfinalize(ptr);
}
// part4
publicintptr;
privatebooleanisFromReturn;
乔成磊 qiaochenglei@163.com