Loading [MathJax]/jax/output/HTML-CSS/jax.js

NDK开发历程(一):android native code的调试方法

简介: 引用:http://www.cnblogs.com/ychellboy/archive/2013/02/22/2922683.html   使用NDK在android上做开发是一件“痛并快乐着”的差事,之所以“快乐”是因为可以将一些原有的C/C++库直接移植到android上,而不需要用java再开发一套功能相同的库。

引用: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] :

image

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开发
相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
71 19
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
79 14
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
158 3
Android code wiki
  Android code wiki Tip1: 类的全局静态变量的使用,这样可以静态变量只分配一次内存,可以不通过类的对象也就是可以通过类名直接使用该变量。(使用场景:Request_Code ,Result_Code,Log Tag,权限名字,Activity之间传递参数Name e...
782 0
Android_CodeWiki_02
1、使用TransitionDrawable实现渐变效果     private void setImageBitmap(ImageView imageView, Bitmap bitmap) { // Use TransitionDrawable to fade in.
741 0
Android_CodeWiki_03
1、发送不重复的通知(Notification)    public static void sendNotification(Context context, String title, String message, Bundle extras) { ...
741 0
Android_CodeWiki_04
1、展开、收起状态栏      public static final void collapseStatusBar(Context ctx) { Object sbservice = ctx.
774 0
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等