(原)android的JNI中使用C++的类

android的JNI代码中可以调用C++的类,但是不能直接调用,要加上一个类似于接口的java类,这个类内部调用C++的类。实际上和接口类直接调用C++中的函数差不多,只是稍微复杂了一点。

 

1. 写一个简单的类(一直都是用VS自动生成的类,很少自己写一个类,因而此处也是用VS生成类,然后复制到Eclipse工程的jni目录下。)该类包含4个函数:

a带参数的构造函数,用于初始化类中的变量。

b析构函数,用于释放类中的指针(数组)

c求和函数calcSum

d求平均值函数calcMean

 

下面的代码是VS转到JNI后编译通过的代码(VS生成的可能需要稍微修改一点)

 1 // myMeanSum.h文件
 2 #pragma once
 3 
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 
 7 class myMeanSum
 8 {
 9 public:
10     myMeanSum();
11     ~myMeanSum();
12     myMeanSum(int* data, int num);
13     
14     double calcMean();
15     int calcSum();
16 
17 private:
18     int m_number;
19     int* databuf;
20     double m_mean;
21     int m_sum;
22 };
 1 // myMeanSum.cpp文件
 2 
 3 #include "myMeanSum.h"
 4 
 5 myMeanSum::myMeanSum()
 6 : m_mean(0)
 7 , m_sum(0)
 8 , m_number(0)
 9 {
10     databuf = new int[10];
11 }
12 
13 myMeanSum::myMeanSum(int* data, int num)
14 : m_mean(0)
15 , m_sum(0)
16 , m_number(0)
17 {
18     m_number = num;
19     databuf = new int[m_number];
20     memcpy(databuf, data, sizeof(int)*m_number);
21 }
22 
23 
24 myMeanSum::~myMeanSum()
25 {
26     if (databuf!=NULL)
27     {
28         delete[] databuf;
29         databuf = NULL;
30     }
31 }
32 
33 
34 double myMeanSum::calcMean()
35 {
36     m_sum = calcSum();
37     return m_sum / 1.0 / m_number;  // / 1.0用来保证int类型自动转换成double类型。
38 }
39 
40 
41 int myMeanSum::calcSum()
42 {
43     m_sum = 0;
44     for (int i = 0; i < m_number; i++)
45     {
46         m_sum += databuf[i];
47     }
48     return m_sum;
49 }

2. 在Eclipse中创建新的android工程testClass。

3. 添加JNI文件AndroidClass.cpp。

4. 添加接口文件AndroidClass.java。

5. 在AndroidClass.java中添加long类型的指针,用于存储类的地址private long ptr_;

6. 在AndroidClass.java添加AndroidClass的构造函数:

1 public AndroidClass(int[] data, int num)
2 {
3     ptr_=AndroidClassGen(data, num);
4 }

7. 在AndroidClass.java添加6中函数调用的函数接口:

private native long AndroidClassGen(int[] data, int num);

8. 在AndroidClass.cpp添加7中调用class的接口函数。

 1 JNIEXPORT jlong JNICALL Java_com_example_testclass_AndroidClass_AndroidClassGen
 2   (JNIEnv *env, jobject obj, jintArray data, jint num)
 3 {
 4     jint *iAdata = env->GetIntArrayElements(data, 0);
 5 
 6     jlong ptr= reinterpret_cast<jlong>(new myMeanSum(reinterpret_cast<int*>(iAdata), num));
 7 
 8     env->ReleaseIntArrayElements(data, iAdata, 0);
 9 
10     return ptr;
11 }

9. 在AndroidClass.java添加释放内存的函数AndroidClassFree,感觉这两个函数名字不一定需要相同(相同没有错误,不相同没测试)。

1 public void AndroidClassFree()
2 {
3     AndroidClassFree(ptr_);
4 }
private native void AndroidClassFree(long ptr);

10. 在AndroidClass.cpp添加9中调用class的接口函数。

1 JNIEXPORT void JNICALL Java_com_example_testclass_AndroidClass_AndroidClassFree
2   (JNIEnv *, jobject, jlong ptr)
3 {
4     if(reinterpret_cast<myMeanSum*>(ptr))
5     {
6         delete reinterpret_cast<myMeanSum*>(ptr);
7         ptr = 0;
8     }
9 }

11. 在android.mk中添加需要编译的C++类的源文件(默认是LOCAL_SRC_FILES := AndroidClass.cpp):

LOCAL_SRC_FILES := AndroidClass.cpp myMeanSum.cpp

12. 分别添加其他的代码,此处略去,详见工程:

https://github.com/darkknightzh/testClass

 

说明:AndroidClassGetPtr程序不使用,如果需要在外部的AndroidClass定义的对象A使用对象B,则使用这个函数传入 B的指针。

 

测试步骤:

1. 在VS中添加如下简单的测试代码:

1 int number = 10;
2 int* data = new int[number];
3 for (int i = 0; i < number; i++)
4 {
5     data[i] = i;
6 }
7 myMeanSum temp(data, number);
8 printf("%d\n", temp.calcSum());
9 printf("%f\n", temp.calcMean());

输出为

45
4.500000

2. 在Eclipse的MainActivity.java的onCreate中添加如下测试代码:

 1 int number = 10;
 2 int[] data = new int[number];
 3 for (int i = 0; i < number; i++)
 4 {
 5       data[i] = i;
 6 }
 7 
 8 AndroidClass test=new AndroidClass(data,number);
 9 System.out.println("test-----"+test.calcSum());
10 System.out.println("test-----"+test.calcMean());        
11 test.AndroidClassFree();

结果如下:

证明程序正确。

 

3. 为了证明class中的析构函数正确被调用,databuf申请的内存能正常释放,填写如下测试代码:

 1  int number = 1000*1000;
 2 int[] data = new int[number];
 3 for (int i = 0; i < number; i++)
 4 {
 5         data[i] = i;
 6 }
 7 
 8 for(int i=0;i<1000;i++)
 9 {
10      AndroidClass test=new AndroidClass(data,number);
11      System.out.println("test-----"+i+"   "+test.calcSum());
12      System.out.println("test-----"+i+"   "+test.calcMean());        
13       test.AndroidClassFree();
14 }

程序执行完没有出错。证明析构函数能正确的执行,databuf申请的内存能正常释放。

 

4. 注释掉test.AndroidClassFree();后,程序跑到128次的时候崩溃,之后继续从0开始跑。证明AndroidClassFree函数正确的执行了析构函数。

 

5. 跑上面3的代码,但是myMeanSum类中的析构函数里面的代码全部注释掉,程序跑到128次后依旧崩溃,证明AndroidClassFree函数正确的执行了析构函数。

 

ps,上面程序只是一个简单的例子,可能有不完善的地方,同时代码写得也不是很好。

 

参考网址(貌似不能愉快的访问,感谢原作者,以及不能愉快的访问的搜索引擎):

http://stylishtoupee.blogspot.jp/2011/07/jni-example-with-class-instantiation-c.html

 

posted on 2015-03-25 16:59  darkknightzh  阅读(1528)  评论(0编辑  收藏  举报

导航