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