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_LIBRARIESLOCAL_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、任何依赖你新modulemodule,必须调用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