你的位置:首页 > 操作系统

[操作系统]开发纯ndk程序之环境搭配


  • 安装ndk

从安卓官网下载,ndk,双击解压到当前文件夹。建议想装在那个文件夹便解压到那个文件夹,而且文件夹的路径中不要有空格,因为gcc编译的时候会把空格前后两个字符串作为两个文件夹来对待。

  • 使用gcc手动编译

使用gcc编译程序需要先编写makefile文件,然后通过gcc make工具进行编译,makefile文件内容如下:

 1 NDK_ROOT=C:/android-ndk-r10d 2 TOOLCHAINS_ROOT=$(NDK_ROOT)/toolchains/arm-linux-androideabi-4.9/prebuilt/windows 3 TOOLCHAINS_PREFIX=$(TOOLCHAINS_ROOT)/bin/arm-linux-androideabi 4 TOOLCHAINS_INCLUDE=$(TOOLCHAINS_ROOT)/lib/gcc/arm-linux-androideabi/4.9/include-fixed 5 PLATFORM_ROOT=$(NDK_ROOT)/platforms/android-21/arch-arm 6 PLATFORM_INCLUDE=$(PLATFORM_ROOT)/usr/include 7 PLATFORM_LIB=$(PLATFORM_ROOT)/usr/lib 8 MODULE_NAME=hello 9 RM=del10 FLAGS=-I$(TOOLCHAINS_INCLUDE)\11   -I$(PLATFORM_INCLUDE)\12   -L$(PLATFORM_LIB)\13   -nostdlib\14   -lgcc\15   -Bdynamic\16   -lc17 OBJS=$(MODULE_NAME).o\18   $(PLATFORM_LIB)/crtbegin_dynamic.o\19   $(PLATFORM_LIB)/crtend_android.o20 21 all:22   $(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -c $(MODULE_NAME).c -o $(MODULE_NAME).o23   $(TOOLCHAINS_PREFIX)-gcc $(FLAGS) $(OBJS) -o $(MODULE_NAME)24 clean:25   $(RM) *.o26 install:27   adb push $(MODULE_NAME) /data/local/tmp28   adb shell chmod 755 /data/local/tmp/$(MODULE_NAME)

其中gcc make工具位于..\android-ndk-r10d\prebuilt\windows\bin下,此目录需要加入到系统或临时的PATH环境变量中,然后将hello.c于makefile文件放到一个目录中,开启虚拟机,然后依次执行以下命令:

makemake install adb shell /data/local/tmp/hello

便会看到熟悉的“hello world!!!”输出了。

makefile中的I和L要记得分清,我没注意到,结果编译的时候总是报错。

  • 使用ndk-build编译

使用ndk-build编译需要一个Android工程,这里使用android脚本来生成一个android工程,此脚本位于../sdk/tools下面,文件名是android.bat,在命令行输入“android --help”可以查看完整的命令,如“android avd”可以启动安卓虚拟机,这里使用“create project”选项,如下面:

android create project -n hello -p hello -t android-21 -k com.droider.hello -a MyActivity

“-n”指定android工程的名称,“-t”指定生成android工程所需要使用平台版本号,此版本号使用“android list”列出,“-p”指定生成工程的目录名,“-k”指定android工程的包名,“-a”指定默认Activity的名称。

Android工程生成好了,在工程目录下面新建一个jni文件夹,并将hello.c文件复制进去,然后编写ndk-build所需要的两个脚本,名字分别为Android.mk与Application.mk,其中后者是可选的。首先,这两个文件的内容在NDK文档中都有描述,打开android-ndk-r10d\docs里面Start_Here.html,选择“NDK Programmer's Guide”,在左侧列表中有“Android.mk“与”Application.mk”按钮,里面有详细内容描述。

好,首先编写Android.mk,如下

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_ARM_MODULE := armLOCAL_MODULE  := helloLOCAL_SRC_FILES := hello.cinclude $(BUILD_EXECUTABLE)

LOCAL_PATH 定义了本地源码的路径,它是Android.mk中必须首先定义好的变量,call my-dir指定了调用my-dir宏,它是由编译系统提供的,返回Android.mk文件本身所在的路径,一般与源码文件目录相同。
CLEAR_VARS指定让编译系统清除掉一些已经定义过的宏,这些宏的定义都是全局的。如LOCAL_MODULE、LOCAL_SRC_FILE,当一个GUN MAKE在编译多个模块时,必须清除并重新设置它们。
LOCAL_ARM_MODULE 指定生成的原生程序所使用的ARM指令模式。arm表示使用32指令系统。
LOCAL_MODULE 指定模块的名称。
LOCAL_SRC_FILE指定c或c++源文件列表。
include $(BUILD_EXECUTABLE)指定生成的文件类型。
然后是Application.mk,如下
APP_ABI := armeabi-v7a

这里指定平台是armeabi-v7a。

最后将Android.mk与Application.mk共同放在jni文件夹下,在工程目录下执行ndk-build,最后生成的文件在libs文件夹下。

  • 搭配调试环境

首先,调试需要gdbserver,还需要有调试符号信息,这里有两个方案,我看文档上说事两个方案缺一不可,但是我测试是两个方案中一个就可以了,其一便是在使用“ndk-build NDK_DEBUG=1”命令来编译,另一个便是在AndroidManifest.

[armeabi-v7a] Gdbserver   : [arm-linux-androideabi-4.8] libs/armeabi-v7a/gdbserver[armeabi-v7a] Gdbsetup    : libs/armeabi-v7a/gdb.setup[armeabi-v7a] Install    : hello => libs/armeabi-v7a/hello

可见生成了gdbserver,在libs文件夹里面,而含有调试符号信息的hello则在objs文件夹里面,然后将这两个文件一起推送到安卓设备里面,使用如下命令:

  adb push ./obj/local/armeabi-v7a/hello /data/local/tmp  adb push ./libs/armeabi-v7a/gdbserver /data/local/tmp  adb shell chmod 755 /data/local/tmp/hello  adb shell chmod 755 /data/local/tmp/gdbserver

启动gdbserver,使其监听端口7000,如下:

adb shell /data/local/tmp/gdbserver :7000 /data/local/tmp/hello

结果如下

adb shell /data/local/tmp/gdbserver :7000 /data/local/tmp/helloProcess /data/local/tmp/hello created; pid = 19133Listening on port 7000

然后启动另一个终端,gdb在android-ndk-r10d\toolchains\arm-linux-androideabi-4.9\prebuilt\windows\bin文件夹里面,我是见这个文件夹路径添加到了环境变量,然后将arm-linux-androideabi-gdb.exe名字改为gdb.exe,这样可以少敲几个字,后来我做成了makefile文件,发现其实做了无用功,呵呵。首先将安卓设备的7000端口映射到本地7000端口,使用如下命令

adb forward tcp:7000 tcp:7000

然后,启动gdb,同时别忘了指定调试文件啊,不然会找不到调试符号的,命令如下

gdb ./obj/local/armeabi-v7a/hello

此时,进入了gdb,如下

GNU gdb (GDB) 7.6Copyright (C) 2013 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "--host=i586-pc-mingw32msvc --target=arm-linux-android".For bug reporting instructions, please see:<http://source.android.com/source/report-bugs.html>...Reading symbols from F:\work\Code\ndk\NdkBuild\helbul\obj\local\armeabi-v7a\hello...done.(gdb)

然后为gdb指定调试进程,如下

target remote localhost:7000

此时,可以调试程序了,先来感受一下吧,使用l命令查看一下源码:

(gdb) lCannot access memory at address 0x01    #include<stdio.h>2    int main(int argc, void* argv)3    {4        printf("hello world\n");5        return 0;6    }(gdb)

然后就是gdb调试的内容了。

我在调试的时候总是提醒找不到共享库,此库位于android-ndk-r10d\platforms\android-21\arch-arm\usr\lib里面,可以使用下面的命令来设置:

set solib-search-path C:/android-ndk-r10d/platforms/android-21/arch-arm/usr/lib

由于指定远程调试进程和设置共享库位置经常用到,可以写成gdb脚本,如下:

define ig  target remote localhost:7000  set solib-search-path C:/android-ndk-r10d/platforms/android-21/arch-arm/usr/libend

然后保存为文件,文件名我取为“InitGdb.gdb”,然后启动gdb后,使用source命令载入脚本,并运行,如下:

(gdb) source InitGdb.gdb(gdb) ig

果然还是讨厌重复性的劳动。

虽然设置了共享库的目录,但还是提醒我库的位置不对,不匹配,所以我想把他们都编译成静态库,结果老失败,这里留下个悬念,以后再来解决吧。