原文网址:http://cherishlc.iteye.com/blog/1756762

在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中包含了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类路径。

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

javah推荐两种方法:

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

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

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

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

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

  1. package com.lc.testndk2;
  2. import java.util.Arrays;
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.widget.TextView;
  6. public class MainActivity extends Activity {
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. TextView  tv = new TextView(this);
  11. int[] array = new int[] { 1, 2,  3};
  12. String str = "数组,调用C++前" + Arrays.toString(array);
  13. boolean isCopyOfArrayInCpp = 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,修改成如下样子:

  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. */JNIEXPORT jboolean JNICALL Java_com_lc_testndk2_NativeClass_jniArrayAdd(
  11. JNIEnv * env, jclass, jintArray array, jint b) {
  12. jsize size = env->GetArrayLength(array);
  13. //  jintArray sum=env->NewIntArray(2);
  14. jboolean isCopy;
  15. jint* pArray = (jint*) env->GetPrimitiveArrayCritical(array, &isCopy);
  16. for (int i = 0; i < size; i++)
  17. pArray[i] += b;
  18. env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);
  19. //env->ReleasePrimitiveArrayCritical(sum,pSum,JNI_COMMIT);
  20. return isCopy;
  21. }
  22. /*
  23. * Class:     com_lc_testndk2_NativeClass
  24. * Method:    jnitArrayMul
  25. * Signature: ([II)[I
  26. */JNIEXPORT jintArray JNICALL Java_com_lc_testndk2_NativeClass_jnitArrayMul(
  27. JNIEnv * env, jclass, jintArray array, jint b) {
  28. jsize size = env->GetArrayLength(array);
  29. jintArray product = env->NewIntArray(size);
  30. jint* pArray = (jint*) env->GetPrimitiveArrayCritical(array, 0);
  31. jint* pProduct=(jint*)env->GetPrimitiveArrayCritical(product,0);
  32. //  jintArray product = env->NewIntArray(size); //不能在这里创建!!因为上面的方法会使java进入critical region, 在这里创建的话虚拟机直接崩溃
  33. for (int i = 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. return product;
  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代码如下:

  1. package com.lc.testndk2;
  2. import java.util.Arrays;
  3. import java.util.Timer;
  4. import java.util.TimerTask;
  5. import android.annotation.SuppressLint;
  6. import android.app.Activity;
  7. import android.os.Bundle;
  8. import android.os.Handler;
  9. import android.os.Message;
  10. import android.util.Log;
  11. import android.widget.TextView;
  12. /**
  13. * @author LC
  14. *
  15. *完整的演示Android通过JNI调用C++代码的工程
  16. */
  17. public class MainActivity extends Activity {
  18. TextView tv = null;
  19. int count = 0;
  20. Timer timer;
  21. @SuppressLint("HandlerLeak")
  22. class MyHandler extends Handler{
  23. @Override
  24. public void handleMessage(Message msg) {
  25. if (tv != null) {
  26. tv.setText(msg.getData().getString("text"));
  27. }
  28. super.handleMessage(msg);
  29. }
  30. };
  31. Handler handle=  new MyHandler();
  32. class refreshTask extends TimerTask {
  33. @Override
  34. public void run() {
  35. try {
  36. count++;
  37. Log.i("MainActivity", "before call native code,count="
  38. + count);
  39. int[] array = new int[] { count, -count, 2*count };
  40. String str = "第" + count + "次了\n";
  41. str += "数组,调用C++前" + Arrays.toString(array);
  42. boolean isCopyOfArrayInCpp = 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. Message msg=new Message();
  49. Bundle b=new Bundle();
  50. b.putString("text", str);
  51. msg.setData(b);
  52. handle.sendMessage(msg);
  53. Log.i("MainActivity", "after call native code");
  54. } catch (Exception e) {
  55. Log.i(MainActivity.class.getSimpleName(), e.toString());
  56. e.printStackTrace();
  57. }
  58. }
  59. };
  60. @Override
  61. protected void onCreate(Bundle savedInstanceState) {
  62. super.onCreate(savedInstanceState);
  63. tv = new TextView(this);
  64. tv.setText("我是初始值");
  65. setContentView(tv);
  66. }
  67. @Override
  68. protected void onPause() {
  69. Log.i(MainActivity.class.getSimpleName(),"onPuase()");
  70. timer.cancel();
  71. timer=null;
  72. super.onPause();
  73. }
  74. @Override
  75. protected void onResume() {
  76. Log.i(MainActivity.class.getSimpleName(),"onResume()");
  77. timer=new Timer();
  78. timer.scheduleAtFixedRate(new refreshTask(), 0, 1000);
  79. super.onResume();
  80. }
  81. }

运行到断点的结果: 

【转】Android 学习笔记——利用JNI技术在Android中调用、调试C++代码的更多相关文章

  1. 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数

    1.                 打开Eclipse,File-->New-->Project…-->Android-->AndroidApplication Projec ...

  2. [转][android][利用JNI技术在Android中调用、调试C++代码]

    在Android中调用C++其实就是在Java中调用C++代码,只是在windows下编译生成DLL,在Android中会生成Linux系统下的.so文件(好吧,其实我基本没用过Linux). 没写过 ...

  3. Android学习笔记————利用JDBC连接服务器数据库

    /******************************************************************************************** * auth ...

  4. android学习笔记——利用BaseAdapter生成40个列表项

    RT: main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns ...

  5. 利用JNI技术在Android中调用、调试C++代码

    参考:http://blog.micro-studios.com/?p=4212 代码:http://pan.baidu.com/s/1sjukSDf

  6. 【转】Pro Android学习笔记(一):Android 平台 2013.6.4

    本系列是阅读<Pro Android4>的读书笔记,也包括网络阅读资料的整理,以及个人心得. 由于智能手机引入AP(应用处理器),Android在某种意义上是个人计算机,具有桌面计算机的完 ...

  7. Android学习笔记之布局技巧以及布局中的细节介绍....

    PS:休息两天,放一放手上的东西,做做总结... 学习内容: 1.Android中LinearLayout布局技巧... 2.layout中drawable属性的区别...   先简单的介绍一下dra ...

  8. Android学习笔记一之第一个Android程序

    /** *Title:总结昨天下午至今天上午的学习成果 *Author:zsg *Date:2017-8-13 / 一.了解Android 1.Android架构 Android大致可分为四层架构:L ...

  9. Android学习笔记——保存数据到SQL数据库中(Saving Data in SQL Databases)

    知识点: 1.使用SQL Helper创建数据库 2.数据的增删查改(PRDU:Put.Read.Delete.Update) 背景知识: 上篇文章学习了保存文件,今天学习的是保存数据到SQL数据库中 ...

随机推荐

  1. Boot Petalinux Project Using a remote system

    通过jtag实现在远程服务器端下载petalinux image到连接在本地PC的开发板上的方法. 具体连接方式为 比如Host的系统为Windows,Remote system为运载在远程服务器上的 ...

  2. 高并发编程陷阱之check and set

    今天公司CTO跟隔壁部门开技术会,旁听了一下.所讲的内容感觉好高大上啊!简单记录一下 场景是这样的: if(check(id)===true) { }else{ set(id); } 什么意思呢? 就 ...

  3. iOSCoreData介绍

    1.CoreData简介 Coredata用作数据持久化,使和大数据量的存储和查询 虽然是用户做数据的保存,但是并不是数据库,CoreData可以使用数据库.XML来存储数据 SQLite通过SQL语 ...

  4. opensuse13.1 双屏幕扩展

    最近搞一项j2eeweb工程,想要使用Linux平台编写程序.对比Ubuntu 14.04.Redhat.openSUSE,选择了最后这个. 安装的时候将/boot只分了100M,后来空间不足软件都安 ...

  5. Android开发系列之SQLite

    上篇博客提到过SQLite,它是嵌入式数据库,由于其轻巧但功能强大,被广泛的用于嵌入式设备当中.后来在智能手机.平板流行之后,它作为文件型数据库,几乎成为了智能设备单机数据库的必选,可以随着安卓app ...

  6. Jquery ajax使用json形式通信

    前台JS $.ajax({                              type: 'post',                              url: 'HandlerL ...

  7. C# 实现对窗体(Form)换肤

    http://www.csharpwin.com/csharpresource/2992.shtml 一直想写一个比较完整的.容易扩展的窗体换肤的方案,由于时间问题,都没去实现这个想法.现在有朋友提出 ...

  8. winfrom 多语言切换

    1.首先将窗体的“Localizable”属性置为“True”,然后将“Language”属性置为自己想要的语言,点击重新生成项目 例如:置为“中文”,以及“英文”.当每次置为不同的语言并重新生成项目 ...

  9. 前端资源多个产品整站一键打包&包版本管理(三)—— gulp分流

    问题: 当我们一个工作台里面有好几个项目的时候,我们要为项目的前端资源进行打包,但是,gulpfile只有一个,如果我们把所有的打包都放在同一个文件里面,首先文件会越来越大,而且不便于管理,这时,我们 ...

  10. jQuery实现鼠标移到元素上动态提示消息框效果

    当光标移动到某些元素上时,会弹出像tips的提示框,这种效果想必大家都有见到过吧,下面有个不错的示例,大家可以感受下 当光标移动到某些元素上时,会弹出像tips的提示框. 复制代码代码如下: < ...