这种生成方法需要NDK,两个makefile文件Android.mk、Application.mk,make工具已经集成在NDK中,不需要额外安装。这种方法有两种实现方式:单独使用NDK和使用IDE,两种方式本质相同。由于Android基于Linux内核的,Android源码也需要在Linux环境下编译,单独使用NDK时,我们主要介绍在Linux环境下交叉编译Android的本地库(Linux和Android都是操作系统是并列概念);而借助AS IDE编译时,我们主要介绍在windows环境下,使用AS IDE编译android本地库。
1 单独使用NDK
(1)Linux下配置NDK:
首先根据需要下载某个版本的NDK,下载链接为NDK 下载 | Android NDK | Android Developers,我这里用的是android-ndk-r17c,在自己的用户目录下,找到.bashrc文件,在最后一行加上下面两句:
NDKROOT="/xxx/xxx/..../android-ndk-r17c"
export PATH=$NDKROOT:$PATH
其中 "/xxx/xxx/..../android-ndk-r17c" 是你下载的NDK的存放路径,android-ndk-r17c是我当时使用的NDK版本,可以替换。保存更改,在当前目录下执行source .bashrc(注:source命令通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录),到此配置完成。在terminal下执行ndk-build会有如下提示,证明配置成功。
(2)创建JNI文件夹:
在任意目录下创建名字为jni的目录,需要注意的是目录名必须为jni,区分大小写。该目录用于存放Android.mk、Application.mk文件,以及头文件和源文件。建议在jni目录下新建include和source文件夹,分别将头文件和源文件放入其中,这样比较清晰。
(3)编写Android.mk和Application.mk:
这两个文件是NDK中集成的makefile编译框架的两个小片段,编译源码需要很多makefile文件,NDK已经帮我们写好了很多编译必须的makefile文件,这些makefile文件相互之间有执行顺序和调用关系,形成了由makefile文件组成的编译框架,Android.mk和Application.mk是NDK为用户暴露出来的可供用户自定内容的makefile文件。当NDK开始编译时会先解析内置的makefile文件,通过makefile的包含关系找到Android.mk和Application.mk并解析。两个文件的基本写法如下:
Android.mk:
#通过$(call 宏名)调用my-dir函数宏,将mk文件所在路径赋给LOCAL_PATH变量。
LOCAL_PATH := $(call my-dir)
#清除除了LOCAL_PATH外的所有LOCAL开头变量值,CLEAR_VARS是一个预定义变量,指向一个makefile文件。
include $(CLEAR_VARS)
#指定产物名字,后期会加上lib前缀和.so扩展名。
LOCAL_MODULE:= ymath
#自定义一个变量,用于存储所有头文件的路径,用“+=”追加
MY_H_LIST := $(LOCAL_PATH)/xx1/include
MY_H_LIST += $(LOCAL_PATH)/xx2/include
#将头文件目录路径列表赋给LOCAL_C_INCLUDES变量。
LOCAL_C_INCLUDES := $(MY_H_LIST:$(LOCAL_PATH)/%=%)
#自定义一个变量,指定所有源文件的路径,用“+=”追加,用wildcard通配符选取路径下所有源文件
MY_CPP_LIST := $(wildcard $(LOCAL_PATH)/xx1/source/*.cpp)
MY_CPP_LIST += $(wildcard $(LOCAL_PATH)/xx2/source/*.cpp)
#将源文件路径列表赋给LOCAL_C_INCLUDES变量。
LOCAL_SRC_FILES := $(MY_CPP_LIST:$(LOCAL_PATH)/%=%)
#引用系统模块liblog.so与libm.so,还可以引入其他的格式 l+模块名
LOCAL_LDLIBS := -llog -lm
#通过引入不同脚本决定编译成什么产物:BUILD_SHARED_LIBRARY为动态库、BUILD_STATIC_LIBRARY为静态库、BUILD_EXECUTABLE为可执行文件
include $(BUILD_SHARED_LIBRARY)
Application.mk:
#声明库支持的最低API级别,对应于android的minSdkVersion
APP_PLATFORM := android-21
#指定API平台,可选值有:armeabi-v7a、arm64-v8a、x86、x86_64、all,若指定多个平台,用空格分开
APP_ABI:= armeabi-v7a arm64-v8a x86_64 x86
#编译调试版本还是发布版本,发布版本会优化且信息少。可选值release 或 debug
APP_OPTIM := release
#声明C++ 标准库。c++_static是libc++标准库的静态库,c++_shared是libc++标准库的动态库,当要编译动态库时
#c++_shared,编译静态库时用c++_static。
APP_STL := c++_static
#指定为clang编译器
NDK_TOOLCHAIN_VERSION := clang
Android.mk和Application.mk文件详解见todo。
(4)进入jni目录执行 ndk-build命令:
在命令行进入jni目录,直接执行ndk-build命令即可。(注:ndk-build是一个shell脚本,执行ndk-build之后,会先找到NDK中的make工具,然后解析build-local.mk文件,相当于NDK的makefile编译框架的入口文件,进而解析所有makefile文件。)
2 使用Android studio IDE
(1)新建项目和Module
在AS菜单栏选择 File -> new -> new project -> Empty Activity(这里无需选择Native C++),项目建好后,再新建一个module,将本地库的编译放在module中完成,不影响app module的逻辑,这步并不是必须的。
(2)配置NDK
在AS的SDK Manager中下载NDK,找到想要的版本并安装
安装好后,进入Project Structure,配置NDK:
(3)构建 jni 目录
在module下的src -> main路径下,建立名为 '' jni '' 的目录(名字必须是jni),将需要编译的C++头/源文件放到 jni 目录下,有时为了结构清晰,可根据需要在jni下新建 prebuilt、src、include三个目录,src中存放需要编译的C++源文件、include中存放对应的头文件、prebuilt中存放本次编译需要依赖的库及对应的头文件;并在jni下新建 Android.mk及Application.mk文件,文件目录可如下分配:
(4)编写Android.mk及Application.mk文件
编写方式及语法与单独使用NDK的情况一样,Android.mk文件内容如下:
LOCAL_PATH := $(call my-dir)
#自定义变量
LOCAL_CUSTOM_PREBUILT_DIR := prebuilt
include $(CLEAR_VARS)
#本地库名字
LOCAL_MODULE:= HelloWorld
#需要的系统库
LOCAL_LDLIBS := -lz -lstdc++ -llog -lm
#需要的头文件
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
$(LOCAL_PATH)/$(LOCAL_CUSTOM_PREBUILT_DIR)/include
#源文件
LOCAL_SRC_FILES := $(LOCAL_PATH)/src/Sourcea.cpp
LOCAL_SRC_FILES += $(LOCAL_PATH)/src/Sourceb.cpp
#编译器配置
LOCAL_CPPFLAGS += -fexceptions
LOCAL_CFLAGS += -std=c++11
#指定要生成so库
include $(BUILD_SHARED_LIBRARY)
Application.mk文件如下:
APP_PLATFORM := android-21
APP_ABI:= armeabi-v7a arm64-v8a
APP_OPTIM := release
APP_STL := c++_static
NDK_TOOLCHAIN_VERSION := clang
内容与上一节 单独使用NDK编译稍有区别,主要多展示了makefile的更多写法,后面会详细介绍makefile的具体语法,这不必太过纠结,只要记住用AS编译本地库的流程即可。
(5)配置build.gradle
在module级别的build.gradle文件中添加
android {
.......
//指定生成并使用那些平台的so库,使用build生成本地库,会覆盖Android.mk的TARGET_ARCH_ABI及
//Application.mk中的APP_ABI变量值
ndk {
abiFilters "armeabi-v7a","arm64-v8a"
}
......
externalNativeBuild {
ndkBuild {
//Android.mk文件的路径
path file('src/main/jni/Android.mk')
}
}
......
}
(6)编译及产物
点击AS的build按钮,,就会生成本地库,在module -> build -> intermediates -> ndkBuild 路径下即可找到本地库。