Chinaunix首页 | 论坛 | 博客
  • 博客访问: 78768
  • 博文数量: 24
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 161
  • 用 户 组: 普通用户
  • 注册时间: 2015-01-04 17:48
文章分类

全部博文(24)

文章存档

2015年(24)

我的朋友

分类: Android平台

2015-09-06 22:25:37

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的路径。
设置方法如下图所示:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

该过程分为以下两步:

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


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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

加入本地代码支持:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

完成情况:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

点击菜单栏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以上:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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推荐两种方法:

http://blog.csdn.net/stalendp/article/details/8712059

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

Cmdineclipse代码

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

--------------------------------------------

${system_path:javah}

${project_loc}

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


选build all

[ Search Path: C:\Program Files\Java\jdk1.6.0_45\jre\lib\resources.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\rt.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\jce.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\modules\jdk.boot.jar;C:\Program Files\Java\jdk1.6.0_45\jre\classes\C:\Users\1\workspace\TestNDK2/bin/classes ]

[Creating file C:\Users\1\workspace\TestNDK2/jni\com_lc_testndk2_NativeClass.h]

[源文件的搜索路径: C:\Users\1\workspace\TestNDK2\bin\classes]

[类文件的搜索路径: C:\Program Files\Java\jdk1.6.0_45\jre\lib\resources.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\rt.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\sunrsasign.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\jsse.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\jce.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\charsets.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\modules\jdk.boot.jar,C:\Program Files\Java\jdk1.6.0_45\jre\classes,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\dnsns.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\localedata.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\sunjce_provider.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\sunmscapi.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\sunpkcs11.jar,C:\Users\1\workspace\TestNDK2\bin\classes]

[正在装入 C:\Users\1\workspace\TestNDK2\bin\classes\com\lc\testndk2\NativeClass.class]

[正在装入 java\lang\Object.class(java\lang:Object.class)]

[正在装入 java\lang\Throwable.class(java\lang:Throwable.class)]

[正在装入 java\lang\Class.class(java\lang:Class.class)]

[在718 毫秒内完成]

--------------------------------------------

配置好之后:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

  1. package com.lc.testndk2;   
  2.   
  3. import java.util.Arrays;   
  4.   
  5. import android.app.Activity;   
  6. import android.os.Bundle;   
  7. import android.widget.TextView;   
  8.   
  9. public class MainActivity extends Activity {   
  10.   
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {   
  13.         super.onCreate(savedInstanceState);   
  14.         TextView  tv new TextView(this);        
  15.         int[] array new int[] 12 3};       
  16.         String str "数组,调用C++前" Arrays.toString(array);   
  17.         boolean isCopyOfArrayInCpp NativeClass.jniArrayAdd(array,             1);   
  18.         str += "\n在C++中为副本?  " isCopyOfArrayInCpp;   
  19.         str += "\n数组,调用C++后:" Arrays.toString(array);   
  20.         tv.setText(str);   
  21.         setContentView(tv);   
  22.     }   
  23.  

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

  1. #include <jni.h>   
  2. #include "com_lc_testndk2_NativeClass.h"   
  3. #ifdef __cplusplus  //最好有这个,否则被编译器改了函数名字找不到不要怪我   
  4. extern "C" {   
  5. #endif   
  6. JNIEXPORT jboolean JNICALL Java_com_lc_testndk2_NativeClass_jniArrayAdd(   
  7.         JNIEnv env, jclass, jintArray array, jint b) {   
  8.   
  9.     jsize size env->GetArrayLength(array);   
  10. //  jintArray sum=env->NewIntArray(2);   
  11.   
  12.     jboolean isCopy;   
  13.     jint* pArray (jint*) env->GetPrimitiveArrayCritical(array, &isCopy);   
  14.     for (int 0; size; i++)   
  15.         pArray[i] += b;   
  16.     env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);   
  17.     //env->ReleasePrimitiveArrayCritical(sum,pSum,JNI_COMMIT);   
  18.   
  19.     return isCopy;   
  20. }   
  21.   
  22. JNIEXPORT jintArray JNICALL Java_com_lc_testndk2_NativeClass_jnitArrayMul(   
  23.         JNIEnv env, jclass, jintArray array, jint b) {   
  24.   
  25.     jsize size env->GetArrayLength(array);   
  26.     jintArray product env->NewIntArray(size);   
  27.     jint* pArray (jint*) env->GetPrimitiveArrayCritical(array, 0);   
  28.     jint* pProduct=(jint*)env->GetPrimitiveArrayCritical(product,0);   
  29. //  jintArray product env->NewIntArray(size); //不能在这里创建!!因为上面的方法会使java进入critical region, 在这里创建的话虚拟机直接崩溃   
  30.     for (int 0; size; i++)   
  31.         pProduct[i] =pArray[i]* b;   
  32.     env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);   
  33.     env->ReleasePrimitiveArrayCritical(product,pProduct,JNI_COMMIT);   
  34.     return product;   
  35. }   
  36.   
  37. #ifdef __cplusplus   
  38. }   
  39. #endif  

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

 

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

注意:

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



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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

---------------------------------------------------------------

http://blog.csdn.net/smfwuxiao/article/details/8523087

NDK Build 用法(NDK Build)

1、ndk-build的用法

Android NDKr4引入了一个新的、小巧的shell脚本ndk-build,来简化源码编译。

该文件位于NDK根目录,进入你的工程根目录或子目录之后,在命令行下调用即可。例如:

    cd $PROJECT

    $NDK/ndk-build

NDK指向你的NDK的安装目录,PROJECT指向你的Android工程目录。建议将ndk-build所在目录加入PATH环境变量或设置alias。

2、ndk-build的选项

所有给ndk-build的选项都会直接传给GNU Make,由make运行NDK的编译脚本。几个常见调用方式如下:

ndk-build                   编译

ndk-build clean          清掉二进制文件

ndk-build NDK_DEBUG=1     编译为可调试版的二进制文件

ndk-build NDK_DEBUG=0     编译为release版

ndk-build V=1                       执行ndk-build且打印出它所执行的详细编译命令。

ndk-build -B                          强制重新编译

ndk-build -B V=1                   -B 和 V=1 的组合

ndk-build NDK_LOG=1          打印出内部的NDK日志信息(用于调试NDK自己)

ndk-build NDK_APP_APPLICATION_MK=<文件路径>      用这里指定的路径寻找Application.mk文件

ndk-build -C <project路径>  先cd进入<project路径>,然后执行ndk-build。

3、Debug版本 Vs Release版本

在NDKr5中,ndk-build做了一些改进,使得在release和debug之间切换更容易了。即通过 NDK_DEBUG 变量:

ndk-build NDK_DEBUG=1    编译出的共享库带调试信息,是debug版本

ndk-build NDK_DEBUG=0    编译出的共享库不带调试信息,是release版本

如果没有指定NDK_DEBUG,则会从 AndroidManifest.xml中获取(查看 <application> 元素是否有 android:debuggable="true")。

注意:如果你的SDK是SDKr8或更高,完全不需要修改AndroidManifest.xml。因为如果你想编译出debug版安装包(在命令行下执行 ant debug或用ADT),SDK的工具会自动选择用NDK_DEBUG=1编译出的共享库。NDK编译出的debug版和release版的object文件存放在不同的目录(例如 obj/local/<abi>/objs 和 obj/local/<abi>/objs-debug),避免了在release和debug之间切换而重新编译所有文件。

4、ndk-build的使用条件

总体来说,必须安装GNU Make 3.81 以上版本的make才能正常使用ndk-build和NDK。


5、ndk-build的实质

ndk-build 其实就是对GNU Make的封装,它的目的是调用正确的NDK编译脚本,它等价于 make -f $NDK/build/core/build-local.mk [参数]

---------------------------------------------------------------

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

  1. package com.lc.testndk2;   
  2.   
  3. import java.util.Arrays;   
  4. import java.util.Timer;   
  5. import java.util.TimerTask;   
  6.   
  7. import android.annotation.SuppressLint;   
  8. import android.app.Activity;   
  9. import android.os.Bundle;   
  10. import android.os.Handler;   
  11. import android.os.Message;   
  12. import android.util.Log;   
  13. import android.widget.TextView;   
  14.   
  15.   
  16. public class MainActivity extends Activity {   
  17.   
  18.     TextView tv null;   
  19.     int count 0;   
  20.     Timer timer;   
  21.   
  22.      @SuppressLint("HandlerLeak")   
  23.     class MyHandler extends Handler{   
  24.         @Override  
  25.         public void handleMessage(Message msg) {   
  26.             if (tv != null{   
  27.                 tv.setText(msg.getData().getString("text"));   
  28.             }   
  29.             super.handleMessage(msg);   
  30.         }   
  31.     };   
  32.        
  33.     Handler handle=  new MyHandler();   
  34.        
  35.        
  36.     class refreshTask extends TimerTask {   
  37.   
  38.         @Override  
  39.         public void run() {   
  40.             try {   
  41.                 count++;   
  42.                 Log.i("MainActivity""before call native code,count="  
  43.                         count);   
  44.                 int[] array new int[] count, -count, 2*count };   
  45.   
  46.                 String str "第" count "次了\n";   
  47.   
  48.                 str += "数组,调用C++前" Arrays.toString(array);   
  49.   
  50.                 boolean isCopyOfArrayInCpp NativeClass.jniArrayAdd(array,1);   
  51.                 str += "\n在C++中为副本?  " isCopyOfArrayInCpp;   
  52.                 str += "\n数组,调用C++后:" Arrays.toString(array)+"\n\n";   
  53.   
  54.                 str+="测试在C++中创建数组:\n";   
  55.                 str +=  Arrays.toString(array)+"* =";   
  56.                 str+=Arrays.toString(NativeClass.jnitArrayMul(array, 2))+"\n\n";   
  57.                    
  58.                 Message msg=new Message();   
  59.                 Bundle b=new Bundle();   
  60.                 b.putString("text"str);   
  61.                 msg.setData(b);                
  62.                 handle.sendMessage(msg);   
  63.                    
  64.                 Log.i("MainActivity""after call native code");   
  65.             catch (Exception e) {   
  66.                 Log.i(MainActivity.class.getSimpleName(), e.toString());   
  67.                 e.printStackTrace();   
  68.             }   
  69.   
  70.         }   
  71.     };   
  72.        
  73.        
  74.     @Override  
  75.     protected void onCreate(Bundle savedInstanceState) {   
  76.         super.onCreate(savedInstanceState);   
  77.         tv new TextView(this);   
  78.         tv.setText("我是初始值");   
  79.         setContentView(tv);   
  80.     }   
  81.   
  82.     @Override  
  83.     protected void onPause() {   
  84.         Log.i(MainActivity.class.getSimpleName(),"onPuase()");   
  85.         timer.cancel();   
  86.         timer=null;   
  87.         super.onPause();   
  88.     }   
  89.   
  90.     @Override  
  91.     protected void onResume() {   
  92.         Log.i(MainActivity.class.getSimpleName(),"onResume()");   
  93.         timer=new Timer();   
  94.         timer.scheduleAtFixedRate(new refreshTask(), 01000);   
  95.         super.onResume();   
  96.     }   
  97.   
  98.  

运行到断点的结果:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

======================================================================================

注释部分:

style="word-wrap:normal;word-break:normal;line-height:25.1875px;font-family:Helvetica, Tahoma, Arial, sans-serif;background-color:#FFFFFF;"> 

随着支持Intel Atom处理器的android手机在CES上的面世,android程序开发也将有一些新的变化,开发人员需要考虑相应的移植问题,对于普通的java程序,开发人员不需要关心处理器的细节,所以,主要是那些使用NDK进行native开发的程序的移植。
随着Intel处理器的加入,相信越来越多基于x86的c/c++库将能更容易的被移植到android平台。

对于为x86进行android NDK程序开发,我能想到的问题主要有:
1. 如何让android NDK程序支持x86?
这点在NDK的手册中有说明,只需要在Application.mk中编辑APP_ABI 行加入 x86即可,如:
APP_ABI := armeabi armeabi-v7a x86
这样就能生成支持android x86的.so文件了。
说明:上面三种abi可以任意指定,也可以同时指定多个,默认为armeabi。

2. 如何测试支持x86的android程序?(以windows平台的SDK为例)
要测试支持Intel处理器的android x86程序,就目前而言,只能使用模拟器了。在SDK Tools的Reverion 12版本更新的General Notes中可以看到:
The AVD manager and emulator can now use system images compiled for ARM v7 and x86 CPUs.
从这里看到新版本的SDK工具应该是支持armv7和x86的模拟器的,然而打开SDK Manager更新列表,发现最新的android提供了ARM EABI v7a System Image,这样就能下载支持arm v7a指令的模拟器进行模拟了。根据http://software.intel.com/en-us/articles/installing-the-intel-atom-tm-android-x86-emulator-image-add-on-from-the-android-sdk-manager/的介绍,SDK Manager已经有x86的images下载,但是我的SDK里面居然没有找到这一项!相信在以后的更新中这个问题会得到修正吧。
尽管这里没有提供下载,但是AOSP中的android源码提供了编译android x86的选择项,也就是我们可以自己编译android x86的系统文件。
关于如何编译android源码,可以参考:(说明:其内容没有完全更新,编译x86和arm的区别在于其中“choose target”的时候选择full_eng_x86即可。)
在编译源码后,我们可以得到下列文件:ramdisk.img,system.img,userdata.img(在out/target/product/generic_x86中),将上面的三个文件和源码prebuilt/android-x86/kernel中的kernel-qemu文件保存起来,如保存在一个新建的x86文件夹中。
下面就是如何在SDK中添加自己编译的x86模拟器,将上面的x86文件夹复制到:platforms\android-**\images中,同时将原有的文件移动到一个新的arm文件夹,这样,在使用AVD Manager.exe创建模拟器的时候,就会出现target的选择。

说明:对于android-14和android-15,其中没有images文件夹,因为SDK将其移动到SDK根目录的system-images文件夹中了,可以自己新建此文件夹并在其中放置相关模拟器需要的文件即可。
说明:http://software.intel.com/en-us/articles/android-237-gingerbread-x86-emulator-image-add-on/提供了其它一些方法来使用android x86模拟器,可以作为参考。
总之,有多种方法可以对x86的android NDK程序进行模拟器的测试。

3. x86和arm利用NDK移植c/c++程序要考虑的问题
由于指令集等的不同,在用NDK移植某些程序的时候,可能需要考虑程序移植结果的正确性,就arm和x86对比而言,主要需要考虑的是:内存中字节对齐的差异、浮点数操作的差异、向量化指令的差异(NEON和SSE等)等。当然,并不是所有的程序都会在这些方面影响程序的结果。

在Intel官网上,有一些和android x86开发相关的文章:,可以作为参考。
那么,除了上面提到的一些问题,大家觉得x86和arm的NDK程序开发需要注意的问题还有哪些呢?欢迎大家一起讨论。


第6步我出现的问题是:

[2013-06-25 15:17:39 - TestNDK2] ------------------------------

[2013-06-25 15:17:39 - TestNDK2] Android Launch!

[2013-06-25 15:17:39 - TestNDK2] adb is running normally.

[2013-06-25 15:17:39 - TestNDK2] Performing com.lc.testndk2.MainActivity activity launch

[2013-06-25 15:17:39 - TestNDK2] Automatic Target Mode: launching new emulator with compatible AVD 'avd422'

[2013-06-25 15:17:39 - TestNDK2] Launching a new emulator with Virtual Device 'avd422'

[2013-06-25 15:17:39 - Emulator] emulator: warning: opening audio input failed

[2013-06-25 15:17:39 - Emulator] 

[2013-06-25 15:17:39 - TestNDK2] New emulator found: emulator-5554

[2013-06-25 15:17:39 - TestNDK2] Waiting for HOME ('android.process.acore') to be launched...

[2013-06-25 15:18:06 - TestNDK2] HOME is up on device 'emulator-5554'

[2013-06-25 15:18:06 - TestNDK2] Uploading TestNDK2.apk onto device 'emulator-5554'

[2013-06-25 15:18:06 - TestNDK2] Installing TestNDK2.apk...

[2013-06-25 15:18:34 - TestNDK2] Success!

[2013-06-25 15:18:34 - TestNDK2] Starting activity com.lc.testndk2.MainActivity on device emulator-5554

[2013-06-25 15:18:36 - TestNDK2] ActivityManager: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.lc.testndk2/.MainActivity }

[2013-06-25 15:18:37 - TestNDK2] Attempting to connect debugger to 'com.lc.testndk2' on port 8631

[2013-06-25 15:25:22 - TestNDK2] gdbserver output:

[2013-06-25 15:25:22 - TestNDK2] run-as: exec failed for lib/gdbserver Error:No such file or directory

[2013-06-25 15:25:22 - TestNDK2] Verify if the application was built with NDK_DEBUG=1

gdb+gdbserver

----------------------------------------------

http://www.ibm.com/developerworks/cn/education/opensource/os-eclipse-tools/section2.html

设置新的 Ant 配置

第一个示例是 Ant 构建文件,该文件只创建一个包含一些经过编译的 Java 代码的 Java Archive (JAR),构建一个包含 Java 源代码的 JAR,并把两个 JAR 移到指定位置。

:Ant 是一个构建工具 — 有点类似 Java 版本的 make(极为简单的工具)— 该工具将使用 XML 格式的构建文件(默认情况下为 build.xml)。如果以前没使用过 Ant,您仍然能够继续学习本教程,但是可能需要以后花些时间了解 Ant 的更多信息(有关更多信息,请参阅侧栏 “了解 Ant 的更多信息”)。

为什么使用 Ant?

虽然可以使用批处理 (.bat) 或 shell (.sh) 脚本创建 JAR 并把它们移到其他位置,但是有很多原因使我们必须使用 Ant 完成前述操作。一个原因是因为 Ant 构建是可移植的 — 如果计划要在其他操作系统中使用 Eclipse 中的配置,那么可移植性是必须要考虑的事项。由于 Eclipse 可以在 Windows、Mac 和 Linux 上运行,因此移植的可能性一定高于仅在一个操作系统中运行的 IDE。


使用 Ant 执行构建的第二个原因是 Ant 是一个常见工具 — 至少在 Java 社区中是这样的。Eclipse Europa 有一个适用于 Ant 构建文件的特殊编辑器,该编辑器内置了语法突出显示功能和模板,这样如果您刚开始接触 build.xml 文件,就可以获得来自 IDE 的一些额外帮助。并且由于 Ant 被广泛使用,因此,团队中的其他人员很可能理解 Ant build.xml 文件,而不理解 .bat 或 .sh 文件,当文件十分复杂时尤其如此。

回页首

创建 JAR

清单 1 展示了样例 Ant 构建文件。您将在本教程中一直使用此构建文件来研究 Eclipse Europa 的功能。


清单 1. 样例 Ant build.xml 文件


<?xml version="1.0" encoding="UTF-8"?>

<project name="myAntExample" default="publish">

    <description>

        Short description here.

    </description>


    <property name="version" value="1.0.0" />

    <property name="dist.dir" value="dist" />

    <property name="publish.dir" value="published" />

    <property name="classes.jar" value="${dist.dir}/classes.jar" />

    <property name="sources.jar" value="${dist.dir}/sources.jar" />


    <target name="publish" depends="package" description="Published the files">

        <copy file="${classes.jar}" tofile="${publish.dir}/classes-${version}.jar" />

        <copy file="${sources.jar}" tofile="${publish.dir}/sources-${version.jar}" />

    </target>


    <target name="package" depends="prepare" description="Packages the jars">

        <jar destfile="${classes.jar}">

            <fileset dir="${classes.dir}">

                <include name="***.java" />

            </fileset>

        </jar>

    </target>


    <target name="prepare">

        <mkdir dir="${dist.dir}" />

        <mkdir dir="${publish.dir}" />

    </target>


</project>

要在 Eclipse 中添加新的 Ant 启动配置,请选择 Run > External Tools > Open External Tools Dialog 以访问 External Tools 窗口,如图 1 所示。在左侧窗格中,单击 Ant Build,然后单击 New 以创建新配置。Main 配置选项卡将被突出显示并启用,并且 Eclipse 将为配置添加一个默认名称。


图 1. External Tools 窗口

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

Main 配置选项卡

Main 配置选项卡包含 Ant 启动配置的一些基本信息,包括 Ant 构建文件的名称及需要传递给该文件的所有实参。图 2 显示 Eclipse 中的 Main 选项卡。


图 2. Main 配置选项卡

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

设置构建文件

您可以键入构建文件的名称,但是为了消除错误,我发现单击 Browse Workspace 或 Browse File System(参见图 2)查找并选择文件更容易。清单 1 中所示的构建文件已被放到新项目的根目录下,因此使用 Browse Workspace 查找该文件。将显示工作区中的项目清单,您可以从中选择文件的位置。

浏览并选择构建文件后,请单击 OK。现在在 Buildfile 框中的值类似 ${workspace_loc:/exampleAntBuilder/build.xml}

通过只更新 Buildfile 值,您可以单击 Apply 或 Run,保存并使用配置。如果不需要执行比运行单个目标更复杂的操作,那么至此已完成全部操作。但是,您可以给 Ant 构建进程提供更多信息以进一步自定义启动配置。

回页首

传入自定义实参

在 Main 配置选项卡中,您可以把实参发送到 Ant 配置中,这只是向 Ant 传递值并以不同方式构建项目的一种方法。例如,在清单 1 中,build.xml 文件包括使用 <property> 标记定义的几个属性。但是,您将不会在构建文件中定义这些属性,而是传入这些属性。

传入自定义参数的一个原因是它允许您在不同项目中使用同一个构建文件,即使这些项目使用的目录和文件名不同。与使用同一 Ant 构建脚本的副本并针对各个项目进行相应调整相比,这可能是更好的替代方案。

Ant 将在 -D<property name>=<value> 表单中获取提供给它的经过覆盖的属性,因此如果需要查看详细的输出并覆盖 JAR 的 dist.dir变量(其中保存 JAR)的名称,则需要传入 -v -Ddist.dir=out。这段代码将把目录名称设为 out。单击 Apply,然后如果需要立即运行,请单击 Run

:还可以从工具栏获得 Ant 构建配置。

除了提供静态数据字符串之外,您可以在 Main 选项卡中单击 Variables 以自动插入可以传递给 Ant 的变量。表 1 中涵盖了可用变量,但是要快速测试提供版本,请使用名为 ${string_prompt} 的内置变量,它将提示您输入值。Main 选项卡中的 Arguments 值现在是 -v -Ddist.dir=out -Dversion=${string_prompt}

插入完变量后,请单击 Apply,然后单击 Run 以运行 Ant 启动配置。系统将提示您输入值,如图 3 所示。


图 3. 字符串输入提示

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
表 1. 启动配置变量

变量 说明
build_files 如果自动构建被启用,则此变量包括触发构建的文件的绝对路径。它支持使用参数缩小选择范围,例如,f 表示仅限于文件。
build_project 这是当前正在构建的项目的绝对路径。
build_type 该变量表示正在执行的构建的类型(incremental、full、auto 或 none)。如果尝试在启动配置不是构建器的情况下使用此变量,则值为 none
container_loc 这是当前资源的容器的绝对名称,可以是文件夹,也可以是目录。
container_name 这是当前资源所在容器的名称。
container_path 该变量表示容器到工作区的相对路径。
eclipse_home 这是 Eclipse 的安装位置。
env_var 该变量将输入给定环境变量的值,其中变量的名称可以作为实参提供给变量。
file_prompt 当 Eclipse 启动程序时,它将显示一个文件选择器。在提示时将选择的文件名放入变量值中。
folder_prompt 该变量与文件提示相同,但是只限于文件夹。
java_extensions_regex 这是匹配正则表达式的已注册的类 Java 文件扩展名。
java_type_name 该变量是所选资源中主要类型的完全限定的 Java 类型名。
project_loc 这是项目在文件系统中的绝对路径。
project_name 这是项目的名称。
project_path 这是当前项目到工作区的相对路径。
resource_loc 该变量表示当前资源在文件系统中的绝对路径。
resource_name 这是资源的名称。
resource_path 这是当前资源到工作区的相对路径。
selected_text 这是选中的文本的值(如果有的话)。
string_prompt 如果使用该变量,Eclipse 将提示您输入字符串值。您在提示时输入的任何内容都将被替换到变量中。
system 这是 Eclipse 系统变量值(ARCH、ECLIPSE_HOME、NL、OS、WS)。
system_path 该变量是作为实参提供的工具在文件系统中的绝对路径。
system_property 这是来自 Eclipse 运行时的系统属性值,其中属性名是变量的实参。
target.arch 该变量表示目标处理器架构(例如,适用于 Intel? 处理器的 x86)。
target.nl 这是目标语言环境(例如,en_US 或 fr_FR)。
target.os 这是目标操作系统(例如,macosx)。
target.ws 该变量表示目标窗口系统(例如,Mac 的 Carbon)。
target_home 这是目标主目录,它与 Eclipse 主目录相同。
workspace_loc 这是工作区在文件系统中的绝对路径。


其他有用实参

连同属性的覆盖值一起传入 Ant 配置的其他有用实参是允许调整日志记录的那些实参。例如,-logfile <filename> 将允许您把输出记录到文件中。使用 -q-v 和 -d 参数可以将输出级别分别调整为静默、详细和调试。


=================================================================================

http://blog.sina.com.cn/s/blog_697bbefe0100vqzc.html

eclipse调试android NDK C/C++代码配置指南

在经历了无数纠结后,决定写下这篇文章,让后面的开发人员少走弯路~

博文中的图片来自原文,可能会与我的开发环境有少许不同,但是整体步骤是对的。本人在以下环境中测试通过:
系统:Ubuntu 11.04
NDK版本:r6
eclipse版本:3.7

参考原文:
http://www.eclipse.org/sequoyah/documentation/native_debug.php

首先下面5步设置eclipse
第1步:
在AndroidManifest.xml文件中application项中加入android:debuggable="true",你也可以在eclipse里的可视化中设置,如下图。

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
第2步:设置你的C/C++调试选项
在Main的选项卡中,在C/C++ Application里面选择app_process作为调试对象(app_process文件会在你运行ndk-gdb时候生成)!而不是你编译出来的*.so文件!这个文件会在 你的项目路径/obj/local/armeabi/app_process  
还有记得选择Disable auto build 配置如下图

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
接下来点击图片中红色方框特别标志的地方,设置启动调试的进一步参数

第3步:选择调试启动方式
选择Standard Create Process 然后确定,如图

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
第4步:
在Debugger的选项卡中设置debugger模式为gdbserver,而Stop on start at就填你希望当调试开始时候的断点函数名(注意:这个函数名是C/C++的函数名称,即使你没有打上断点当运行到这个函数时会自动暂停)
在Debugger Option栏目框里的Main选项卡的GDB Debugger选择NDK的gdb,这个文件在  NDK目录/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin 里面,可能包含window平台等,请根据自己的开发机器和目标平台做正确的选择!!!接下来,需要修改一些东西,当你编译好你的工程后会生成一个gdb.setup的gdb配置文件,例如  你的工程目录/obj/local/armeabi/gdb.setup,先将该文件做个拷贝,命名为gdb2.setup,然后用打开这个文件将最后一行 target remote :5039删掉,因为接下来我们会在eclipse里面设置,因此这一行是多余的,如果不删掉的话会出错!然后就将改好的文件设置为GDB Command file,整体如下图

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
第5步:
在Debugger Option栏目框里的Connection选项卡设置连接类型Type为TCP,端口Port number为5039,如图

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
最后修改ndk-gdb这个文件,该文件位于你的NDK目录下,先将该文件做个拷贝,命名为ndk-gdb2,然后打开,将最后一行  $GDBCLIENT -x `native_path $GDBSETUP` 删掉,这一行的功能是启动控制台的gdb我们已经在eclipse里启动了,所以不需要这行!

具体调试步骤:
1.用eclipse启动android的java调试在你自己的Activity类中的onCreate方法中加一个断点,避免程序一闪而过你还没来得及启动C/C++部分的调试。
2.在你的工程目录下运行刚才修改好的ndk-gdb2,如果你需要一些信息可以在后面加上--verbose参数。
3.启动eclipse的C/C++调试

启动成功后如下图(这张图是我自己的,呵呵...)

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
一些比较弱智的个人心得:
android的模拟器是可以正常使用超级用户的权限的,当你是用真机设备调试的时候请确保你的真机设备已经root否则会出现一些权限的错误,甚至导致莫名奇妙的问题...我就被这些问题纠结了几天....

jni4-Android 学习笔记--利用JNI技术在Android中调用、调试C++代码

  (2013-06-19 22:27:37)
标签: 

it

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的路径。
设置方法如下图所示:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

该过程分为以下两步:

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


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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

加入本地代码支持:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

完成情况:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

点击菜单栏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以上:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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推荐两种方法:

http://blog.csdn.net/stalendp/article/details/8712059

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

Cmdineclipse代码

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

--------------------------------------------

${system_path:javah}

${project_loc}

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


选build all

[ Search Path: C:\Program Files\Java\jdk1.6.0_45\jre\lib\resources.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\rt.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\jce.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.6.0_45\jre\lib\modules\jdk.boot.jar;C:\Program Files\Java\jdk1.6.0_45\jre\classes\C:\Users\1\workspace\TestNDK2/bin/classes ]

[Creating file C:\Users\1\workspace\TestNDK2/jni\com_lc_testndk2_NativeClass.h]

[源文件的搜索路径: C:\Users\1\workspace\TestNDK2\bin\classes]

[类文件的搜索路径: C:\Program Files\Java\jdk1.6.0_45\jre\lib\resources.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\rt.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\sunrsasign.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\jsse.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\jce.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\charsets.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\modules\jdk.boot.jar,C:\Program Files\Java\jdk1.6.0_45\jre\classes,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\dnsns.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\localedata.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\sunjce_provider.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\sunmscapi.jar,C:\Program Files\Java\jdk1.6.0_45\jre\lib\ext\sunpkcs11.jar,C:\Users\1\workspace\TestNDK2\bin\classes]

[正在装入 C:\Users\1\workspace\TestNDK2\bin\classes\com\lc\testndk2\NativeClass.class]

[正在装入 java\lang\Object.class(java\lang:Object.class)]

[正在装入 java\lang\Throwable.class(java\lang:Throwable.class)]

[正在装入 java\lang\Class.class(java\lang:Class.class)]

[在718 毫秒内完成]

--------------------------------------------

配置好之后:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

  1. package com.lc.testndk2;   
  2.   
  3. import java.util.Arrays;   
  4.   
  5. import android.app.Activity;   
  6. import android.os.Bundle;   
  7. import android.widget.TextView;   
  8.   
  9. public class MainActivity extends Activity {   
  10.   
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {   
  13.         super.onCreate(savedInstanceState);   
  14.         TextView  tv new TextView(this);        
  15.         int[] array new int[] 12 3};       
  16.         String str "数组,调用C++前" Arrays.toString(array);   
  17.         boolean isCopyOfArrayInCpp NativeClass.jniArrayAdd(array,             1);   
  18.         str += "\n在C++中为副本?  " isCopyOfArrayInCpp;   
  19.         str += "\n数组,调用C++后:" Arrays.toString(array);   
  20.         tv.setText(str);   
  21.         setContentView(tv);   
  22.     }   
  23.  

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

  1. #include <jni.h>   
  2. #include "com_lc_testndk2_NativeClass.h"   
  3. #ifdef __cplusplus  //最好有这个,否则被编译器改了函数名字找不到不要怪我   
  4. extern "C" {   
  5. #endif   
  6. JNIEXPORT jboolean JNICALL Java_com_lc_testndk2_NativeClass_jniArrayAdd(   
  7.         JNIEnv env, jclass, jintArray array, jint b) {   
  8.   
  9.     jsize size env->GetArrayLength(array);   
  10. //  jintArray sum=env->NewIntArray(2);   
  11.   
  12.     jboolean isCopy;   
  13.     jint* pArray (jint*) env->GetPrimitiveArrayCritical(array, &isCopy);   
  14.     for (int 0; size; i++)   
  15.         pArray[i] += b;   
  16.     env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);   
  17.     //env->ReleasePrimitiveArrayCritical(sum,pSum,JNI_COMMIT);   
  18.   
  19.     return isCopy;   
  20. }   
  21.   
  22. JNIEXPORT jintArray JNICALL Java_com_lc_testndk2_NativeClass_jnitArrayMul(   
  23.         JNIEnv env, jclass, jintArray array, jint b) {   
  24.   
  25.     jsize size env->GetArrayLength(array);   
  26.     jintArray product env->NewIntArray(size);   
  27.     jint* pArray (jint*) env->GetPrimitiveArrayCritical(array, 0);   
  28.     jint* pProduct=(jint*)env->GetPrimitiveArrayCritical(product,0);   
  29. //  jintArray product env->NewIntArray(size); //不能在这里创建!!因为上面的方法会使java进入critical region, 在这里创建的话虚拟机直接崩溃   
  30.     for (int 0; size; i++)   
  31.         pProduct[i] =pArray[i]* b;   
  32.     env->ReleasePrimitiveArrayCritical(array, pArray, JNI_COMMIT);   
  33.     env->ReleasePrimitiveArrayCritical(product,pProduct,JNI_COMMIT);   
  34.     return product;   
  35. }   
  36.   
  37. #ifdef __cplusplus   
  38. }   
  39. #endif  

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

 

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

注意:

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



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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

---------------------------------------------------------------

http://blog.csdn.net/smfwuxiao/article/details/8523087

NDK Build 用法(NDK Build)

1、ndk-build的用法

Android NDKr4引入了一个新的、小巧的shell脚本ndk-build,来简化源码编译。

该文件位于NDK根目录,进入你的工程根目录或子目录之后,在命令行下调用即可。例如:

    cd $PROJECT

    $NDK/ndk-build

NDK指向你的NDK的安装目录,PROJECT指向你的Android工程目录。建议将ndk-build所在目录加入PATH环境变量或设置alias。

2、ndk-build的选项

所有给ndk-build的选项都会直接传给GNU Make,由make运行NDK的编译脚本。几个常见调用方式如下:

ndk-build                   编译

ndk-build clean          清掉二进制文件

ndk-build NDK_DEBUG=1     编译为可调试版的二进制文件

ndk-build NDK_DEBUG=0     编译为release版

ndk-build V=1                       执行ndk-build且打印出它所执行的详细编译命令。

ndk-build -B                          强制重新编译

ndk-build -B V=1                   -B 和 V=1 的组合

ndk-build NDK_LOG=1          打印出内部的NDK日志信息(用于调试NDK自己)

ndk-build NDK_APP_APPLICATION_MK=<文件路径>      用这里指定的路径寻找Application.mk文件

ndk-build -C <project路径>  先cd进入<project路径>,然后执行ndk-build。

3、Debug版本 Vs Release版本

在NDKr5中,ndk-build做了一些改进,使得在release和debug之间切换更容易了。即通过 NDK_DEBUG 变量:

ndk-build NDK_DEBUG=1    编译出的共享库带调试信息,是debug版本

ndk-build NDK_DEBUG=0    编译出的共享库不带调试信息,是release版本

如果没有指定NDK_DEBUG,则会从 AndroidManifest.xml中获取(查看 <application> 元素是否有 android:debuggable="true")。

注意:如果你的SDK是SDKr8或更高,完全不需要修改AndroidManifest.xml。因为如果你想编译出debug版安装包(在命令行下执行 ant debug或用ADT),SDK的工具会自动选择用NDK_DEBUG=1编译出的共享库。NDK编译出的debug版和release版的object文件存放在不同的目录(例如 obj/local/<abi>/objs 和 obj/local/<abi>/objs-debug),避免了在release和debug之间切换而重新编译所有文件。

4、ndk-build的使用条件

总体来说,必须安装GNU Make 3.81 以上版本的make才能正常使用ndk-build和NDK。


5、ndk-build的实质

ndk-build 其实就是对GNU Make的封装,它的目的是调用正确的NDK编译脚本,它等价于 make -f $NDK/build/core/build-local.mk [参数]

---------------------------------------------------------------

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

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

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

  1. package com.lc.testndk2;   
  2.   
  3. import java.util.Arrays;   
  4. import java.util.Timer;   
  5. import java.util.TimerTask;   
  6.   
  7. import android.annotation.SuppressLint;   
  8. import android.app.Activity;   
  9. import android.os.Bundle;   
  10. import android.os.Handler;   
  11. import android.os.Message;   
  12. import android.util.Log;   
  13. import android.widget.TextView;   
  14.   
  15.   
  16. public class MainActivity extends Activity {   
  17.   
  18.     TextView tv null;   
  19.     int count 0;   
  20.     Timer timer;   
  21.   
  22.      @SuppressLint("HandlerLeak")   
  23.     class MyHandler extends Handler{   
  24.         @Override  
  25.         public void handleMessage(Message msg) {   
  26.             if (tv != null{   
  27.                 tv.setText(msg.getData().getString("text"));   
  28.             }   
  29.             super.handleMessage(msg);   
  30.         }   
  31.     };   
  32.        
  33.     Handler handle=  new MyHandler();   
  34.        
  35.        
  36.     class refreshTask extends TimerTask {   
  37.   
  38.         @Override  
  39.         public void run() {   
  40.             try {   
  41.                 count++;   
  42.                 Log.i("MainActivity""before call native code,count="  
  43.                         count);   
  44.                 int[] array new int[] count, -count, 2*count };   
  45.   
  46.                 String str "第" count "次了\n";   
  47.   
  48.                 str += "数组,调用C++前" Arrays.toString(array);   
  49.   
  50.                 boolean isCopyOfArrayInCpp NativeClass.jniArrayAdd(array,1);   
  51.                 str += "\n在C++中为副本?  " isCopyOfArrayInCpp;   
  52.                 str += "\n数组,调用C++后:" Arrays.toString(array)+"\n\n";   
  53.   
  54.                 str+="测试在C++中创建数组:\n";   
  55.                 str +=  Arrays.toString(array)+"* =";   
  56.                 str+=Arrays.toString(NativeClass.jnitArrayMul(array, 2))+"\n\n";   
  57.                    
  58.                 Message msg=new Message();   
  59.                 Bundle b=new Bundle();   
  60.                 b.putString("text"str);   
  61.                 msg.setData(b);                
  62.                 handle.sendMessage(msg);   
  63.                    
  64.                 Log.i("MainActivity""after call native code");   
  65.             catch (Exception e) {   
  66.                 Log.i(MainActivity.class.getSimpleName(), e.toString());   
  67.                 e.printStackTrace();   
  68.             }   
  69.   
  70.         }   
  71.     };   
  72.        
  73.        
  74.     @Override  
  75.     protected void onCreate(Bundle savedInstanceState) {   
  76.         super.onCreate(savedInstanceState);   
  77.         tv new TextView(this);   
  78.         tv.setText("我是初始值");   
  79.         setContentView(tv);   
  80.     }   
  81.   
  82.     @Override  
  83.     protected void onPause() {   
  84.         Log.i(MainActivity.class.getSimpleName(),"onPuase()");   
  85.         timer.cancel();   
  86.         timer=null;   
  87.         super.onPause();   
  88.     }   
  89.   
  90.     @Override  
  91.     protected void onResume() {   
  92.         Log.i(MainActivity.class.getSimpleName(),"onResume()");   
  93.         timer=new Timer();   
  94.         timer.scheduleAtFixedRate(new refreshTask(), 01000);   
  95.         super.onResume();   
  96.     }   
  97.   
  98.  

运行到断点的结果:

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

======================================================================================

注释部分:

style="word-wrap:normal;word-break:normal;line-height:25.1875px;font-family:Helvetica, Tahoma, Arial, sans-serif;background-color:#FFFFFF;"> 

随着支持Intel Atom处理器的android手机在CES上的面世,android程序开发也将有一些新的变化,开发人员需要考虑相应的移植问题,对于普通的java程序,开发人员不需要关心处理器的细节,所以,主要是那些使用NDK进行native开发的程序的移植。
随着Intel处理器的加入,相信越来越多基于x86的c/c++库将能更容易的被移植到android平台。

对于为x86进行android NDK程序开发,我能想到的问题主要有:
1. 如何让android NDK程序支持x86?
这点在NDK的手册中有说明,只需要在Application.mk中编辑APP_ABI 行加入 x86即可,如:
APP_ABI := armeabi armeabi-v7a x86
这样就能生成支持android x86的.so文件了。
说明:上面三种abi可以任意指定,也可以同时指定多个,默认为armeabi。

2. 如何测试支持x86的android程序?(以windows平台的SDK为例)
要测试支持Intel处理器的android x86程序,就目前而言,只能使用模拟器了。在SDK Tools的Reverion 12版本更新的General Notes中可以看到:
The AVD manager and emulator can now use system images compiled for ARM v7 and x86 CPUs.
从这里看到新版本的SDK工具应该是支持armv7和x86的模拟器的,然而打开SDK Manager更新列表,发现最新的android提供了ARM EABI v7a System Image,这样就能下载支持arm v7a指令的模拟器进行模拟了。根据http://software.intel.com/en-us/articles/installing-the-intel-atom-tm-android-x86-emulator-image-add-on-from-the-android-sdk-manager/的介绍,SDK Manager已经有x86的images下载,但是我的SDK里面居然没有找到这一项!相信在以后的更新中这个问题会得到修正吧。
尽管这里没有提供下载,但是AOSP中的android源码提供了编译android x86的选择项,也就是我们可以自己编译android x86的系统文件。
关于如何编译android源码,可以参考:(说明:其内容没有完全更新,编译x86和arm的区别在于其中“choose target”的时候选择full_eng_x86即可。)
在编译源码后,我们可以得到下列文件:ramdisk.img,system.img,userdata.img(在out/target/product/generic_x86中),将上面的三个文件和源码prebuilt/android-x86/kernel中的kernel-qemu文件保存起来,如保存在一个新建的x86文件夹中。
下面就是如何在SDK中添加自己编译的x86模拟器,将上面的x86文件夹复制到:platforms\android-**\images中,同时将原有的文件移动到一个新的arm文件夹,这样,在使用AVD Manager.exe创建模拟器的时候,就会出现target的选择。

说明:对于android-14和android-15,其中没有images文件夹,因为SDK将其移动到SDK根目录的system-images文件夹中了,可以自己新建此文件夹并在其中放置相关模拟器需要的文件即可。
说明:http://software.intel.com/en-us/articles/android-237-gingerbread-x86-emulator-image-add-on/提供了其它一些方法来使用android x86模拟器,可以作为参考。
总之,有多种方法可以对x86的android NDK程序进行模拟器的测试。

3. x86和arm利用NDK移植c/c++程序要考虑的问题
由于指令集等的不同,在用NDK移植某些程序的时候,可能需要考虑程序移植结果的正确性,就arm和x86对比而言,主要需要考虑的是:内存中字节对齐的差异、浮点数操作的差异、向量化指令的差异(NEON和SSE等)等。当然,并不是所有的程序都会在这些方面影响程序的结果。

在Intel官网上,有一些和android x86开发相关的文章:,可以作为参考。
那么,除了上面提到的一些问题,大家觉得x86和arm的NDK程序开发需要注意的问题还有哪些呢?欢迎大家一起讨论。


第6步我出现的问题是:

[2013-06-25 15:17:39 - TestNDK2] ------------------------------

[2013-06-25 15:17:39 - TestNDK2] Android Launch!

[2013-06-25 15:17:39 - TestNDK2] adb is running normally.

[2013-06-25 15:17:39 - TestNDK2] Performing com.lc.testndk2.MainActivity activity launch

[2013-06-25 15:17:39 - TestNDK2] Automatic Target Mode: launching new emulator with compatible AVD 'avd422'

[2013-06-25 15:17:39 - TestNDK2] Launching a new emulator with Virtual Device 'avd422'

[2013-06-25 15:17:39 - Emulator] emulator: warning: opening audio input failed

[2013-06-25 15:17:39 - Emulator] 

[2013-06-25 15:17:39 - TestNDK2] New emulator found: emulator-5554

[2013-06-25 15:17:39 - TestNDK2] Waiting for HOME ('android.process.acore') to be launched...

[2013-06-25 15:18:06 - TestNDK2] HOME is up on device 'emulator-5554'

[2013-06-25 15:18:06 - TestNDK2] Uploading TestNDK2.apk onto device 'emulator-5554'

[2013-06-25 15:18:06 - TestNDK2] Installing TestNDK2.apk...

[2013-06-25 15:18:34 - TestNDK2] Success!

[2013-06-25 15:18:34 - TestNDK2] Starting activity com.lc.testndk2.MainActivity on device emulator-5554

[2013-06-25 15:18:36 - TestNDK2] ActivityManager: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.lc.testndk2/.MainActivity }

[2013-06-25 15:18:37 - TestNDK2] Attempting to connect debugger to 'com.lc.testndk2' on port 8631

[2013-06-25 15:25:22 - TestNDK2] gdbserver output:

[2013-06-25 15:25:22 - TestNDK2] run-as: exec failed for lib/gdbserver Error:No such file or directory

[2013-06-25 15:25:22 - TestNDK2] Verify if the application was built with NDK_DEBUG=1

gdb+gdbserver

----------------------------------------------

http://www.ibm.com/developerworks/cn/education/opensource/os-eclipse-tools/section2.html

设置新的 Ant 配置

第一个示例是 Ant 构建文件,该文件只创建一个包含一些经过编译的 Java 代码的 Java Archive (JAR),构建一个包含 Java 源代码的 JAR,并把两个 JAR 移到指定位置。

:Ant 是一个构建工具 — 有点类似 Java 版本的 make(极为简单的工具)— 该工具将使用 XML 格式的构建文件(默认情况下为 build.xml)。如果以前没使用过 Ant,您仍然能够继续学习本教程,但是可能需要以后花些时间了解 Ant 的更多信息(有关更多信息,请参阅侧栏 “了解 Ant 的更多信息”)。

为什么使用 Ant?

虽然可以使用批处理 (.bat) 或 shell (.sh) 脚本创建 JAR 并把它们移到其他位置,但是有很多原因使我们必须使用 Ant 完成前述操作。一个原因是因为 Ant 构建是可移植的 — 如果计划要在其他操作系统中使用 Eclipse 中的配置,那么可移植性是必须要考虑的事项。由于 Eclipse 可以在 Windows、Mac 和 Linux 上运行,因此移植的可能性一定高于仅在一个操作系统中运行的 IDE。


使用 Ant 执行构建的第二个原因是 Ant 是一个常见工具 — 至少在 Java 社区中是这样的。Eclipse Europa 有一个适用于 Ant 构建文件的特殊编辑器,该编辑器内置了语法突出显示功能和模板,这样如果您刚开始接触 build.xml 文件,就可以获得来自 IDE 的一些额外帮助。并且由于 Ant 被广泛使用,因此,团队中的其他人员很可能理解 Ant build.xml 文件,而不理解 .bat 或 .sh 文件,当文件十分复杂时尤其如此。

回页首

创建 JAR

清单 1 展示了样例 Ant 构建文件。您将在本教程中一直使用此构建文件来研究 Eclipse Europa 的功能。


清单 1. 样例 Ant build.xml 文件


<?xml version="1.0" encoding="UTF-8"?>

<project name="myAntExample" default="publish">

    <description>

        Short description here.

    </description>


    <property name="version" value="1.0.0" />

    <property name="dist.dir" value="dist" />

    <property name="publish.dir" value="published" />

    <property name="classes.jar" value="${dist.dir}/classes.jar" />

    <property name="sources.jar" value="${dist.dir}/sources.jar" />


    <target name="publish" depends="package" description="Published the files">

        <copy file="${classes.jar}" tofile="${publish.dir}/classes-${version}.jar" />

        <copy file="${sources.jar}" tofile="${publish.dir}/sources-${version.jar}" />

    </target>


    <target name="package" depends="prepare" description="Packages the jars">

        <jar destfile="${classes.jar}">

            <fileset dir="${classes.dir}">

                <include name="***.java" />

            </fileset>

        </jar>

    </target>


    <target name="prepare">

        <mkdir dir="${dist.dir}" />

        <mkdir dir="${publish.dir}" />

    </target>


</project>

要在 Eclipse 中添加新的 Ant 启动配置,请选择 Run > External Tools > Open External Tools Dialog 以访问 External Tools 窗口,如图 1 所示。在左侧窗格中,单击 Ant Build,然后单击 New 以创建新配置。Main 配置选项卡将被突出显示并启用,并且 Eclipse 将为配置添加一个默认名称。


图 1. External Tools 窗口

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

Main 配置选项卡

Main 配置选项卡包含 Ant 启动配置的一些基本信息,包括 Ant 构建文件的名称及需要传递给该文件的所有实参。图 2 显示 Eclipse 中的 Main 选项卡。


图 2. Main 配置选项卡

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码

设置构建文件

您可以键入构建文件的名称,但是为了消除错误,我发现单击 Browse Workspace 或 Browse File System(参见图 2)查找并选择文件更容易。清单 1 中所示的构建文件已被放到新项目的根目录下,因此使用 Browse Workspace 查找该文件。将显示工作区中的项目清单,您可以从中选择文件的位置。

浏览并选择构建文件后,请单击 OK。现在在 Buildfile 框中的值类似 ${workspace_loc:/exampleAntBuilder/build.xml}

通过只更新 Buildfile 值,您可以单击 Apply 或 Run,保存并使用配置。如果不需要执行比运行单个目标更复杂的操作,那么至此已完成全部操作。但是,您可以给 Ant 构建进程提供更多信息以进一步自定义启动配置。

回页首

传入自定义实参

在 Main 配置选项卡中,您可以把实参发送到 Ant 配置中,这只是向 Ant 传递值并以不同方式构建项目的一种方法。例如,在清单 1 中,build.xml 文件包括使用 <property> 标记定义的几个属性。但是,您将不会在构建文件中定义这些属性,而是传入这些属性。

传入自定义参数的一个原因是它允许您在不同项目中使用同一个构建文件,即使这些项目使用的目录和文件名不同。与使用同一 Ant 构建脚本的副本并针对各个项目进行相应调整相比,这可能是更好的替代方案。

Ant 将在 -D<property name>=<value> 表单中获取提供给它的经过覆盖的属性,因此如果需要查看详细的输出并覆盖 JAR 的 dist.dir变量(其中保存 JAR)的名称,则需要传入 -v -Ddist.dir=out。这段代码将把目录名称设为 out。单击 Apply,然后如果需要立即运行,请单击 Run

:还可以从工具栏获得 Ant 构建配置。

除了提供静态数据字符串之外,您可以在 Main 选项卡中单击 Variables 以自动插入可以传递给 Ant 的变量。表 1 中涵盖了可用变量,但是要快速测试提供版本,请使用名为 ${string_prompt} 的内置变量,它将提示您输入值。Main 选项卡中的 Arguments 值现在是 -v -Ddist.dir=out -Dversion=${string_prompt}

插入完变量后,请单击 Apply,然后单击 Run 以运行 Ant 启动配置。系统将提示您输入值,如图 3 所示。


图 3. 字符串输入提示

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
表 1. 启动配置变量

变量 说明
build_files 如果自动构建被启用,则此变量包括触发构建的文件的绝对路径。它支持使用参数缩小选择范围,例如,f 表示仅限于文件。
build_project 这是当前正在构建的项目的绝对路径。
build_type 该变量表示正在执行的构建的类型(incremental、full、auto 或 none)。如果尝试在启动配置不是构建器的情况下使用此变量,则值为 none
container_loc 这是当前资源的容器的绝对名称,可以是文件夹,也可以是目录。
container_name 这是当前资源所在容器的名称。
container_path 该变量表示容器到工作区的相对路径。
eclipse_home 这是 Eclipse 的安装位置。
env_var 该变量将输入给定环境变量的值,其中变量的名称可以作为实参提供给变量。
file_prompt 当 Eclipse 启动程序时,它将显示一个文件选择器。在提示时将选择的文件名放入变量值中。
folder_prompt 该变量与文件提示相同,但是只限于文件夹。
java_extensions_regex 这是匹配正则表达式的已注册的类 Java 文件扩展名。
java_type_name 该变量是所选资源中主要类型的完全限定的 Java 类型名。
project_loc 这是项目在文件系统中的绝对路径。
project_name 这是项目的名称。
project_path 这是当前项目到工作区的相对路径。
resource_loc 该变量表示当前资源在文件系统中的绝对路径。
resource_name 这是资源的名称。
resource_path 这是当前资源到工作区的相对路径。
selected_text 这是选中的文本的值(如果有的话)。
string_prompt 如果使用该变量,Eclipse 将提示您输入字符串值。您在提示时输入的任何内容都将被替换到变量中。
system 这是 Eclipse 系统变量值(ARCH、ECLIPSE_HOME、NL、OS、WS)。
system_path 该变量是作为实参提供的工具在文件系统中的绝对路径。
system_property 这是来自 Eclipse 运行时的系统属性值,其中属性名是变量的实参。
target.arch 该变量表示目标处理器架构(例如,适用于 Intel? 处理器的 x86)。
target.nl 这是目标语言环境(例如,en_US 或 fr_FR)。
target.os 这是目标操作系统(例如,macosx)。
target.ws 该变量表示目标窗口系统(例如,Mac 的 Carbon)。
target_home 这是目标主目录,它与 Eclipse 主目录相同。
workspace_loc 这是工作区在文件系统中的绝对路径。


其他有用实参

连同属性的覆盖值一起传入 Ant 配置的其他有用实参是允许调整日志记录的那些实参。例如,-logfile <filename> 将允许您把输出记录到文件中。使用 -q-v 和 -d 参数可以将输出级别分别调整为静默、详细和调试。


=================================================================================

http://blog.sina.com.cn/s/blog_697bbefe0100vqzc.html

eclipse调试android NDK C/C++代码配置指南

在经历了无数纠结后,决定写下这篇文章,让后面的开发人员少走弯路~

博文中的图片来自原文,可能会与我的开发环境有少许不同,但是整体步骤是对的。本人在以下环境中测试通过:
系统:Ubuntu 11.04
NDK版本:r6
eclipse版本:3.7

参考原文:
http://www.eclipse.org/sequoyah/documentation/native_debug.php

首先下面5步设置eclipse
第1步:
在AndroidManifest.xml文件中application项中加入android:debuggable="true",你也可以在eclipse里的可视化中设置,如下图。

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
第2步:设置你的C/C++调试选项
在Main的选项卡中,在C/C++ Application里面选择app_process作为调试对象(app_process文件会在你运行ndk-gdb时候生成)!而不是你编译出来的*.so文件!这个文件会在 你的项目路径/obj/local/armeabi/app_process  
还有记得选择Disable auto build 配置如下图

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
接下来点击图片中红色方框特别标志的地方,设置启动调试的进一步参数

第3步:选择调试启动方式
选择Standard Create Process 然后确定,如图

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
第4步:
在Debugger的选项卡中设置debugger模式为gdbserver,而Stop on start at就填你希望当调试开始时候的断点函数名(注意:这个函数名是C/C++的函数名称,即使你没有打上断点当运行到这个函数时会自动暂停)
在Debugger Option栏目框里的Main选项卡的GDB Debugger选择NDK的gdb,这个文件在  NDK目录/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin 里面,可能包含window平台等,请根据自己的开发机器和目标平台做正确的选择!!!接下来,需要修改一些东西,当你编译好你的工程后会生成一个gdb.setup的gdb配置文件,例如  你的工程目录/obj/local/armeabi/gdb.setup,先将该文件做个拷贝,命名为gdb2.setup,然后用打开这个文件将最后一行 target remote :5039删掉,因为接下来我们会在eclipse里面设置,因此这一行是多余的,如果不删掉的话会出错!然后就将改好的文件设置为GDB Command file,整体如下图

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
第5步:
在Debugger Option栏目框里的Connection选项卡设置连接类型Type为TCP,端口Port number为5039,如图

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
最后修改ndk-gdb这个文件,该文件位于你的NDK目录下,先将该文件做个拷贝,命名为ndk-gdb2,然后打开,将最后一行  $GDBCLIENT -x `native_path $GDBSETUP` 删掉,这一行的功能是启动控制台的gdb我们已经在eclipse里启动了,所以不需要这行!

具体调试步骤:
1.用eclipse启动android的java调试在你自己的Activity类中的onCreate方法中加一个断点,避免程序一闪而过你还没来得及启动C/C++部分的调试。
2.在你的工程目录下运行刚才修改好的ndk-gdb2,如果你需要一些信息可以在后面加上--verbose参数。
3.启动eclipse的C/C++调试

启动成功后如下图(这张图是我自己的,呵呵...)

jni4-Android <wbr>学习笔记--利用JNI技术在Android中调用、调试C++代码
一些比较弱智的个人心得:
android的模拟器是可以正常使用超级用户的权限的,当你是用真机设备调试的时候请确保你的真机设备已经root否则会出现一些权限的错误,甚至导致莫名奇妙的问题...我就被这些问题纠结了几天.


阅读(2498) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~