[转][android][利用JNI技术在Android中调用、调试C++代码]
在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类路径。
- package com.lc.testndk2;
- import android.util.Log;
- public class NativeClass {
- //数组a中的每个元素都加上b,返回值为在C++中数据是否为a中数据拷贝得到的(按值拷贝还是传递指针)
- public static native boolean jniArrayAdd(int[] a, int b);
- // 在C++中创建Java中的int数组,其中元素为 数组a中的对应元素乘以b
- public static native int[] jnitArrayMul(int[] a,int b);
- static {
- Log.i("NativeClass","before load library");
- System.loadLibrary("TestNDK2");//注意这里为自己指定的.so文件,无lib前缀,亦无后缀
- Log.i("NativeClass","after load library");
- }
- }
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外部工具方法为:

上图中最长的一行命令如下:
- -v -classpath "${project_loc}/bin/classes" -d "${project_loc}/jni" ${java_type_name}
配置好之后:
点刚才配置好的javah工具,生成.h文件,然后: 
Java端调用JNI方法的代码:
将MainActivity改为:
- package com.lc.testndk2;
- import java.util.Arrays;
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.TextView;
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- TextView tv = new TextView(this);
- int[] array = new int[] { 1, 2, 3};
- String str = "数组,调用C++前" + Arrays.toString(array);
- boolean isCopyOfArrayInCpp = NativeClass.jniArrayAdd(array, 1);
- str += "\n在C++中为副本? " + isCopyOfArrayInCpp;
- str += "\n数组,调用C++后:" + Arrays.toString(array);
- tv.setText(str);
- setContentView(tv);
- }
- }
编写C++代码:
打开刚才系统生成的TestNDK2.cpp,修改成如下样子:
- #include <jni.h>
- #include "com_lc_testndk2_NativeClass.h"
- #ifdef __cplusplus //最好有这个,否则被编译器改了函数名字找不到不要怪我
- extern "C" {
- #endif
- /*
- * Class: com_lc_testndk2_NativeClass
- * Method: jinArrayAdd
- * Signature: ([II)[I
- */JNIEXPORT jboolean JNICALL Java_com_lc_testndk2_NativeClass_jniArrayAdd(
- JNIEnv * env, jclass, jintArray array, jint b) {
- jsize size = env->GetArrayLength(array);
- // jintArray sum=env->NewIntArray(2);
- jboolean isCopy;
- jint* pArray = (jint*) env->GetPrimitiveArrayCritical(array, &isCopy);
- for (int i = 0; i < size; i++)
- pArray[i] += b;
- env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);
- //env->ReleasePrimitiveArrayCritical(sum,pSum,JNI_COMMIT);
- return isCopy;
- }
- /*
- * Class: com_lc_testndk2_NativeClass
- * Method: jnitArrayMul
- * Signature: ([II)[I
- */JNIEXPORT jintArray JNICALL Java_com_lc_testndk2_NativeClass_jnitArrayMul(
- JNIEnv * env, jclass, jintArray array, jint b) {
- jsize size = env->GetArrayLength(array);
- jintArray product = env->NewIntArray(size);
- jint* pArray = (jint*) env->GetPrimitiveArrayCritical(array, 0);
- jint* pProduct=(jint*)env->GetPrimitiveArrayCritical(product,0);
- // jintArray product = env->NewIntArray(size); //不能在这里创建!!因为上面的方法会使java进入critical region, 在这里创建的话虚拟机直接崩溃
- for (int i = 0; i < size; i++)
- pProduct[i] =pArray[i]* b;
- env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);
- env->ReleasePrimitiveArrayCritical(product,pProduct,JNI_COMMIT);
- return product;
- }
- #ifdef __cplusplus
- }
- #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代码如下:
- package com.lc.testndk2;
- import java.util.Arrays;
- import java.util.Timer;
- import java.util.TimerTask;
- import android.annotation.SuppressLint;
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.util.Log;
- import android.widget.TextView;
- /**
- * @author LC
- *
- *完整的演示Android通过JNI调用C++代码的工程
- */
- public class MainActivity extends Activity {
- TextView tv = null;
- int count = 0;
- Timer timer;
- @SuppressLint("HandlerLeak")
- class MyHandler extends Handler{
- @Override
- public void handleMessage(Message msg) {
- if (tv != null) {
- tv.setText(msg.getData().getString("text"));
- }
- super.handleMessage(msg);
- }
- };
- Handler handle= new MyHandler();
- class refreshTask extends TimerTask {
- @Override
- public void run() {
- try {
- count++;
- Log.i("MainActivity", "before call native code,count="
- + count);
- int[] array = new int[] { count, -count, 2*count };
- String str = "第" + count + "次了\n";
- str += "数组,调用C++前" + Arrays.toString(array);
- boolean isCopyOfArrayInCpp = NativeClass.jniArrayAdd(array,1);
- str += "\n在C++中为副本? " + isCopyOfArrayInCpp;
- str += "\n数组,调用C++后:" + Arrays.toString(array)+"\n\n";
- str+="测试在C++中创建数组:\n";
- str += Arrays.toString(array)+"* 2 =";
- str+=Arrays.toString(NativeClass.jnitArrayMul(array, 2))+"\n\n";
- Message msg=new Message();
- Bundle b=new Bundle();
- b.putString("text", str);
- msg.setData(b);
- handle.sendMessage(msg);
- Log.i("MainActivity", "after call native code");
- } catch (Exception e) {
- Log.i(MainActivity.class.getSimpleName(), e.toString());
- e.printStackTrace();
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- tv = new TextView(this);
- tv.setText("我是初始值");
- setContentView(tv);
- }
- @Override
- protected void onPause() {
- Log.i(MainActivity.class.getSimpleName(),"onPuase()");
- timer.cancel();
- timer=null;
- super.onPause();
- }
- @Override
- protected void onResume() {
- Log.i(MainActivity.class.getSimpleName(),"onResume()");
- timer=new Timer();
- timer.scheduleAtFixedRate(new refreshTask(), 0, 1000);
- super.onResume();
- }
- }
运行到断点的结果: 
[转][android][利用JNI技术在Android中调用、调试C++代码]的更多相关文章
- 【转】Android 学习笔记——利用JNI技术在Android中调用、调试C++代码
原文网址:http://cherishlc.iteye.com/blog/1756762 在Android中调用C++其实就是在Java中调用C++代码,只是在windows下编译生成DLL,在And ...
- 利用JNI技术在Android中调用C++形式的OpenGL ES 2.0函数
1. 打开Eclipse,File-->New-->Project…-->Android-->AndroidApplication Projec ...
- 利用JNI技术在Android中调用、调试C++代码
参考:http://blog.micro-studios.com/?p=4212 代码:http://pan.baidu.com/s/1sjukSDf
- [置顶] android利用jni调用第三方库——第三篇——编写库android程序整合第三方库libhello.so到自己的库libhelloword.so
0:前言: 在第二篇中,我们主要介绍了丙方android公司利用乙方C++公司给的动态库,直接调用库中的方法,但是这样方式受限于: 乙方C++公司开发的动态库是否符合jni的规范,如果不规范,则不能直 ...
- [置顶] android利用jni调用第三方库——第二篇——编写库android程序直接调用第三方库libhello.so
0:前言 1:本文主要作为丙方android公司的身份来写 2:作者有不对的地方,请指出,谢谢 [第一篇:android利用jni调用第三方库——编写库libhello.so] [第二篇:androi ...
- Android For JNI(二)——C语言中的数据类型,输出,输入函数以及操作内存地址,内存修改器
Android For JNI(二)--C语言中的数据类型,输出,输入函数以及操作内存地址,内存修改器 当我们把Hello World写完之后,我们就可以迈入C的大门了,今天就来讲讲基本的一些数据类型 ...
- python利用or在列表解析中调用多个函数.py
python利用or在列表解析中调用多个函数.py """ python利用or在列表解析中调用多个函数.py 2016年3月15日 05:08:42 codegay & ...
- 如何利用好chrome控制台这个神器好好调试javascript代码
上面的文章已经大致介绍了一下console对象具体有哪些方面以及基本的应用,下面简单介绍一下如何利用好chrome控制台这个神器好好调试javascript代码(这个才是我们真正能用到实处的地方) 1 ...
- 在vim中 安装php的xdebug和 vdebug插件, 在vim中进行调试php代码
在vim中 安装php的xdebug和 vdebug插件, 在vim中进行调试php代码 参考: http://www.cnblogs.com/qiantuwuliang/archive/2011/0 ...
随机推荐
- WebRTC学习
1. WebRTC学习 1.1 WebRTC现状 本人最早接触WebRTC是在2011年底,那时Google已经在Android源码中加入了webrtc源码,放在/external/web ...
- Material Design 之 定义状态栏(Status Bar)的颜色
Hey,好久不见.今天遇到一个问题,想要把Status Bar 和 Tool Bar的颜色弄成一样的,或者是类似的,例如Material Design: 图中Status Bar颜色比Tool Bar ...
- java虚拟机内存溢出和泄漏实例
测试参数设置: 1.循环调用new A()实现堆溢出,java.lang.OutOfMemoryError: Java heap space, 虚拟机参数:-Xms1M -Xmx1M -XX:+Hea ...
- pycharm常用快捷键和自定义快捷键
默认快捷键 编辑类: Ctrl + Space 基本的代码完成(类.方法.属性)Ctrl + Alt + Space 类名完成Ctrl + Shift + Enter 语句完成Ctrl + P 参数 ...
- Nwjs简单配置
1.创建一个工程,配置一个 package.json 文件 { "name": "application-name", "version" ...
- 转:CURL库在程序中的运用浅析
CURL库在程序中的运用浅析-nk_ysg-ChinaUnix博客 http://blog.chinaunix.net/uid-22476414-id-3286638.html 这个目录的文章转载fr ...
- 2.3-2.6 HBase java API
一.get .put.delete.scan 1.代码 package com.beifeng.senior.hadoop.hbase; import org.apache.hadoop.conf.C ...
- python类的特殊成员和方法
变量或方法 描述 调用时机 __init__ 构造方法 通过类创建对象时,即类名() ,自动触发执行 __doc__ 表示类的描述信息 __module__ 表示当前操作的对象在那个模块 __ ...
- 关于window 查看端口命令
今天小R又知道了一个新的小常识,个人觉得还是蛮实用的,可能对于有些新手来讲应该也不知道吧. 如:大家都知道window查看命令是用 “netstat -an” , 如图显示一大推打开的端口. 但 ...
- Java判断一个数是不是快乐数
快乐数的定义: 快乐数(happy number)有以下的特性: 在给定的进位制下,该数字所有数位(digits)的平方和,得到的新数再次求所有数位的平方和,如此重复进行,最终结果必为1. 以十进制为 ...