Android JNI之执杖生涯 .a -> .so(mk
缘由
曾梦想仗剑走天涯,看一看世界的繁华。岂料一入 JNI 深似海,从此女神是路人。生成so文件,是每个 JNI 人的终极梦想。前段日子,公司人少,扔过来的是cpp实现文件,呵呵,以为要往c渣渣方向发展,吓得我赶紧拿起那本红皮书,c++从入门到放弃。几个月过去,书还没人捧热,cpp不见了,取而代之是.a文件,什么玩意儿?掏出度娘,怒刷binggo。一手Shadowsocks,谷狗汪汪汪。静态库,简单来说,编译器认识,而linux不认识。故事,从这里开始。
.a 怎么用
想用就用,用得响亮。我就静静的,静静的看着。一巴掌一个脚印,啪啪啪,痛吗?不痛,轻轻松松三步走。
啪. 定位
下药要对口,位置不能随心。来来来,jni (JNI Floder)目录下。又回到最初的起点,记忆中你cpp文件的位置(打个广告,参考我前边写的关于JNI文章)。对,没错,又是jni目录。进阶的.a,还是老地方,熟悉的味道,不一样的配方。然,no pic no bb。呐~*@……#!#!神圣召唤术 --- 图图图。
JNI目录图
上边的sources目录是瞎写的,归个类。这里有东西解释下,比如说 arm64-v8a之类的, 这个是指cpu类型。目前而言,armeabi 这个是基类,所有手机都支持的类型。芸芸众手机,天涯cpu类型what?no,no,nobody I want nobody nobody But You,一个字:懒。so armeabi,你值得拥有。x86/x86-64这个一般是电脑cpu类型,模拟器用得上。如果能够每个类型都build出来,自然是好的,毕竟每个类型cpu都有各自的优化。
啪啪. 链接
重点来了。使用.a的过程,就是链接。当然不仅仅是,整个打包成so过程,都是链接过程,这里主讲如何链接.a。
mk,从 jni 诞生那一刻起,随之诞生。这里暂且先讲mk方式实现链接,其它暂且不提。悠久的历史,脚本性质的配置,承载着老一辈人的远方与诗。左手,右手,一个慢动作。一步两步三四步,带你走个过场。记住了,固然可喜,若是没有,就再看一遍吧。
mk版结构图
# Android.mk文件,稍作修改,即可食用
LOCAL_PATH := $(call my-dir)
//链接.a块
include $(CLEAR_VARS)
LOCAL_MODULE := libHello
LOCAL_C_INCLUDES := $(LOCAL_PATH)/sources
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/sources
LOCAL_SRC_FILES := sources/$(TARGET_ARCH_ABI)/libHello.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := -std=c++14
LDFLAGS := -shared
LOCAL_LDFLAGS += -fuse-ld=bfd
LOCAL_MODULE := JniDemo
LOCAL_SRC_FILES := jni_dynamic.cpp
LOCAL_STATIC_LIBRARIES := libHello
include $(BUILD_SHARED_LIBRARY)
# Application.mk 这个基本不需要改
APP_STL := gnustl_static//使用 STL 静态库
NDK_TOOLCHAIN_VERSION := 4.9//链接器tool版本
/**
*fexceptions 允许异常功能
*frtti 运行时类型识别
*fpermissive 此项有效时表示宽松的编译形式,比如没有用到的代码中有错误也可以通过
* std=c++14 允许使用c++14的函数等功能
*/
APP_CPPFLAGS := -fexceptions -frtti -fpermissive -std=c++14
APP_ABI := armeabi armeabi-v7a arm64-v8a mips mips64
APP_PLATFORM := android-14
APP_OPTIM := debug
这2个固定搭配,没什么需要特别解释的地方,照搬砖头即可。如果你砸到了自己的脚,火速120,你来找我也没用。mk文件已经就位,当然还需要build.gradle的支持。
android{
....
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
commandLine "E:/adt-bundle-windows-x86_64-20140321/android-ndk-r10/ndk-build.cmd",//ndk.build位置
'NDK_PROJECT_PATH=build/intermediates/ndk',//ndk位置
'NDK_LIBS_OUT=src/main/jniLibs',//输出目录
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
}
这段,不解释呢,说不过去。解释呢,一句话,自定义ndkbuild命令(生成so的利器)。到这里本想着已经告一段路了,是时候见证奇迹的时刻了。然而还不料,一个error让我沉醉,毫无保留,没有余力悲伤:
D:\Study_Code\JniDemo\app\src\main\jni\sources\hello.h
Error:(1, 18) string: No such file or directory
Error:Execution failed for task ':app:compileDebugNdk'.
> com.android.ide.common.process.ProcessException: Error while executing 'E:\adt-bundle-windows-x86_64-20140321\android-ndk-r10\ndk-build.cmd' with arguments {NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=D:\Study_Code\JniDemo\app\build\intermediates\ndk\debug\Android.mk APP_PLATFORM=android-25 NDK_OUT=D:\Study_Code\JniDemo\app\build\intermediates\ndk\debug\obj NDK_LIBS_OUT=D:\Study_Code\JniDemo\app\build\intermediates\ndk\debug\lib APP_ABI=all}
显然,c++中string找不到。哟哟哟,切克闹。bug,bug,bug,惊起一滩苦水。既然开启了自定义,那么就需要做点限制,比如说禁用自动执行ndk-build。如果自动执行ndk-build的话,那么我的Application.mk配置将失效,默认是c环境,缘来如此。
android{
...
sourceSets {
main {
jni.srcDirs = []//禁用自动执行ndk-build
}
}
}
.mk 这艘小船,到这儿,已经上岸了。
啪啪啪.见证奇迹的时刻
上边的已然做到,那么这一刻,属于你的奇迹终究还是到来了。biu,biu,build。接下来,或许更精彩。
附送一张机票,带你领略世界的浩瀚