Android.mk解析

一 简介

Android NDK的构建系统是基于GUN Makefile的,Android.mk其实就是一个用于向Android NDK构建系统描述NDK项目的GUN Makefile片段。
在编写涉及JNI的项目时,往往需要自己配置Android.mk:主要通过makefile语法,使用变量或语句对用到的工具链、要编译生成的库名称、生成库所依赖的源文件、生成动态库或静态库等信息进行描述...
下面从变量开始认识Android.mk

二 Android.mk中的常用变量

1、LOCAL_PATH
LOCAL_PATH :=$(call my-dir)
Android构建系统利用LOCAL_PATH来定位源文件,Android构建系统要求,Android.mk文档必须以该变量的定义开头。
my-dir是宏功能,这里将该变量设置为my-dir的返回值,即设置为当前目录
my-dir就是该Android.mk所在目录,一般就是我们的jni目录

2、Include &(CLEAR_VARS)
Android构建系统在单次执行中解析多个构建文件,
LOCAL_是全局变量,这条语句在每个模块开头使用,可以帮助清除 除了LOCAL_PATH以外的其他LOCAL_变量,避免模块间变量冲突,
例如可以清除如下文件:LOCAL_MODULELOCAL_SRC_FILES

3、LOCAL_MODULE
LOCAL_MODULE变量,简单理解就是给生成的so库文件设置的名称,如:
LOCAL_MODULE : = hello-jni,表示生成名为hello-jni的模块,
因为模块名称也被用于给构建过程生成的文件命名,
因此,构建系统加上适当的前缀,最终生成libhello-jni.so

4、LOCAL_SRC_FILES
该变量定义了生成库文件的源文件列表:
LOCAL_SRC_FILES := hello-jni.cpp
这里hello-jni只由一个源文件生成,包含多个源文件,注意用空格分开:
LOCAL_SRC_FILES := hello-jni.cpp hello-ndk.cpp

5、LOCAL_C_INCLUDES:
表示头文件的搜索路径,用来指定在编译时即将使用的头文件的位置,
一般在使用预编译库构建so时使用,用于包含第三方源文件对应的头文件
例:

LOCAL_C_INCLUDES := $(LOCAL_PATH) \
            $(ClientCommon_Path)/basecore/a2/framecore/include \
            $(ClientCommon_Path)/include/android/client \
            $(ClientCommon_Path)/basecore/a2/appshare/include/ \
            $(ClientCommon_Path)/basecore/a2/avcore/include/avcore \
            $(ClientCommon_Path)/include/android/meetingcore \
            $(ClientCommon_Path)/include/android/uithreadhandler \
            $(ClientCommon_Path)/include/android/MultiWhiteBoard \
            $(ClientCommon_Path)/include/android/terminalonlinemanager \
            $(ClientCommon_Path)/protocol 

6、LOCAL_LDLIBS:
用于告诉链接器,生成的模块要在加载时链接到的库,表示编译模块时要使用的附加的链接器选项。
使用‘-l’前缀传递指定库的名字
例:
LOCAL_LDLIBS := -lz表示告诉链接器生成的模块要在加载时链接到
/system/lib/libz.so/system/lib/libz.a(可链接到动态库或静态库)

例:
要与NDK日志库链接:
LOCAL_LDLIBS:= -llog

连接到多个静态库或动态库(用空格隔开):
LOCAL_LDLIBS:= -ltinyxml32 -luithreadhandler -lxmlprotocolparser32

7、 自定义新的变量
MyLibrary_Path := $(LOCAL_PATH)/../../library
( ../ 表示上一级目录)
注意:以LOCAL和NDK前缀开头的名称预留给Android NDK构建系统使用,自定义变量应避免使用这两个前缀

三 构建动态库(xxx.so)

为了建立可供主应用程序使用的模块,必须将该模块变成动态库,只有动态库才能被安装/复制到APK包中。
构建动态库,在Android.mk中相应模块下添加如下描述语句即可:

include &(BUILD_SHARED_LIBRARY)

比如在一个工程中定义两个动态库:

LOCAL_PATH := &(call my-dir)

//模块1:
include &(CLEAR_VARS)

LOCAL_MODULE := module1
LOCAL_SRC_FILES := moudel1.cpp
include &(BUILD_SHARED_LIBRARY)
----------------------------------------------------
//模块2:
include &(CLEAR_VARS)

LOCAL_MODULE := module2
LOCAL_SRC_FILES := moudel2.cpp
include &(BUILD_SHARED_LIBRARY)

定义完成后,NDK构建系统将会产生两个动态库:moudel1.somoudel2.so

四 构建静态库(xxx.a)

构建静态库,在Android.mk相应模块添加描述语句:
include $(BUILD_STATIC_LIBRARY) (生成静态库)

Android应用程序不直接使用静态库,且apk中也不包含静态库,但静态库可以用来构建动态库
例如:
开放某个核心库的功能给他人使用时,无须将核心源代码提供给客户,而是将这些代码编译成静态库,用户只需并入他们的动态库就可使用了

//编译.a静态库
LOCAL_PATH := &(call my-dir)
include &(CLEAR_VARS)

LOCAL_MODULE := avilib
LOCAL_SRC_FILES := avilib.cpp  platform.cpp
include &(BUILD_STATIC_LIBRARY)   //编译生成静态库

第三方使用这个静态库构建自己的动态库在应用程序中使用:
用库名称为变量LOCAL_STATIC_LIBRARY赋值,即可引用静态库:
LOCAL_STATIC_LIBRARY:静态库名称

//原生模块
LOCAL_PATH := &(call my-dir)
include &(CLEAR_VARS)

LOCAL_MODULE := module2
LOCAL_SRC_FILES := module2.cpp
LOCAL_STATIC_LIBRARIES := avilib   //引用编译好的.a静态库
include &(BUILD_SHARED_LIBRARY)
五 用动态库构建通用模块

承接上一个静态库的示例代码,多个动态库与同一个静态库相连时,会产生重复连接,增加了apk大小
所以,当有多个动态库都使用某个库时,一般将该模块建立为动态库,避免重复连接。

LOCAL_PATH := &(call my-dir)

//编译动态库
include &(CLEAR_VARS)

LOCAL_MODULE := avilib
LOCAL_SRC_FILES := avilib.cpp  platform.cpp
include &(BUILD_SHARED_LIBRARY)     //多个moduel使用,构建成动态库

多个模块引用同一个动态库:
使用变量LOCAL_SHARED_LIBRARIES引用动态库:
LOCAL_SHARED_LIBRARIES :动态库名称

//原生模块1
include &(CLEAR_VARS)

LOCAL_MODULE := module1
LOCAL_SRC_FILES := module1.cpp
LOCAL_SHARED_LIBRARIES := avilib    //引用1
include &(BUILD_SHARED_LIBRARY)
----------------------------------------------------
//原生模块2
include &(CLEAR_VARS)

LOCAL_MODULE := module2
LOCAL_SRC_FILES := module2.cpp
LOCAL_SHARED_LIBRARIES := avilib    //引用2
include &(BUILD_SHARED_LIBRARY)
六 使用Rebuild库完成预构建

Prebuid:预构建
NDK R5以后包含Rebuid库,在Rebuild库的支持下,可以依赖编译好的so生成动态库

1、声明预编译库的模块
Android.mk可以写成如下形式:

LOCAL_PATH := &(call my-dir)
COMMON_LIBS_PATH := ../../ClientCommon

// 预构建库
include &(CLEAR_VARS)
LOCAL_MODULE := avnet       //生成的预编译库名称(名称可不与依赖库相同)
LOCAL_SRC_FILES := $(COMMON_LIBS_PATH)/basecore/libavnet.so
include &(PREBUILT_SHARED_LIBRARY)

说明:
1)LOCAL_SRC_FILES变量不再指向源文件,而是直接指向so库文件
2)预构建库的声明语句:
include &(PREBUILT_SHARED_LIBRARY)生成预构建动态库
include &(PREBUILT_STATIC_LIBRARY)生成预构建静态库

2、在其他模块引用这个预编译库:
与引用普通动态库的方式相同
预编译静态库的名字赋值给变量LOCAL_STATIC_LIBRARIES
预编译动态库的名字赋值给变量LOCAL_SHARED_LIBRARIES

3、将预编译库的头文件导出:LOCAL_C_INCLUDES
得到预编译库后,一般需要提供它对应的头文件,
因为依赖库生成预编译库时,需要将它的头文件提供给NDK构建系统
例:
依赖libavnet.so生成预编译库时,有对应的avnet.h头文件在include子目录,
那么在Android.mk中需要使用LOCAL_C_INCLUDES变量指定该头文件或直接指定该目录:
LOCAL_C_INCLUDES := $(LOCAL_PATH) /include

4、优点
利用Prebuild预编译,依赖so库生成最终的so库
1)可不发布源代码,只将so库发布给第三方开发人员
2)直接指向so库文件,称之为预构建,可以加速构建过程

七 条件操作

针对不同的平台,可能使用不同的代码,使用条件操作可进行区分处理
例:

ifeq (&(TARGET_ARCH),arm)
LOCAL_SRC_FILES += armonly.c
else
LOCAL_SRC_FILES += generic.c
endif

你可能感兴趣的:(Android.mk解析)