Android module paths (sharing code made easy):
==============================================
从r5开始,Android NDK带来一个非常酷的功能,使得你共享和重用其他人的modules更加简单。
I . Overviw:
------------------
这个功能背后的主要思想是:
-你可以在你的主要项目源代码树之外安装NDK modules。
-你可以简单的只用一条命令就可以将它们‘import’到你的项目中。
在实践中,它们是这样工作的:
1.你的环境变量NDK_MODULE_PATH将会包含你系统中的一系列搜索路径来查找modules。
这个变量应该由你来设置,还有复制其他modules到你列出的目录中。
2.为了导入一个module,最好将类似下面的一行放在Android.mk的尾部:
$(call import-module , <tag>)
这会在NDK_MODULE_PATH列出的任何目录下查找<tag>/Android.mk。
(这就是为什么它要放在尾部的原因,主要是为了避免搞乱‘my-dir’函数调用的结果。请查看Android.mk了解更多细节)。
3.声明你项目的modules所依赖的import module,可以通过在LOCAL_STATIC_LIBRARIES或LOCAL_SHARED_LIBRARIES中列出它们。例如:
LOCAL_STATIC_LIBRARIES += <tag>
4.重新编译!
记住NDK r5同样增加了:一个module“导出”声明到其他依赖于它的modules(例如,请查看Android.mk文档中LOCAL_EXPORT_CFLAGS的定义)。
一个编写良好的module会正确的导出它所依赖的所有东西。
现在是全部的细节:
I. NDK_MODULE_PATH:
NDK_MODULE_PATH变量必须包含一个目录列表。
* 由于GNU Make的限制,NDK_MODULE_PATH不能包含任何空白字符。如果不满足这个条件 的话,NDK会报错。
* 使用‘:’作为路径分隔符。
*在Windows上,使用‘/’作为目录分隔符。
NDK_MODULE_PATH指定的目录会被顺序的搜索。在搜索过程中首先会将找到的<path>/<tag>/Android.mk自动的包含进来。
作为便利,NDK编译系统会将$NDK/sources 追加到你的NDK_MODULE_PATH的定义中。这允许你非常轻松的将NDK自带的辅助库导入进来(请查看CPU-FEATURES文档中一个实际例子)。
II.编写import module:
------------------------------------
编写import module和编写project modules是类似的:
1.在你的NDK_MODULE_PATH目录下创建一个子目录。
例如,如果你的NDK_MODULE_PATH定义为/home/user/ndk-modules,那么就创建目录/home/user/ndk-modules/my-module
2、把Android.mk文件放在源代码所在位置。
就像你要查找project module那样,这里的这些文件正常会转到$PROJECT/Android.mk。在上面的例子中,会转到 /home/user/ndk-modules/my-module/Android.mk
注意:这里的Application.mk将会被忽略。
3、任何依赖你新module的module,必须调用import-module函数来导入。例如:
$(callimport-module,my-first-module)
import modules可以导入其他的modules,但是循环依赖是不允许的也会被检测到。依赖是可以传递的,编译系统会计算你编译需要的所有事情。
NDK编译系统不会放置对象文件或可执行文件在你的importmodule目录中(它们会被放置在项目的编译目录下,如$PROJECT/obj/)。
但是你能够将预编译库distribute在你的import module中,通过使用新的PREBUILT_STATIC_LIBRARIES 或 PREBUILT_SHARED_LIBRARIES的功能(请查看Android.mk文档)。
这使得能够非常简单的打包和redistribute你的import module目录到第三方库中。
III.为import module命名:
--------------------------------------
理解import module命名相关的东西是非常重要的:
- ‘import-module’实际上所做的就是通过使用NDK_MODULE_PATH提供的列表查找Android.mk文件,然后包含进来。
你导入的Android.mk可以定义任何数量的modules,使用任意的名字。像在下面的这行中,在<name>之间它们是没有直接关系:
$(callimport-module,<tag>/<name>)
modules的名字定义在<tag>/<name>/Android.mk中。
在有疑问的情况下,尽量保持简单!
如果你只想提供一个importmodule,把它命名成像base import directory。
另一方面,你可能想提供一个静态的或共享版本的module:在Android.mk的顶级目录下使用唯一的名字。考虑下面的编译脚本:
$NDK_MODULE_PATH/foo/bar/Android.mk:
LOCAL_PATH := $(call my-dir)
# Static version of the library is named 'bar_static'
include $(CLEAR_VARS)
LOCAL_MODULE :=bar_static
LOCAL_SRC_FILES := bar.c
# Ensure our dependees can include <bar.h> too LOCAL_EXPORT_C_INCLUDES :=$(LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)
# Shared version of the library is named 'bar_shared' LOCAL_MODULE := bar_shared
LOCAL_SRC_FILES := bar.c
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
include $(BUILD_SHARED_LIBRARY)
另一个module需要引用它,可以通过下面的方式:
1、导入‘foo/bar’,例如:
$(callimport-module,foo/bar)
2、使用静态库:
。。。
LOCAL_STATIC_LIBRARIES := bar_static
3、或者使用动态库:
。。。
LOCAL_SHARED_LIBRARIES := bar_shared
- The module namespace is flat,所以你的modules的名字不要相同。注意你可以使用LOCAL_MODULE_FILENAME来给你的module的二进制文件命名,可以和它的LOCAL_MODULE独立开来(请查看Android.mk文档中的定义和使用)。例如:
include $(CLEAR_VARS)
LOCAL_MODULE := super_foo
LOCAL_MODULE_FILENAME := foo # will give libfoo.so LOCAL_SRC_FILES := foo-src.c
LOCAL_CFLAGS := -DVOLUME=11
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := normal_foo
LOCAL_MODULE_FILENAME := foo # will also give libfoo.so LOCAL_SRC_FILES := foo-src.c
include $(BUILD_SHARED_LIBRARY)
定义了两个modules,分别叫“super_foo”和“normal_foo”,它们都可以生成叫着“libfoo.so”的动态库。
但是只能在你的项目中使用它们其中的一个,否则在编译的时候会发生冲突。这允许你在NDK编译脚本中选择普通的或优化的版本,而在你Java源代码中加载的方式都是一样的:
static {
System.loadLibrary("foo");
}
IV. 提示和建议:
----------------------
* 在你引用一个module之前,没有必要导入它!
* 在Android.mk的尾部使用import-module,避免搞乱‘my-dir’调用的结果。请查看Android.mk里面关于这个函数的描述来理解是为什么。
*强烈建议使用子目录来作为你的importtags,就像原先描述的那样,例如:
$(callimport-module,gtk/glib)
或者像这样:
$(callimport-module,com.example/awesomelib)
重要:‘android’的import 目录,还有它的任何子目录是为NDK使用预留的。你可以根据你想要的自由的组织你的其他import modules。