使用JNI和Reflect实现Object向void*的自动转换之五:测试 [原]

来源:互联网 发布:两院院士 知乎 编辑:程序博客网 时间:2024/06/07 08:38

五、测试:享受和更好的享受

       好了,完成了这个包装,我们可以渐渐大胆的对这个设备进行测试了。

 

测试一:

首先拿个Integer来做测试。这个类的成员中,很多静态变量,但是核心的值是一个整型,定义形如:private int value

 

根据TDD的思想,先写JUnit的测试案例:

       public static void testInteger(){

              Integer i = new Integer(1);

              TestObjectPtrEntry.plusOneInteger(i);//调用本地方法

              assertEquals("1+1应该是2:",2,i.intValue());

       }

其中的本地方法函数为:

       native static void plusOneInteger(Object i);

对应的C++的实现:

void JNICALL Java_org_JAPI_test_TestObjectPtrEntry_plusOneInteger(JNIEnv * env, jclass that, jobject integer)

{

     ObjectPtr* op = new ObjectPtr(env,integer);

     //do change: i+=1

     int* pi = (int*)op->getBuffer();

     *pi += 1;

     op->updateObject();

     delete op;

}

OK,Green Bar! Junit的绿条出现,说明我们的测试通过,也标志着取得局部的胜利。

 

测试二:

我们混合intdouble,试图构造一个类似如下的javaC的对应关系:

              class IntDouble{//in java

                     double d;

                     int i;

              }

对应的C结构体为:

typedef struct tagIntDouble{

       double     d;

       int           i;

}IntDouble;

         测试用例为:

       public static void testIntDoubleStruct(){

              IntDouble id = new IntDouble();

              id.d = 10.0;

              id.i = 20;

              TestObjectPtrEntry.exchangeIntDouble(id);

              assertEquals("after exchange,i = 10",10,id.i);

              assertTrue("after exchange,d = 20.0",Math.abs(20.0 - id.d) < 0.001);

       }

对应的C++实现为:

void JNICALL Java_org_JAPI_test_TestObjectPtrEntry_exchangeIntDouble(JNIEnv * env, jclass that, jobject id)

{

       int isz = sizeof(int);

       int dsz = sizeof(double);

       int sz = sizeof(IntDouble);

       //assert(sz == 12);//pls set byte 1 as the struct align

       ObjectPtr* op = new ObjectPtr(env,id);

       IntDouble* pid = (IntDouble*)op->getBuffer();

       //do change:exchange int and double

       double d = pid->d;

       pid->d = pid->i;

       pid->i = d;

 

       op->updateObject();

       delete op;

}

很遗憾,运行测试用例没有成功,发现double域不正确,查找原因,发现Sizeof(IntDouble)的值为16,而不是期望的4+8=12,原来c的结构体对齐出了问题。好,在C的编译器中设置对齐属性为1byte即可。重新编译连接,运行测试,okpass!

联想一下,我们发现很多的WindowsAPI接口,要求传递的结构体,都是有讲究的,使用那些结构体,就不需要设定对齐属性。一些结构体中,故意设定一些保留的数据成员(reserved)就是这个目的。许多结构体第一个域要求添入结构体的大小,也有这方面的原因。

      

测试三:

再往下测试,使用字符串+数组测试,由于javaString类型的内部实现并不是一个字符串,所以还是不要用它直接测试了,我们可以构造一个char[]来测试。如下:

              public static void testString(){

              String str = "hello";

              char[] wchStr = str.toCharArray();

              TestObjectPtrEntry.converseString(wchStr);

              assertEquals("after converse hello is olleh", "olleh" ,new String(wchStr));

       }

本地方法声明为:

native static void converseString(Object str)

C++的本地方法实现被写成:

JNIEXPORT void JNICALL Java_org_JAPI_test_TestObjectPtrEntry_converseString(JNIEnv *env, jclass that, jobject str){

       ObjectPtr* op = new ObjectPtr(env,str);

       //as unicode char

       wchar_t *data = (wchar_t*)op->getBuffer();//注意到我们要使用宽字符串来操作传来的数组。

       int num = op->getMemberNum();

       for(int i = 0; i < num/2; i++){

              wchar_t temp = data[i];

              data[i] = data[num-i-1];

              data[num-i-1] = temp;

       }

       op->updateObject();

       delete op;

}

 

必然通过,我们越来越有信心了。Green means success!

 

测试四:

我们要让数据成员是object类型。这是最复杂的情况。

Java中构造的数据类型为:

              class IntObject{

                     int i;

                     Integer obj;

              }

期望的C结构为:

typedef    struct tagInt{

              int io;

       }Integer;

 

typedef struct tagIntInteger{

       int           i;

       Integer* pInt;//same as void* accord to java/lang/Object

}IntInteger;

我们的测试案例为:

       public static void testIntObjStruct(){

              IntObject io = new IntObject();

              io.i = 10;

              io.obj = new Integer(io.i*2);

              TestObjectPtrEntry.exchangeIntInteger(io);

              assertEquals("after exchange,i = 10",20,io.i);

              assertEquals("after exchange,integer = 20",10,io.obj.intValue());

       }

本地方法被实现为:

void JNICALL Java_org_JAPI_test_TestObjectPtrEntry_exchangeIntInteger(JNIEnv * env, jclass that, jobject id)

{

       ObjectPtr* op = new ObjectPtr(env,id);

       IntInteger* pid = (IntInteger*)op->getBuffer();

       //exchange int and object i

       int i = pid->i;

       pid->i = pid->pInt->io;

       pid->pInt->io = i;

       op->updateObject();

       delete op;

}

测试又一次成功,也是对我们工作的充分肯定。看来我们做的工作是有意义的。

 

下一步,就可以包装sendMessage这种类型的函数了。比如下面的一个对PostMessage的实现:

JNIEXPORT jint JNICALL Java_org_JAPI_WindowAPI_postMessage(JNIEnv *env, jobject window,  jint msg, jobject lObject, jobject wObject){

     HWND hWnd = Java_getHandle(env,window);

     if (hWnd == NULL){

         Java_Throw_IllegalArgumentException(env,NOT_SET_HANDLE_MESSAGE);

         return NULL;

     }

     ObjectPtr* lob = NULL;

     if(lObject != NULL){

         lob = new ObjectPtr(env,lObject);

     }

     ObjectPtr* wob = NULL;

     if(wObject != NULL){

         wob = new ObjectPtr(env,wObject);

     }

     LRESULT lRes = 0;

     if(lObject == NULL && wObject == NULL)

         lRes = ::PostMessage(hWnd,msg,NULL,NULL);

     if(lObject == NULL && wObject != NULL)

         lRes = ::PostMessage(hWnd,msg,NULL,(WPARAM)wob->getBuffer());

     else if(wObject == NULL && lObject != NULL)

         lRes = ::PostMessage(hWnd,msg,(LPARAM)lob->getBuffer(),NULL);

     else

         lRes = ::PostMessage(hWnd,msg,(LPARAM)lob->getBuffer(),(WPARAM)wob->getBuffer());

 

     //update object

     if(lob != NULL){

         lob->updateObject();

         delete lob;

     }

     if(wob != NULL){

         wob->updateObject();

         delete wob;

     }   

     return lRes;  //交给上层解析

}

好的,在包装了这些关键的函数后,我们发现使用java来进行Windows系统编程是如此的方便,高效,当然,不要忘了参考MSDN的协议定义,这是最关键的。

六、提高和构想

       有人会提出,这样包装是不是对C语言的运行速度优势的损害,我认为这点不用过分担心,在系统编程中,速度一般来说并不是主要的。

       也可以在java层上实现类似的包装,不过访问private成员就成了问题。

 

对于Windows的系统API的包装可以更加的自动化,规模更大,如果借助了LoadLibraryGetProcessAddress函数,可以动态的查找函数,不过参数要定义好了。这种包装,会更有挑战性,当然更加的自动。Windows编程部分中C++Java所存在的指针上的鸿沟,会更加方便的化解。实现一个“JMFC”,也不是难事。使用Java来操作Windows系统强大的界面,更是可望且可即的。

需要本文源码,可联系作者E-mail: hhhust@gmail.com

原创粉丝点击