引用: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. Updating My Notepad_1.1

    The old version Notepad 1.0 you can get it from below link : My Notepad I am very happy to announce ...

  2. Hibernate学习笔记4

    一.关于联合主键的映射测试实例 实体类: package com.***.comBineKey;public class Person { private Person_pk pk; private ...

  3. 不可变字符串NSString

    /*字符串的常用方法*/ //1.通常用来把一些基本数据类型和字符串进行拼接 ; float b = 9527.0; NSString *string = [NSString stringWithFo ...

  4. RCurl getURL()函数做debug

    getURL()函数做获取网页做debug,三步骤 1.首先创建一个对象debugGatherer(),该对象包含三个函数:(update(), value(), reset()); R> de ...

  5. 11.static关键字

    1.用static修饰的方法,直接用类调用 2.static修饰的方法只能调用static方法,不能调用非  static属性和方法 ①因为static属性和方法在类没有实例化的时候调用 ②因为普通属 ...

  6. 第一章-第十一题(请问 “软件” 和 “软件工程” 这些词汇是如何出现的 - 何时、何地、何人)--By 侯伟婷

    从邹欣老师的<构建执法:现代软件工程>一书中,我们得到有关这些名词的起源的信息是软件工程的概念是1968年第一次提出的[1].而在一篇专访Margaret Hamilton的报道中,我们通 ...

  7. 机器学习常用Python扩展包

    在Ubuntu下安装Python模块通常有3种方法:1)使用apt-get:2)使用pip命令(推荐);3)easy_instal 可安装方法参考:[转]linux和windows下安装python集 ...

  8. PHP 天巡机票接口

    一个旅游网站项目,网站需要机票预订接入了天巡机票接口,获取机票信息,不搞不知道,一搞吓一跳比较麻烦. 搜索机票信息需要分2步,首先POST获得一个SESSION,2秒之后,根据这个SESSION,从一 ...

  9. AI PRO I 第4章

    Behavior Selection Algorithms An Overview Michael Dawe, Steve Gargolinski, Luke Dicken, Troy Humphre ...

  10. Organization SYMMETRIC MULTIPROCESSORS

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION Figure 17.4 depicts i ...