基于 Android NDK 的学习之旅目录
来源:Linux社区 作者:Linux
Android是一个专为移动设备设计的软件平台,它包括一个操作系统、中间件和一些关键性的平台应用。目前发布的Android SDK提供了使用Java语言开发Android平台应用的必要工具和API。
基于 Android NDK 的学习之旅目录
基于 Android NDK 的学习之旅-----序言 http://www.linuxidc.com/Linux/2011-08/40814.htm基于 Android NDK 的学习之旅-----环境搭建 http://www.linuxidc.com/Linux/2011-08/40812.htm
基于 Android NDK 的学习之旅-----Android.mk 介绍 http://www.linuxidc.com/Linux/2011-08/40811.htm
基于 Android NDK 的学习之旅-----HelloWorld (附源码) http://www.linuxidc.com/Linux/2011-08/40816.htm
基于 Android NDK 的学习之旅-----JNI LOG 打印(附源码) http://www.linuxidc.com/Linux/2011-08/40817.htm
基于 Android NDK 的学习之旅-----JNI 数据类型 http://www.linuxidc.com/Linux/2011-08/40815.htm
基于 Android NDK 的学习之旅-----Java 调用 C(附源码) http://www.linuxidc.com/Linux/2011-08/40810.htm
基于 Android NDK 的学习之旅----- C调用Java(附源码) http://www.linuxidc.com/Linux/2011-08/41173.htm
基于 Android NDK 的学习之旅----- Java 方法映射到C中的签名(附源码) http://www.linuxidc.com/Linux/2011-08/41174.htm
基于 Android NDK 的学习之旅-----数据传输一(基本数据类型和数组传输)(附源码) http://www.linuxidc.com/Linux/2011-08/41175.htm
基于 Android NDK 的学习之旅-----数据传输二(引用数据类型)(附源码) http://www.linuxidc.com/Linux/2011-08/41176.htm
基于 Android NDK 的学习之旅-----资源释放http://www.linuxidc.com/Linux/2011-08/41278.htm
以下是一些GNU Make的宏‘函数’,必须通过这样的形式调用:'$(call<function>)'。
函数返回文本信息。
my-dir
all-subdir-makefiles
this-makefile
parent-makefile
grand-parent-makefile
组件描述相关的变量:
- - - - - - - - - -
以下的变量是用来向生成系统描述你的组件的。你应该在'include $(CLEAR_VARS)'
和'include$(BUILD_XXXXX)'之间定义其中的一些变量。正如在前面所说的,$(CLEAR_VARS)
是一个将会取消所有这些变量的脚本,除非在对变量的描述时有显式的说明。
LOCAL_PATH
LOCAL_MODULE
LOCAL_SRC_FILES
LOCAL_CPP_EXTENSION
LOCAL_C_INCLUDES
LOCAL_CFLAGS
LOCAL_CXXFLAGS
LOCAL_CPPFLAGS
LOCAL_STATIC_LIBRARIES
LOCAL_SHARED_LIBRARIES
LOCAL_LDLIBS
LOCAL_ALLOW_UNDEFINED_SYMBOLS
LOCAL_ARM_MODE
怎样添加一个模块
LOCAL_PATH:= $(call my-dir)
#编译静态库
include $(CLEAR_VARS)
LOCAL_MODULE = libhellos
LOCAL_CFLAGS = $(L_CFLAGS)
LOCAL_SRC_FILES = hellos.c
LOCAL_C_INCLUDES = $(INCLUDES)
LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_COPY_HEADERS_TO := libhellos
LOCAL_COPY_HEADERS := hellos.h
include $(BUILD_STATIC_LIBRARY)
#编译动态库
include $(CLEAR_VARS)
LOCAL_MODULE = libhellod
LOCAL_CFLAGS = $(L_CFLAGS)
LOCAL_SRC_FILES = hellod.c
LOCAL_C_INCLUDES = $(INCLUDES)
LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_COPY_HEADERS_TO := libhellod
LOCAL_COPY_HEADERS := hellod.h
include $(BUILD_SHARED_LIBRARY)
BUILD_TEST=true
ifeq ($(BUILD_TEST),true)
#使用静态库
include $(CLEAR_VARS)
LOCAL_MODULE := hellos
LOCAL_STATIC_LIBRARIES := libhellos
LOCAL_SHARED_LIBRARIES :=
LOCAL_LDLIBS += -ldl
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := mains.c
LOCAL_C_INCLUDES := $(INCLUDES)
include $(BUILD_EXECUTABLE)
#使用动态库
include $(CLEAR_VARS)
LOCAL_MODULE := hellod
LOCAL_MODULE_TAGS := debug
LOCAL_SHARED_LIBRARIES := libc libcutils libhellod
LOCAL_LDLIBS += -ldl
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := maind.c
LOCAL_C_INCLUDES := $(INCLUDES)
include $(BUILD_EXECUTABLE)
endif # ifeq ($(WPA_BUILD_SUPPLICANT),true)
########################
#local_target_dir := $(TARGET_OUT)/etc/wifi
#include $(CLEAR_VARS)
#LOCAL_MODULE := wpa_supplicant.conf
#LOCAL_MODULE_TAGS := user
#LOCAL_MODULE_CLASS := ETC
#LOCAL_MODULE_PATH := $(local_target_dir)
#LOCAL_SRC_FILES := $(LOCAL_MODULE)
#include $(BUILD_PREBUILT)
########################
系统变量解析
LOCAL_MODULE - 编译的目标对象
LOCAL_SRC_FILES - 编译的源文件
LOCAL_C_INCLUDES - 需要包含的头文件目录
LOCAL_SHARED_LIBRARIES - 链接时需要的外部库
LOCAL_PRELINK_MODULE - 是否需要prelink处理
BUILD_SHARED_LIBRARY - 指明要编译成动态库
LOCAL_PATH - 编译时的目录
$(call 目录,目录….) 目录引入操作符
如该目录下有个文件夹名称 src,则可以这样写 $(call src),那么就会得到 src 目录的完整路径
include $(CLEAR_VARS) -清除之前的一些系统变量
CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
在 build/core/config.mk 定义 CLEAR_VARS:=$(BUILD_SYSTEM)/clear_vars.mk
通过include 包含自定义的.mk文件(即是自定义编译规则)或是引用系统其他的.mk文件(系统定义的编译规则)。
LOCAL_SRC_FILES - 编译的源文件
可以是.c, .cpp, .java, .S(汇编文件)或是.aidl等格式
不同的文件用空格隔开。如果编译目录子目录,采用相对路径,如子目录/文件名。也可以通过$(call目录),指明编译某目录
下所有.c/.cpp/.java/.S/ .aidl文件.追加文件 LOCAL_SRC_FILES += 文件
LOCAL_C_INCLUDES - 需要包含的头文件目录
可以是系统定义路径,也可以是相对路径. 如该编译目录下有个include目录,写法是include/*.h
LOCAL_SHARED_LIBRARIES - 链接时需要的外部共享库
LOCAL_STATIC_LIBRA RIES - 链接时需要的外部外部静态
LOCAL_JAVA_LIBRARIES 加入jar包
LOCAL_MODULE - 编译的目标对象
module 是指系统的 native code,通常针对c,c++代码
./system/core/sh/Android.mk:32:LOCAL_MODULE:= sh
./system/core/libcutils/Android.mk:71:LOCAL_MODULE :=libcutils
./system/core/cpio/Android.mk:9:LOCAL_MODULE := mkbootfs
./system/core/mkbootimg/Android.mk:8:LOCAL_MODULE :=mkbootimg
./system/core/toolbox/Android.mk:61:LOCAL_MODULE:=toolbox
./system/core/logcat/Android.mk:10:LOCAL_MODULE:= logcat
./system/core/adb/Android.mk:65:LOCAL_MODULE := adb
./system/core/adb/Android.mk:125:LOCAL_MODULE := adbd
./system/core/init/Android.mk:20:LOCAL_MODULE:= init
./system/core/vold/Android.mk:24:LOCAL_MODULE:= vold
./system/core/mountd/Android.mk:13:LOCAL_MODULE:= mountd
LOCAL_PACKAGE_NAME
Java 应用程序的名字用该变量定义
./packages/apps/Music/Android.mk:9:LOCAL_PACKAGE_NAME :=Music
./packages/apps/Browser/Android.mk:14:LOCAL_PACKAGE_NAME :=Browser
./packages/apps/Settings/Android.mk:8:LOCAL_PACKAGE_NAME :=Settings
./packages/apps/Stk/Android.mk:10:LOCAL_PACKAGE_NAME :=Stk
./packages/apps/Contacts/Android.mk:10:LOCAL_PACKAGE_NAME :=Contacts
./packages/apps/Mms/Android.mk:8:LOCAL_PACKAGE_NAME :=Mms
./packages/apps/Camera/Android.mk:8:LOCAL_PACKAGE_NAME :=Camera
./packages/apps/Phone/Android.mk:11:LOCAL_PACKAGE_NAME :=Phone
./packages/apps/VoiceDialer/Android.mk:8:LOCAL_PACKAGE_NAME :=VoiceDialer
BUILD_SHARED_LIBRARY - 指明要编译成动态库。
编译的目标,用include 操作符
UILD_STATIC_LIBRARY来指明要编译成静态库。
如果是java文件的话,会用到系统的编译脚本host_java_library.mk,用BUILD_PACKAGE来指明。三个编译
-------------------
include $(BUILD_STATIC_LIBRARY)
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
-------------------
include $(BUILD_SHARED_LIBRARY)
./build/core/config.mk:50:BUILD_SHARED_LIBRARY:=$(BUILD_SYSTEM)/shared_library.mk
-------------------
include $(BUILD_HOST_SHARED_LIBRARY)
BUILD_HOST_SHARED_LIBRARY:=$(BUILD_SYSTEM)/host_shared_library.mk
-------------------
include $(BUILD_EXECUTABLE)
build/core/config.mk:51:BUILD_EXECUTABLE:=$(BUILD_SYSTEM)/executable.mk
-------------------
include $(BUILD_HOST_EXECUTABLE)
./build/core/config.mk:53:BUILD_HOST_EXECUTABLE:=$(BUILD_SYSTEM)/host_executable.mk
-------------------
BUILD_HOST_JAVA_LIBRARY:=$(BUILD_SYSTEM)/host_java_library.mk
-------------------
BUILD_JAVA_LIBRARY
./build/core/config.mk:58:BUILD_JAVA_LIBRARY:=$(BUILD_SYSTEM)/java_library.mk
------------------
BUILD_STATIC_JAVA_LIBRARY 编译静态JAVA库
./build/core/config.mk:59:BUILD_STATIC_JAVA_LIBRARY:=$(BUILD_SYSTEM)/static_java_library.mk
------------------
BUILD_HOST_JAVA_LIBRARY 编译本机用的JAVA库
./build/core/config.mk:60:BUILD_HOST_JAVA_LIBRARY:=$(BUILD_SYSTEM)/host_java_library.mk
------------------
BUILD_HOST_STATIC_LIBRARY:=$(BUILD_SYSTEM)/host_static_library.mk
BUILD_HOST_SHARED_LIBRARY:=$(BUILD_SYSTEM)/host_shared_library.mk
BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
BUILD_RAW_STATIC_LIBRARY :=$(BUILD_SYSTEM)/raw_static_library.mk
BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_executable.mk
BUILD_HOST_EXECUTABLE:=$(BUILD_SYSTEM)/host_executable.mk
BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
BUILD_STATIC_JAVA_LIBRARY:=$(BUILD_SYSTEM)/static_java_library.mk
BUILD_HOST_JAVA_LIBRARY:=$(BUILD_SYSTEM)/host_java_library.mk
BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
BUILD_KEY_CHAR_MAP := $(BUILD_SYSTEM)/key_char_map.mk
============
LOCAL_PRELINK_MODULE
Prelink利用事先链接代替运行时链接的方法来加速共享库的加载,它不仅可以加快起动速度,还可以减少部分内存开销,
是各种Linux架构上用于减少程序加载时间、缩短系统启动时间和加快应用程序启动的很受欢迎的一个工具。程序运行时的
动态链接尤其是重定位(relocation)的开销对于大型系统来说是很大的。
动态链接和加载的过程开销很大,并且在大多数的系统上, 函数库并不会常常被更动, 每次程序被执行时所进行的链接
动作都是完全相同的,对于嵌入式系统来说尤其如此。因此,这一过程可以改在运行时之前就可以预先处理好,即花一些时间
利用Prelink工具对动态共享库和可执行文件进行处理,修改这些二进制文件并加入相应的重定位等信息,节约了本来在程序
启动时的比较耗时的查询函数地址等工作,这样可以减少程序启动的时间,同时也减少了内存的耗用。
Prelink的这种做法当然也有代价:每次更新动态共享库时,相关的可执行文件都需要重新执行一遍Prelink才能保
证有效,因为新的共享库中的符号信息、地址等很可能与原来的已经不同了,这就是为什么 androidframework代码一改动,
这时候就会导致相关的应用程序重新被编译。
这种代价对于嵌入式系统的开发者来说可能稍微带来一些复杂度,不过好在对用户来说几乎是可以忽略的。
--------------------
变量设置为false那么将不做prelink操作
LOCAL_PRELINK_MODULE := false
默认是需要prlink的,同时需要在 build/core/prelink-linux-arm.map 中加入
libhellod.so 0x96000000
这个map文件好像是制定动态库的地址的,在前面注释上面有一些地址范围的信息,注意库与库之间的间隔数,
如果指定不好的话编译的时候会提示说地址空间冲突的问题。另外,注意排序,这里要把数大的放到前面去,
按照大小降序排序。
解析 LOCAL_PRELINK_MODULE 变量
build/core/dynamic_binary.mk:94:ifeq($(LOCAL_PRELINK_MODULE),true)
ifeq ($(LOCAL_PRELINK_MODULE),true)
$(prelink_output): $(prelink_input) $(TARGET_PRELINKER_MAP)$(APRIORI)
$(transform-to-prelinked)
transform-to-prelinked定义:
./build/core/definitions.mk:1002:definetransform-to-prelinked
define transform-to-prelinked
@mkdir -p $(dir $@)
@echo "target Prelink: $(PRIVATE_MODULE) ($@)"
$(hide) $(APRIORI) \
--prelinkmap $(TARGET_PRELINKER_MAP) \
--locals-only \
--quiet \
$< \
--output $@
endef
./build/core/config.mk:183:APRIORI :=$(HOST_OUT_EXECUTABLES)/apriori$(HOST_EXECUTABLE_SUFFIX)
prelink工具不是常用的prelink而是apriori,其源代码位于”<your_android>/build/tools/apriori”
参考文档:
动态库优化——Prelink(预连接)技术
http://www.eefocus.com/article/09-04/71629s.html
===============
LOCAL_ARM_MODE := arm
目前Android大部分都是基于Arm处理器的,Arm指令用两种模式Thumb(每条指令两个字节)和arm指令(每条指令四个字节)
LOCAL_CFLAGS += -O3 -fstrict-aliasing-fprefetch-loop-arrays
通过设定编译器操作,优化级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
LOCAL_CFLAGS += -W -Wall
LOCAL_CFLAGS += -fPIC -DPIC
LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall-Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE-DSH_HISTORY
LOCAL_CFLAGS += -DUSEOVERLAY2
根据条件选择相应的编译参数
ifeq ($(TARGET_ARCH),arm)
LOCAL_CFLAGS += -DANDROID_GADGET=1
LOCAL_CFLAGS := $(PV_CFLAGS)
endif
ifeq ($(TARGET_BUILD_TYPE),release)
LOCAL_CFLAGS += -O2
endif
LOCAL_LDLIBS := -lpthread
LOCAL_LDLIBS += -ldl
ifdef USE_MARVELL_MVED
LOCAL_WHOLE_STATIC_LIBRARIES += lib_il_mpeg4aspdecmved_wmmx2lnxlib_il_h264decmved_wmmx2lnx
LOCAL_SHARED_LIBRARIES += libMrvlMVED
else
LOCAL_WHOLE_STATIC_LIBRARIES += lib_il_h264dec_wmmx2lnxlib_il_mpeg4aspdec_wmmx2lnx
endif
============================================
添加Android.mk
完成了上一步,可以知道,Android.mk在编译中起着至关重要的作用,这其实就是Android编译环境中的makefile。为了完成我们的工作,需要在源代码中添加Android.mk。添加自己的Android.mk可以仿照SimpleJNI中的Android.mk,稍微修改即可。我们首先看看SimpleJNI目录下的两个Android.mk的内容:
- 根
目录下的Android.mk
TOP_LOCAL_PATH:= $(call my-dir)
# Build activity
LOCAL_PATH:= $(TOP_LOCAL_PATH)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := samples
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := SimpleJNI
LOCAL_JNI_SHARED_LIBRARIES := libsimplejni
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
#============================================================
# Also build all of the sub-targets under this one: the sharedlibrary.
include $(call all-makefiles-under,$(LOCAL_PATH))
对于包含LOCAL_PACKAGE_NAME的mk文件,该项默认为optinal,具体可以参看build/core/package.mk。SimpleJNI中定义为samples的具体作用我也不太清楚,为了保险起见,我自己的apk一般定义为optional。
TOP_LOCAL_PATH:= $(call my-dir)
# Build activity
LOCAL_PATH:= $(TOP_LOCAL_PATH)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := TestJniApp
LOCAL_JNI_SHARED_LIBRARIES := libtestjniapp
include $(BUILD_PACKAGE)
#============================================================
# Also build all of the sub-targets under this one: the sharedlibrary.
include $(call all-makefiles-under,$(LOCAL_PATH))
- Jni目录下的Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := samples
# This is the target being built.
LOCAL_MODULE:= libsimplejni
# All of the source files that we will compile.
LOCAL_SRC_FILES:= /
native.cpp
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES := /
libutils
# No static libraries.
LOCAL_STATIC_LIBRARIES :=
# Also need the JNI headers.
LOCAL_C_INCLUDES += /
$(JNI_H_INCLUDE)
# No special compiler flags.
LOCAL_CFLAGS +=
# Don't prelink this library.For more efficientcode, you may want
# to add this library to the prelink map and set this to true.However,
# it's difficult to do this for applications that are not suppliedas
# part of a system image.
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
LOCAL_PATH := $(callmy-dir)
include$(CLEAR_VARS)
LOCAL_MODULE
:=libtestjniapp LOCAL_SRC_FILES :=com_test_app_Jni.c
LOCAL_C_INCLUDES +=$(JNI_H_INCLUDE)
LOCAL_PRELINK_MODULE :=false
include$(BUILD_SHARED_LIBRARY)
4. 修改/bulid/target/product/generic.mk 把工程编译到系统中
PRODUCT_PACKAGES := /
AccountAndSyncSettings /
CarHome/
DeskClock/
……
SyncProvider/
TestJniApp