在Android中调用C++其实就是在Java中调用C++代码,只是在windows下编译生成DLL,在Android中会生成Linux系统下的.so文件(好吧,其实我基本没用过Linux)。
没写过JNI的可以看看我之前的博客(Windows下利用Visual Studio开发的过程):http://cherishlc.iteye.com/admin/blogs/1328136

以及自动生成工具swig的使用方法(数组的支持不好!其他挺方便):http://cherishlc.iteye.com/admin/blogs/1689224

另外推荐一篇非常不错的NDK博文,(配置忽略,主要是各种数据的传递,下代码看看吧)http://vaero.blog.51cto.com/4350852/782787
扯远了,下面来看看真正在Android中的开发过程。

1、下载ADT及NDK

  • 下载ADT (Android Developer Tools) Bundle:http://developer.android.com/sdk/index.html
  • 下载NDKhttp://developer.android.com/tools/sdk/ndk/index.html


其中ADT中包含了Eclipse及google的开发套件,不用写C++的下载ADT就足够了。
NDK则是包含了GCC的编译器,以及各个平台(arm,X86,MIPS)的相关头文件,交叉编译的一些平台相关文件等。

2、在ADT中配置NDK路径
解压NDK压缩包到任意路径,按下图在ADT中(也即ADT解压后的Eclipse文件下的Eclipse中)设置NDK的路径。
设置方法如下图所示:


3、创建含有本地代码的Android Project

该过程分为以下两步:

  • 创建普通的Android Application工程(注意最小支持的API版本要不小于14)
  • 加入本地代码支持


具体过程如下图所示:
创建工程:


加入本地代码支持:


完成情况:


点击菜单栏Project->Build All命令进行编译。


注意:如果之前最小支持的API版本要不小于14,将出现编译错误。“Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 7 in ./AndroidManifest.xml”

解决方法如下:
打开AndroidManifest.xml,切换到源文件视图,将minSdkVersion 改为14以上:


4、编写Java端代码和C++端代码
Java端,注意不要继承自Android中的类,否则javah编译头文件时要指定android类路径。

Java代码
  1. packagecom.lc.testndk2;
  2. importandroid.util.Log;
  3. publicclassNativeClass{
  4. //数组a中的每个元素都加上b,返回值为在C++中数据是否为a中数据拷贝得到的(按值拷贝还是传递指针)
  5. publicstaticnativebooleanjniArrayAdd(int[]a,intb);
  6. //在C++中创建Java中的int数组,其中元素为数组a中的对应元素乘以b
  7. publicstaticnativeint[]jnitArrayMul(int[]a,intb);
  8. static{
  9. Log.i("NativeClass","beforeloadlibrary");
  10. System.loadLibrary("TestNDK2");//注意这里为自己指定的.so文件,无lib前缀,亦无后缀
  11. Log.i("NativeClass","afterloadlibrary");
  12. }
  13. }



javah推荐两种方法:

  • 使用我写的工具,含本地方法的类多时,一次编译完成:http://cherishlc.iteye.com/blog/1326893
  • 在Eclipse中配置外部工具,配置方法参考自(同时也是讲Android中JNI配置方法的博客):http://www.cnblogs.com/yemeishu/archive/2012/12/24/NDK%E5%BC%80%E5%8F%91.html


在Eclipse中配置javah外部工具方法为:


上图中最长的一行命令如下:

Cmdineclipse代码
  1. -v-classpath"${project_loc}/bin/classes"-d"${project_loc}/jni"${java_type_name}


配置好之后:

点刚才配置好的javah工具,生成.h文件,然后:



Java端调用JNI方法的代码:
将MainActivity改为:

Java代码
  1. packagecom.lc.testndk2;
  2. importjava.util.Arrays;
  3. importandroid.app.Activity;
  4. importandroid.os.Bundle;
  5. importandroid.widget.TextView;
  6. publicclassMainActivityextendsActivity{
  7. @Override
  8. protectedvoidonCreate(BundlesavedInstanceState){
  9. super.onCreate(savedInstanceState);
  10. TextViewtv=newTextView(this);
  11. int[]array=newint[]{1,2,3};
  12. Stringstr="数组,调用C++前"+Arrays.toString(array);
  13. booleanisCopyOfArrayInCpp=NativeClass.jniArrayAdd(array,1);
  14. str+="\n在C++中为副本?"+isCopyOfArrayInCpp;
  15. str+="\n数组,调用C++后:"+Arrays.toString(array);
  16. tv.setText(str);
  17. setContentView(tv);
  18. }
  19. }




编写C++代码:
打开刚才系统生成的TestNDK2.cpp,修改成如下样子:

C++代码
  1. #include<jni.h>
  2. #include"com_lc_testndk2_NativeClass.h"
  3. #ifdef__cplusplus//最好有这个,否则被编译器改了函数名字找不到不要怪我
  4. extern"C"{
  5. #endif
  6. /*
  7. *Class:com_lc_testndk2_NativeClass
  8. *Method:jinArrayAdd
  9. *Signature:([II)[I
  10. */JNIEXPORTjbooleanJNICALLJava_com_lc_testndk2_NativeClass_jniArrayAdd(
  11. JNIEnv*env,jclass,jintArrayarray,jintb){
  12. jsizesize=env->GetArrayLength(array);
  13. //jintArraysum=env->NewIntArray(2);
  14. jbooleanisCopy;
  15. jint*pArray=(jint*)env->GetPrimitiveArrayCritical(array,&isCopy);
  16. for(inti=0;i<size;i++)
  17. pArray[i]+=b;
  18. env->ReleasePrimitiveArrayCritical(array,pArray,JNI_COMMIT);
  19. //env->ReleasePrimitiveArrayCritical(sum,pSum,JNI_COMMIT);
  20. returnisCopy;
  21. }
  22. /*
  23. *Class:com_lc_testndk2_NativeClass
  24. *Method:jnitArrayMul
  25. *Signature:([II)[I
  26. */JNIEXPORTjintArrayJNICALLJava_com_lc_testndk2_NativeClass_jnitArrayMul(
  27. JNIEnv*env,jclass,jintArrayarray,jintb){
  28. jsizesize=env->GetArrayLength(array);
  29. jintArrayproduct=env->NewIntArray(size);
  30. jint*pArray=(jint*)env->GetPrimitiveArrayCritical(array,0);
  31. jint*pProduct=(jint*)env->GetPrimitiveArrayCritical(product,0);
  32. //jintArrayproduct=env->NewIntArray(size);//不能在这里创建!!因为上面的方法会使java进入criticalregion,在这里创建的话虚拟机直接崩溃
  33. for(inti=0;i<size;i++)
  34. pProduct[i]=pArray[i]*b;
  35. env->ReleasePrimitiveArrayCritical(array,pArray,JNI_COMMIT);
  36. env->ReleasePrimitiveArrayCritical(product,pProduct,JNI_COMMIT);
  37. returnproduct;
  38. }
  39. #ifdef__cplusplus
  40. }
  41. #endif



5、配置生成的.so文件的目标平台
Java是跨平台的可是C++生成动态链接文件不是!!!同是Android,底层的CPU架构不同,动态链接文件也不同。。。好吧,这个我不知道原因。。。
于是乎,还得为不同的CPU创建不同的动态链接库文件,好在一行命令搞定~所有的动态链接一起打包,管他是哪个CPU,统统适用,Happy啊。
参考自:http://bbs.csdn.net/topics/390158301
过程如下:

再编译时会发现生成了对应以上四个平台的.so文件~~~


一切搞定,可以运行了!!!运行结果如下:



6、Java与C++联合调试
参见:http://blog.csdn.net/wjr2012/article/details/7993722

注意:

  • C++的调试器有几秒的延迟才能启动好,也就是程序运行了一会儿才可以开始调试,所以要调试的代码一定要是几秒钟后才能调试!!!
  • 断点设置在C++中才有效。。。



过程为:
右键点击工程文件, 在properties -> C/C++ Build中:

完了设置断点(只能在C++中)就可以启动调试了~~



好吧,,,调试怎么不灵光呢。。。再想想刚才的注意事项。。。好吧,早执行完了JNI代码了。。。
于是乎,修改MainActivity代码如下:

Java代码
  1. packagecom.lc.testndk2;
  2. importjava.util.Arrays;
  3. importjava.util.Timer;
  4. importjava.util.TimerTask;
  5. importandroid.annotation.SuppressLint;
  6. importandroid.app.Activity;
  7. importandroid.os.Bundle;
  8. importandroid.os.Handler;
  9. importandroid.os.Message;
  10. importandroid.util.Log;
  11. importandroid.widget.TextView;
  12. /**
  13. *@authorLC
  14. *
  15. *完整的演示Android通过JNI调用C++代码的工程
  16. */
  17. publicclassMainActivityextendsActivity{
  18. TextViewtv=null;
  19. intcount=0;
  20. Timertimer;
  21. @SuppressLint("HandlerLeak")
  22. classMyHandlerextendsHandler{
  23. @Override
  24. publicvoidhandleMessage(Messagemsg){
  25. if(tv!=null){
  26. tv.setText(msg.getData().getString("text"));
  27. }
  28. super.handleMessage(msg);
  29. }
  30. };
  31. Handlerhandle=newMyHandler();
  32. classrefreshTaskextendsTimerTask{
  33. @Override
  34. publicvoidrun(){
  35. try{
  36. count++;
  37. Log.i("MainActivity","beforecallnativecode,count="
  38. +count);
  39. int[]array=newint[]{count,-count,2*count};
  40. Stringstr="第"+count+"次了\n";
  41. str+="数组,调用C++前"+Arrays.toString(array);
  42. booleanisCopyOfArrayInCpp=NativeClass.jniArrayAdd(array,1);
  43. str+="\n在C++中为副本?"+isCopyOfArrayInCpp;
  44. str+="\n数组,调用C++后:"+Arrays.toString(array)+"\n\n";
  45. str+="测试在C++中创建数组:\n";
  46. str+=Arrays.toString(array)+"*2=";
  47. str+=Arrays.toString(NativeClass.jnitArrayMul(array,2))+"\n\n";
  48. Messagemsg=newMessage();
  49. Bundleb=newBundle();
  50. b.putString("text",str);
  51. msg.setData(b);
  52. handle.sendMessage(msg);
  53. Log.i("MainActivity","aftercallnativecode");
  54. }catch(Exceptione){
  55. Log.i(MainActivity.class.getSimpleName(),e.toString());
  56. e.printStackTrace();
  57. }
  58. }
  59. };
  60. @Override
  61. protectedvoidonCreate(BundlesavedInstanceState){
  62. super.onCreate(savedInstanceState);
  63. tv=newTextView(this);
  64. tv.setText("我是初始值");
  65. setContentView(tv);
  66. }
  67. @Override
  68. protectedvoidonPause(){
  69. Log.i(MainActivity.class.getSimpleName(),"onPuase()");
  70. timer.cancel();
  71. timer=null;
  72. super.onPause();
  73. }
  74. @Override
  75. protectedvoidonResume(){
  76. Log.i(MainActivity.class.getSimpleName(),"onResume()");
  77. timer=newTimer();
  78. timer.scheduleAtFixedRate(newrefreshTask(),0,1000);
  79. super.onResume();
  80. }
  81. }



运行到断点的结果:

更多相关文章

  1. Android有用代码片断(五)
  2. Android内核的简单分析
  3. Android学习笔记之开发必备
  4. Android(安卓)file类使用详解-SDcard
  5. HTML---Android与js交互实例
  6. Android中Javascript中的调用
  7. 索引:Android(安卓)Studio安装及工程项目目录简介
  8. Android入门教程五十五之DrawerLayout(官方侧滑菜单)的简单使用
  9. android第一天

随机推荐

  1. 如何用Python递归地思考问题?
  2. Scrapy框架的使用之Scrapy框架介绍
  3. 还在抱怨pandas运行速度慢?这几个方法会颠
  4. 5个原因告诉你:为什么在成为数据科学家之
  5. 高效方法 | Jupyter Notebook 比你想象中
  6. 金州勇士4年3冠的成功秘诀!数据可视化分析
  7. 分布式爬虫的部署之Scrapyd对接Docker
  8. Scrapy框架的使用之Scrapyrt的使用
  9. OpenCV:模板匹配。
  10. OpenCV:人脸检测。