购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.4 实战演练—演示两种编译Android程序的方法

知识点讲解:光盘:视频\知识点\第2章\演示两种编译Android程序的方法.avi

Android编译环境本身比较复杂,并且不像普通的编译环境那样只有顶层目录下才有Makefile文件,而其他的每个component都使用统一标准的Android.mk文件。不过这并不是我们熟悉的Makefile,而是经过Android自身编译系统的很多处理的。所以说要真正理清楚其中的联系还比较复杂,不过这种方式的好处在于,编写一个新的Android.mk给Android增加一个新的Component会变得比较简单。为了使读者更加深入地理解在Linux环境下编译Android程序的方法,在接下来的内容中,将分别演示两种编译Android程序的方法。

2.4.1 编译Native C(本地C程序)的helloworld模块

编译Java程序可以直接采用Eclipse的集成环境来完成,实现方法非常简单,在这里就不再重复了。接下来将主要针对C/C++进行说明,通过一个例子来讲解在Android中增加一个C程序的Hello World的方法。

(1)在$(YOUR_ANDROID)/development目录下创建一个名为hello的目录,并用“$(YOUR_ANDROID)”指向Android源代码所在的目录。

- # mkdir $(YOUR_ANDROID)/development/hello

(2)在目录$(YOUR_ANDROID)/development/hello/下编写一个名为hello.c的C语言文件,文件hello.c的实现代码如下所示。

#include <stdio.h>
int main()
{
    printf("Hello World!\n");//输出Hello World
return 0;
}

(3)在目录“$(YOUR_ANDROID)/development/hello/”下编写Android.mk文件。这是Android Makefile的标准命名,不能更改。文件Android.mk的格式和内容可以参考其他已有的Android.mk文件的写法,针对helloworld程序的Android.mk文件内容如下所示。

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
    hello.c
LOCAL_MODULE := helloworld
include $(BUILD_EXECUTABLE)

上述各个内容的具体说明如下。

(4)回到Android源代码顶层目录进行编译。

# cd $(YOUR_ANDROID)&& make helloworld

在此需要注意,make helloworld中的目标名helloworld就是上面Android.mk文件中由LOCAL_MODULE指定的模块名,最终的编译结果如下所示。

target thumb C: helloworld <= development/hello/hello.c
target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/ helloworld)
target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/helloworld)
target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld)
Install: out/target/product/generic/system/bin/helloworld

(5)如果和上述编译结果相同,则编译后的可执行文件存放在如下目录:

out/target/product/generic/system/bin/helloworld

这样通过adb push将它传送到模拟器上,再通过adb shell登录到模拟器终端后就可以执行了。

2.4.2 手工编译C模块

前面讲解了通过标准的Android.mk文件来编译C模块的具体流程,其实也可以直接运用gcc命令行来编译C程序,这样可以更好地了解Android编译环境的细节。具体流程如下。

(1)在Android编译环境中,提供了showcommands选项来显示编译命令行,我们可以通过打开这个选项来查看一些编译时的细节。

(2)在具体操作之前需要使用如下命令把前面中的helloworld模块清除。

# make clean-helloworld

上面的“make clean-$(LOCAL_MODULE)”命令是Android编译环境提供的make clean的方式。

(3)使用showcommands选项重新编译helloworld,具体命令如下所示。

  # make helloworld showcommands
  build/core/product_config.mk:229: WARNING: adding test OTA key
  target thumb C: helloworld <= development/hello/hello.c
prebuilt/linux-x86/toolchain/arm-eabi-5.0.1/bin/arm-eabi-gcc -I system/core/include -I hardware/libhardware/
include   -I hardware/ril/include   -I dalvik/libnativehelper/include   -I frameworks/base/include   -I external/ 
skia/include   -I out/target/product/generic/obj/include   -I bionic/libc/arch-arm/include   -I bionic/libc/include   
-I bionic/libstdc++/include   -I bionic/libc/kernel/common   -I bionic/libc/kernel/arch-arm   -I bionic/libm/ 
include   -I bionic/libm/include/arch/arm   -I bionic/libthread_db/include   -I development/hello   -I 
out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates  -c  -fno-exceptions -Wno-multichar 
-march=armv5te -mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables 
-fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ 
-include system/core/include/arch/linux-arm/AndroidConfig.h -DANDROID -fmessage-length=0 -W -Wall 
-Wno-unused -DSK_RELEASE -DNDEBUG -O2 -g -Wstrict-aliasing=2 -finline-functions 
-fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -DNDEBUG 
-UDEBUG -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64      -MD -o 
out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o development/hello/hello.c
 
target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/ helloworld)
 
prebuilt/linux-x86/toolchain/arm-eabi-5.0.1/bin/arm-eabi-g++ -nostdlib -Bdynamic -Wl,-T,build/core/armelf.x 
-Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -o out/target/product/generic/obj/ 
EXECUTABLES/helloworld_intermediates/LINKED/helloworld -Lout/target/product/generic/obj/lib -Wl,-rpath-link 
=out/target/product/generic/obj/lib -lc -lstdc++ -lm  out/target/product/generic/obj/lib/crtbegin_ dynamic.o         
out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o       -Wl,--no-undefined 
prebuilt/linux-x86/toolchain/arm-eabi-5.0.1/bin/../lib/gcc/arm-eabi/5.0.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o
 
target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/helloworld)
 
out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/ 
helloworld out/target/product/generic/symbols/system/bin/helloworld
 
target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld)
 
  out/host/linux-x86/bin/soslim --strip --shady --quiet out/target/product/generic/symbols/system/bin/helloworld 
--outfile out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld
 
Install: out/target/product/generic/system/bin/helloworld
 
  out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld out/target/product/generic/system/bin/helloworld

从上述命令行可以看到,Android编译环境所用的交叉编译工具链如下所示。

prebuilt/linux-x86/toolchain/arm-eabi-5.0.1/bin/arm-eabi-gcc

其中参数-I和-L分别指定了所用的C库头文件和动态库文件路径分别是bionic/libc/include和out/target/product/generic/obj/li,其他还包括很多编译选项以及-D所定义的预编译宏。

(4)此时就可以利用上面的编译命令来手工编译helloworld程序,首先手工删除上次编译得到的helloworld程序。

# rm out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o
# rm out/target/product/generic/system/bin/helloworld

然后再用gcc编译以生成目标文件。

  # prebuilt/linux-x86/toolchain/arm-eabi-5.0.1/bin/arm-eabi-gcc -I bionic/libc/arch-arm/include -I bionic/libc/include -I 
bionic/libc/kernel/common   -I bionic/libc/kernel/arch-arm -c  -fno-exceptions -Wno-multichar -march=armv5te 
-mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -D__
ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -include system/
core/include/arch/linux-arm/AndroidConfig.h -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -DSK_
RELEASE -DNDEBUG -O2 -g -Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once -fgcse-
after-reload -frerun-cse-after-loop -frename-registers -DNDEBUG -UDEBUG -mthumb -Os -fomit-frame- pointer -fno-strict-aliasing -finline-limit=64      -MD -o out/target/product/generic/obj/EXECUTABLES/ helloworld_
intermediates/hello.o development/hello/hello.c

如果此时与Android.mk编译参数进行比较,会发现上面主要减少了不必要的参数-I。

(5)接下来开始生成可执行文件。

  # prebuilt/linux-x86/toolchain/arm-eabi-5.0.1/bin/arm-eabi-gcc -nostdlib -Bdynamic -Wl,-T,build/core/armelf.x 
-Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -o out/target/product/generic/obj/ 
EXECUTABLES/helloworld_intermediates/LINKED/helloworld -Lout/target/product/generic/obj/lib 
-Wl,-rpath-link=out/target/product/generic/obj/lib -lc -lm  out/target/product/generic/obj/EXECUTABLES/ 
helloworld_intermediates/hello.o out/target/product/generic/obj/lib/crtbegin_dynamic.
o -Wl,--no-undefined ./prebuilt/linux-x86/toolchain/arm-eabi-5.0.1/bin/../lib/gcc/arm-eabi/5.0.1/interwork/libgcc.
a out/target/product/generic/obj/lib/crtend_android.o

在此需要特别注意的是参数“-Wl,-dynamic-linker,/system/bin/linker”,它指定了Android专用的动态链接器是“/system/bin/linker”,而不是平常使用的ld.so。

(6)最后可以使用命令file和readelf来查看生成的可执行程序。

  # file out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld
  out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
  #  readelf -d out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld |grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]

这就是ARM格式的动态链接可执行文件,在运行时需要libc.so和libm.so。当提示not stripped时表示它还没被STRIP(剥离)。嵌入式系统中为节省空间通常将编译完成的可执行文件或动态库进行剥离,即去掉其中多余的符号表信息。在前面make helloworld showcommands命令的最后我们也可以看到,Android编译环境中使用了out/host/linux-x86/bin/soslim工具进行STRIP。 ZMwBwPsgLCTMbiqtuOGgYZSQ5DS9XB84y3KcZUT6CAni1SV8Vna92XHfA6Amk6RU

点击中间区域
呼出菜单
上一章
目录
下一章
×