前一阵子攒了点工程小经验,忙的有点乱,梳理一下。
随着Android Studio的兴起跟Eclipse的没落(仅限于Android开发用途吧),越来越多人倾向于使用Android Studio搞Android开发,我在之前的时候也是这样。但是现在知道了,用NDK开发包通过ndk-build命令编译库更方便(NDK包 + Cygwin)。
进入正题,就是这么直接。
一、安装配置Cygwin开发环境
作为一个程序猿,基本搜索的能力还是要有的,自己去官网下载Cygwin就好了。安装的时候下载源找个顺眼的(哈哈哈)就阔以了,然后最关键的是在下载环境包的时候,有两个选择:
1、勾选安装Devel(点击列表中Devel,将后面的Default改为Install),这是为了下载需要的NDK开发需要的环境包。
2、或者一个个勾选下载autoconf2.1、automake1.10、binutils、gcc-core、gcc- g++、gcc4-core、gcc4-g++、gdb、pcre、pcre-devel、gawk、make共12个包(注:版本号也许会不太一样)。
接下来就next下载安装吧。
二、下载NDK
之前项目组编译32位Android库用的NDK r8e版本(此版本只支持32位库编译),编译64位Android库用的是NDK r10版本(此版本理论上支持编译32位个64位库,但是在实际中有些小问题,不知道是项目组内流传的NDK r10包有问题还是NDK r10版本本身就有问题)。我后来选了NDK r10c版本,32位、64位库都编译木有问题,用着还不错。
假如我推荐的话,就下载NDK r10c吧。这个直接解压到想要的位置就好了,不需要配置什么。如下图,我下载的该版本的自解压文件。
三、关联
首先保证运行一次Cygwin(保证Cygwin生成必要的文件)。然后找到Cygwin安装目录,进入home文件夹。
你会看到有个以你电脑用户名命名的文件夹,进入这个文件夹。以文本形式打开.bash_profile文件,在其末尾添加Shell环境变量:
NDK10C=/cygdrive/D/Usual/android-ndk-r10c
export NDK10C
其中“D/Usual/android-ndk-r10c”为我NDK的目录。
设置Cygwin的shell变量的意义就在于用一个变量就代表了后面的一大串路径,开发起来方便快捷。
四、使用
使用起来就是:
1、将NDK编译用到的Android.mk、Application.mk以及相关的jni层源文件、头文件放在一个文件夹里,这里我的命名为jni文件夹。
2、打开Cygwin, cd到jni文件夹, 然后用NDK开发包的ndk-build命令就可以开始编译Android库了。 Cygwin如何知道ndk-build这个命令在哪里呢?前面我们设置的shell变量就起作用了,以我的配置为例子,在jni目录下执行:
$NDK10C/ndk-build
这样就可以执行编译了。对于“$NDK10C/ndk-build”中的“$”,它是跟“NDK10C”搭配使用的,自己可以搜搜"shell 变量"。
五、关于Android.mk、Application.mk文件书写
1、基础内容:好的社区好的开发文档很重要,谷歌在这方面那必须好。去Android开发官网去找NDK开发相关内容,很丰富的入门文档。
2、进阶:我看了好多中文博客,参差不齐吧,感觉还是Github开源代码能学到不少相关的知识,所以也推荐多用用Github。
接下来,以一个简单例子讲解一下如何编写make文件来让NDK编译更自动化,先放一下文件。
Android.mk文件:
#file: Android.mk
LOCAL_PATH := $(call my-dir)
my_LOCAL_PATH:=$(LOCAL_PATH)
#设置编译项 base_a base_b base_c final
to_COMPILED:=base_a
#编译base_a库
ifeq ($(to_COMPILED),base_a)
include $(my_LOCAL_PATH)/../../base_a/base_a.mk
endif
#编译base_b库
ifeq ($(to_COMPILED),base_b)
include $(my_LOCAL_PATH)/../../base_b/base_b.mk
endif
#编译base_c库
ifeq ($(to_COMPILED),base_c)
include $(my_LOCAL_PATH)/../../base_c/base_c.mk
endif
#编译final库
ifeq ($(to_COMPILED),final)
include $(my_LOCAL_PATH)/../../final/final.mk
include $(CLEAR_VARS)
LOCAL_MODULE:=base_a
LOCAL_SRC_FILES:=$(my_LOCAL_PATH)/../MyLibs/$(TARGET_ARCH_ABI)/libbase_a.a
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(my_LOCAL_PATH)
include $(CLEAR_VARS)
LOCAL_MODULE := finalAndroid
FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp) \
$(wildcard $(LOCAL_PATH)/../../common/test.cpp)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_LDLIBS := -llog
LOCAL_STATIC_LIBRARIES := base_a final
include $(BUILD_SHARED_LIBRARY)
endif
Application.mk文件:
#file: Application.mk
APP_PLATFORM := android-9
APP_STL := stlport_static
#编译平台armeabi arm64-v8a
APP_ABI := armeabi
base_a.mk文件:
#file: base_a.mk
USER_LOCAL_PATH:=$(LOCAL_PATH)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := base_a
FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp) \
$(wildcard $(LOCAL_PATH)/../common/*.cpp)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_LDLIBS := -llog
LOCAL_PATH := $(USER_LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)
base_b.mk文件:
#file: base_b.mk
USER_LOCAL_PATH:=$(LOCAL_PATH)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := base_a
LOCAL_SRC_FILES := $(LOCAL_PATH)/../Android/MyLibs/$(TARGET_ARCH_ABI)/libbase_a.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := base_b
FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp) \
$(wildcard $(LOCAL_PATH)/../common/*.cpp)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_LDLIBS := -llog
LOCAL_STATIC_LIBRARIES := base_a
LOCAL_PATH := $(USER_LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)
base_c.mk文件
#file: base_c.mk
USER_LOCAL_PATH:=$(LOCAL_PATH)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := base_d
LOCAL_SRC_FILES := $(LOCAL_PATH)/../Android/MyLibs/$(TARGET_ARCH_ABI)/libbase_d.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := base_b
LOCAL_SRC_FILES := $(LOCAL_PATH)/../Android/MyLibs/$(TARGET_ARCH_ABI)/libbase_b.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := base_c
FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_LDLIBS := -llog
LOCAL_STATIC_LIBRARIES := base_d base_b
LOCAL_PATH := $(USER_LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)
我在make文件里面也做了好多注释。
我现在挑一些关键部分解释下:
1、我在Android.mk文件中设置可以依次生成几个库,并且通过一个to_COMPILED变量来保存当前要编译的库。然后接下来通过判断to_COMPILED字段的值,然后执行不同的代码。 这样,我每次编译不同的库只需要改一处地方。
#设置编译项 base_a base_b base_c final
to_COMPILED:=base_a
ifeq ($(to_COMPILED),base_b)
include $(my_LOCAL_PATH)/../../base_b/base_b.mk
endif
2、大家应该注意到了,在每个子make文件我都是类似这样写的:
USER_LOCAL_PATH:=$(LOCAL_PATH)
LOCAL_PATH := $(call my-dir)
#中间略去细节
LOCAL_PATH := $(USER_LOCAL_PATH)
include $(BUILD_STATIC_LIBRARY)
这是因为,每进入一个子make文件,随着LOCAL_PATH := $(call my-dir)这句话的使用,LOCAL_PATH的值被更新为当前子make文件的路径,假如不做处理,当执行完子make文件返回到Android.mk文件中的时候,LOCAL_PATH保存的还是刚刚执行过的子make文件的路径。 我用的处理方式就是,对于子make文件,用一个变量保存调用者LOCAL_PATH的值,当执行到子make文件的末尾的时候,我再将保存的调用者LOCAL_PATH的值赋给当前LOCAL_PATH,从而程序执行完子make文件回到调用者make文件的时候,LOCAL_PATH的值未发生改变。
3、这里说一下(哈哈哈),旧版本NDK(反正NDK r8e不可以,NDK10可以)的话Application.mk文件可不敢这么写。
4、别的也没啥了,官方文档都有。暂时写这点内容。
来源:oschina
链接:https://my.oschina.net/u/4383937/blog/4328565