引用:http://www.cnblogs.com/ychellboy/archive/2013/02/22/2922683.html

使用NDK在android上做开发是一件“痛并快乐着”的差事,之所以“快乐”是因为可以将一些原有的C/C++库直接移植到android上,而不需要用java再开发一套功能相同的库。然而这同时也是一件“痛苦”的事件,因为android本身是裁减过的linux,好些system call不能使用,另外由于没有采用glibc(用的是Bionic libc,原因见wiki),好些函数所在的头文件位置也有变化,这都给移植工作带来困难。更为坑爹的是一些函数在头文件里能找到定义在具体库里确没有实现(比如:pthread_mutex_timedlock)。

android native开发在编译链接阶段会遇到上述“惨痛”经历,但更为痛苦的是好不容易变成可执行文件,一运行就crash没有任何信息。遇到这种情况,在排除了代码有低级错误的情况后,最终只能想办法做debug。(本文余下篇幅在不特殊注明的情况下都是指使用NDK在android上做native code的开发)。

在android上NDK开发的程序进行查错主要有两种方法:

(1)使用log进行查错:在程序源代码上加log,根据log信息来排查错误。这种方式应该是最为常用的,因为其普适性很高。不过作为在VxWorks上移植过网络库的苦逼,深知用log排错的效率是多么的低,特别是在排查底层库时。而遇到多线程的程序,log排错是多么的无力。

(2)使用ndk-gdb调试程序:用过gdb的都知道它多么的强大,但是想要使用ndk-gdb需要做很多的配置,还会碰到很多坑,因此想真正使用起来也不是件容易的事(毕竟是开源项目,和VxWorks这种高富帅是没法比的)。

本文主要介绍如何配置使用ndk-gdb进行debug,所使用android-ndk-r8d/samples/hello-jni作为入口调用一个static library。 ——  Here we go!

一、开发环境

1. ubuntu 12.04 x86_64

2. eclipse 3.7(只是为了方便启动android模拟器)

3. android NDK r8d

4. android SDK 2.2 ~ 4.2

5. ant (打包程序使用)

在windows环境下可以配置cygwin来实现ndk-gdb,本人在windows上使用相同方法也达到了效果,对cygwin的配置这里不再讨论,有疑问可以找google老师。

二、准备阶段

1. 下载linux平台的NDK,并解压到相应目录。这里需要注意的是:虽然google网站上写着NDK for Linux 32/64-bit(x86),但是ndk中的一些工具(比如NDK自带的awk,make,sed)在64bit的ubuntu上并不能直接运行,因为这些工具是32bit的程序,需要32bit的运行时库。解决方法是:sudo apt-get install libc6-i386, sudo apt-get install lib32asound2 lib32z1 lib32stdc++6 lib32bz2-1.0  安装这些常用的32位库。如果是CentOS则需要:yum install libgcc.i686 yum install glibc-static.i686 yum install glibc-devel.i686

2. 下载SDK,在下载页面的”DOWNLOAD FOR OTHER PLATFORMS“ –>“ADT Bundle”找到对应的版本下载,并解压到相应的目录。这时SDK下的platforms会有最新版本的android,下载历史版本的android就要使用tools下的工具:./android list sdk 根据列举出来的编号执行如下: ./android update sdk –t 1 –u 则更新编号是1的包。使用android update把所需要的历史版本都下载下来。

3. 根据实际情况在~/.profile(或~/.bash_profile)中设置如下环境变量,设置完毕后执行source ~/.profile使之生效:

# ---- NDK ---- 
NDK_ROOT=~/mysoftware/NDK/android-ndk-r8d 
PATH=$PATH:$NDK_ROOT 
export NDK_ROOT

# ---- android-SDK ---- 
ANDROID_SDK_ROOT=~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk 
PATH=$PATH:$ANDROID_SDK_ROOT 
export ANDROID_SDK_ROOT

# ---- adb ---- 
ADB_PATH=~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk/platform-tools 
PATH=$PATH:$ADB_PATH

# ---- tools/android ---- 
PATH=$PATH:~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk/tools

export PATH

三、修改hello-jni

由于项目使用c++编程,做这个实验的时候就将jni/hello-jni.c 改为hello-jni.cpp,代码如下:

   1: #include <string.h>
   2: #include <jni.h>
   3: #include <unistd.h>
   4: #include "shared/thread.h"
   5:  
   6: /* This is a trivial JNI example where we use a native method
   7:  * to return a new VM String. See the corresponding Java source
   8:  * file located at:
   9:  *
  10:  *   apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java
  11:  */
  12:  
  13: using namespace shared;
  14:  
  15: extern "C"  {
  16:  
  17: void* StartThread(void* obj)
  18: {   
  19:     return NULL;
  20: }   
  21:  
  22: jstring
  23: Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
  24:                                                   jobject thiz )
  25: {       
  26:     volatile int bGo = 0;
  27:     while(!bGo) {
  28:         sleep(1);
  29:     }   
  30:     
  31:     Thread mythread(&StartThread, NULL);
  32:     mythread.Start();
  33:     
  34:     return env->NewStringUTF("Hello from JNI !");
  35:     //return (*env)->NewStringUTF(env, "Hello from JNI !");
  36: }   
  37:  
  38: }

注意需要用extern “C”{ } 把Java_com_example_hellojni_HelloJni_stringFromJNI函数包起来,while (!bGo)是为了方便调试,因为ndk-gdb会先把程序run起来后再attach上去,这里需要一个while让程序等一会。上述代码中的Thread类是在libshared.a的静态库中,因此需要修改hello-jni目录下的jni/Android.mk文件。如下:

   1: LOCAL_PATH := $(call my-dir)
   2:  
   3: include $(CLEAR_VARS)
   4: LOCAL_MODULE := shared
   5: LOCAL_SRC_FILES := ../shared/obj/local/armeabi/libshared.a
   6: LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/shared
   7: include $(PREBUILT_STATIC_LIBRARY)
   8:  
   9: include $(CLEAR_VARS)
  10: LOCAL_MODULE    := hello-jni
  11: LOCAL_SRC_FILES := hello-jni.cpp
  12: LOCAL_STATIC_LIBRARIES := shared
  13: LOCAL_C_INCLUDES := $(LOCAL_PATH)/../
  14:  
  15: include $(BUILD_SHARED_LIBRARY)

红色部分为添加或修改项,编译前需要在环境变量C_INCLUDE_PATH中加入jni.h的路径,比如:

   1: C_INCLUDE_PATH=$C_INCLUDE_PATH:~/mysoftware/NDK/android-ndk-r8d/platforms/android-8/arch-arm/usr/include
   2:  
   3: export C_INCLUDE_PATH

PS:libshared.a在build时需要加NDK_DEBUG=1的参数,即:ndk-build NDK_DEBUG=1,这么编译才能带上debug信息。

四、万事俱备

1. shell进入ndk/samples/目录,运行android update project --path hello-jni,生成build.xml用于apk打包。(也可以在hello-jni目录里运行:android update project -t 1 -p . --subprojects)

2. 进入ndk/samples/hello-jni,修改AndroidManifest.xml文件

   1: <application android:label="@string/app_name"

   2:      android:debuggable="true">

3. 运行ndk-build

4. 运行ant debug

5. 启动android的模拟器(可以从eclipse启动)

6. 运行adb install –r bin/HelloJni-debug.apk

7. 运行ndk-gdb –start 开始debug,后续和使用gdb一样

8. 需要图形化界面进行debug,可以参考[2]

    几点重要说明:

1. ndk-gdb用的是client/server形式对目标机器进行debug, gdb 调试器 与 gdbserver 的关系,就是 gdb 与 stub的关系,如下图所示[3] :

2. ndk-gdb最坑爹的是:gdb和gdbserver的版本必须是匹配的才能debug:

每一个模拟器在system/bin下都有gdbserver,这些gdbserver是和模拟器本身的android版本有关的,而下载的NDK的ndk-gdb一般都是最新的gdb,因此gdb和gdbserver的版本常常匹配不了。这时需要把对应版本的gdbserver push到emulator上,然后指定./gdbserver,必须指定“./”因为在linux下默认优先查找system目录。

References:

[1] 使用eclipse/ndk-gdb对java/native code联合调试

[2] Eclipse+CDT+GDB调试android NDK程序

[3] ndk-gdb对java/native code联合调试

[4] 使用eclipse/ndk-gdb对java/native code联合调试

[5] 把hello-jni的.c后缀改成.cpp后出错

 
 
分类: Android开发

NDK开发历程(一):android native code的调试方法的更多相关文章

  1. GDB + gdbserver 远程调试android native code

    原文地址:GDB + gdbserver 远程调试android native code 作者:tq08g2z 以调试模拟器中的native library code为例. Host: ubuntuT ...

  2. ndk-gdb 对java/native code联合调试(升级版)

    之前写过一篇 关于android native 开发,调试的文章(http://www.cnblogs.com/yaozhongxiao/archive/2012/03/13/2393959.html ...

  3. NDK环境搭建(Native Code的编译,不需要Cygwin)

    分类: android2013-06-21 15:49 475人阅读 评论(0) 收藏 举报 Android NDK 目录(?)[-] System and Software Requirements ...

  4. [教程] Android Native内存泄漏检测方法

    转载请注明出处:https://www.cnblogs.com/zzcperf/p/9563389.html Android 检测 C/C++内存泄漏的方法越来越简便了,下面列举一下不同场景下检测C/ ...

  5. android应用崩溃的调试方法(c++ lib so文件库崩溃)

    android调试工具addr2line使用: 1.将ndk中的arm-linux-androideabi-addr2line可执行文件的路径加入配置文件~/.bashrc中,例如: export P ...

  6. 使用Unity开发Android的几种调试方法

    前言 本文举例几种Android 调试的方法(PS:我是通过unity引擎来开发安卓游戏) Eclipse + adt 查看LOG 1.为Eclipse 装上adt 插件 2.打开Eclipse 的L ...

  7. android默认开启adb调试方法分析

    用adb调试android时,每次接入usb线,都会提示一个确认打开usb调试功能的窗口,有时候,我们需要默认打开usb调试功能.或者无需弹出对话框,直接默认开启.这个我们需要分析adb的流程了. a ...

  8. android机顶盒真机调试方法

    最近接触电视APP开发,之前对Android开发也不太了解还一直以为不能真机调试.最近静下心来想一想肯定能真机调试的,我是我不知道而已.现在讲述一下真机调试的步骤: 1.进入设置--关于,连续点击版本 ...

  9. Android平台 Psensor传感器调试方法

    一. 驱动层调试1.在板级dts文件中增加相应的dts代码,如:调试sensor类,在代码中找到已经有的代码在修改,I2C使用那根(一般为I2C3),I2C地址(找datasheet或者FAE),哪个 ...

随机推荐

  1. 有限状态机(FSM)

    在游戏开发中,AI是个永恒不变的话题,如果你要的AI只是很简单的一个逻辑 那么有限状态机是一个很好的解决方案,尽管在实际开发中,AI的设计并不是一个简单的逻辑, 如果用有限状态机,维护起来会非常麻烦, ...

  2. i2c协议

    i2c协议 http://blog.csdn.net/g_salamander/article/details/8016698 总线设备驱动模型 http://blog.csdn.net/u01395 ...

  3. python 之 前端初识 html

    html语法代码示例 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  4. CommonJS Promises/A规范

    本文来自四火哥的翻译 CommonJS是一组javascript编程规范,而promise是其中之一. 简而言之,promises是一种令代码的异步行为变得更加优雅的软件抽象.在基本的定义中,代码可能 ...

  5. PHP伪静态

    伪静态 从形式上看是一个静态地址,实际上还是访问的动态地址, 比如:访问:http://www.abc.com/news-sport-id123.html 实际上是:http://www.abc.co ...

  6. SQL Server中的事物

    1.事务的四个属性 原子性Atomicity,一致性Consistency,隔离性Isolation,持久性Durability ,即ACID特性. 原子性:事务必须是一个完整工作的单元,要么全部执行 ...

  7. gcc编译命令行依赖库的指定顺序

    gcc链接过程中定义了三个集合:可重定位目标文件集合E.未解析符号集合U和已定义符号集合D,链接基本流程如下: 1) 按命令行指定顺序依次处理每个目标文件和库文件: 2) 如果为目标文件,将其加入集合 ...

  8. svn 服务器搭建

    http://www.cnblogs.com/wrmfw/archive/2011/09/08/2170465.html 一,安装必须的软件包. $ apt-get install subversio ...

  9. 关于python,一些整理

    参数传递 1 a = 1 2 def fun(a): 3 a = 2 4 fun(a) 5 print a 6 7 # 输出: 1 a = [] def fun(a): a.append(1) fun ...

  10. drop表后仍占表空间解决办法

    练习oracle时create了很多表,drop表后select * from tab; 网上找了好些方法,但是好多都适用... SQL>purge recyclebin; 回收站已清空.