博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android NDK 如何缩减库的大小
阅读量:5173 次
发布时间:2019-06-13

本文共 4468 字,大约阅读时间需要 14 分钟。

当我们刚开始做Algolia的android开发时,二进制文件的大小并不是我主要关注的。事实上我们一开始用的是java,后来出于性能的压迫下才换成了C/C++

后来要在AVelov(一个android应用)中集成我们的库时,才发现这货太大了:850KB,而AVelov整个app才638k!这就意味着AVelov要翻倍的趋势啊
后来我们将Algolia从850KB减小到了307KB,下面就来分享一下我们所做的东西吧

不用exceptions和RTTI

实际上在我们的底层库中我们没有使用异常,鉴于论述完整性,我也把exceptions一起说了

C++ exceptions 和RTTI默认是关闭的,可以通过设置在Application中设置APP_CPPFLAGS可以打开它,赠送一个使用共享的STL:

APP_CPPFLAGS += -fexceptions -frttiAPP_STL := stlport_shared

同时使用exceptions和RTTI可能,会显著增加你的binary size,这货能删就删吧,不要手软。还有另外一个避免使用C++exceptions的原因:C++对异常的支持不够好。例如:在jni层根本不可能catch一个C++exceptions然后再启动一个java异常。下面的code将会crash(将来的NDK toolchain可能会fix,说这话是2013/1/10):

try {    ...}catch () {    env->ThrowNew(env->FindClass("java/lang/Exception"), "Error occured");}

不用iostream

当我们接到Cyril的反馈,开始想办法减小library size时,我们发现最后一次提交后我们的库直接从850KB涨到了1.35M(此处有大汗!)。首先我们就怀疑是NDK toolchain更新导致的,随之用两个版本就测了一把,发现变化微乎其微。

当我们用二分法回溯commit历史时,逮住了罪魁祸首:
std::cerr << .... << std::endl;
只是这一行就引进了C++的iostream,经我们测试发现如果用了iostream至少会增加300KB。所以用__android_log_print替换吧:

#include 
#define APPNAME "MyApp"__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "The value of 1 + 1 is %d", 1+1);

tips: 需要在Android.mk中加上:LOCAL_LDLIBS := -llog

使用 -fvisibility=hidden

一个非常有效的减小library是使用gcc的visibility feature。这个特性可以让你控制导出在符号表中的函数。jni自带了一个JNIEXPORT的可以标志公开函数的宏。所以需要确认所有的需要导出的函数都有JNIEXPORT:

JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString)

也可以自定义JNIEXPORT:

#define JNIEXPORT __attribute__ ((visibility ("default")))
然后需要在Android.mk中加入:

LOCAL_CPPFLAGS += -fvisibility=hiddenLOCAL_CFLAGS += -fvisibility=hidden

用了这一招之后library已经减小到809KB(-5%),可能根据project的不同略有差别。

用gc-section抛弃不用的函数

这招可以彻底的缩减library的size,直接砍掉了所有没有用途的函数。怎么使用呢,在Android.mk修改C和C++编译选项,请看:

LOCAL_CPPFLAGS += -ffunction-sections -fdata-sectionsLOCAL_CFLAGS += -ffunction-sections -fdata-sectionsLOCAL_LDFLAGS += -Wl,--gc-sections

这个优化选项只减少了1%(此处又有汗Σ( ° △ °|||)︴)

but如果以上选项贴加在一起,这下就超级牛X了:

LOCAL_CPPFLAGS += -ffunction-sections -fdata-sections -fvisibility=hiddenLOCAL_CFLAGS += -ffunction-sections -fdata-sections -fvisibility=hiddenLOCAL_LDFLAGS += -Wl,--gc-sections

直接减少到了691KB(-18.7%)

删除多余的代码

用-icf=safe链接选项就可以。但是要注意这招有副作用:有可能移除了内联函数,从而影响程序的性能。

在mips架构上没有这个指令,需要在Android.mk上加判断:

ifneq ($(TARGET_ARCH), mips)    LOCAL_LDFLAGS += -Wl,--gc-sectionselse    LOCAL_LDFLAGS += -Wl,--gc-sections,--icf=safeendif

这一步减少了0.8%,所有目前操作共减小到了687KB(-19.2%)

改变toolchain的默认选项

如果你想缩减的更彻底,那就需要修改默认编译选项了。这些编译选项因架构而异,例如:

在arm架构上inline-limit设置到64,x86/mips就是300;arm优化flags设置到-Os(size最优),x86/mips上则要设置为-O2(性能最优)。
由于arm事多力广,我们直接使用了arm配置。下面就是在android toolchain(version r8d)我们使用配置选项:

--- android-ndk-r8d/toolchains/mipsel-linux-android-4.6/setup.mk+++ android-ndk-r8d.new/toolchains/mipsel-linux-android-4.6/setup.mk@@ -41,12 +41,12 @@ TARGET_C_INCLUDES :=      $(SYSROOT)/usr/include-TARGET_mips_release_CFLAGS := -O2 +TARGET_mips_release_CFLAGS := -Os                                -g                                -DNDEBUG                                -fomit-frame-pointer                                -funswitch-loops     -                              -finline-limit=300+                              -finline-limit=64 TARGET_mips_debug_CFLAGS := -O0                              -g --- android-ndk-r8d/toolchains/x86-4.6/setup.mk+++ android-ndk-r8d.new/toolchains/x86-4.6/setup.mk@@ -39,13 +39,13 @@ TARGET_CFLAGS += -fstack-protector-TARGET_x86_release_CFLAGS := -O2 +TARGET_x86_release_CFLAGS := -Os                               -g                               -DNDEBUG                               -fomit-frame-pointer                               -fstrict-aliasing                                  -funswitch-loops     -                             -finline-limit=300+                             -finline-limit=64 # When building for debug, compile everything as x86. TARGET_x86_debug_CFLAGS := $(TARGET_x86_release_CFLAGS)

这次使用这些新的flags又缩减了8.5%,和之前的累加在一起减少到了613KB(-27.9%)

限制架构的数量

我们的最终建议是减少支持的架构。如果你有大量的浮点计算出于性能考虑就需要支持armeabi-v7a,但是如果你不需要一个FPU,armeabi也会给出一个近似的结果。然而对于mips处理器...现在市场上还没有用武之地呢~

如果binary size真的对你很重要,你可以只支持armeabi和x86架构(Application.mk):
APP_ABI := armeabi x86
看到了没?砍掉了两个架构,我们的library一下变成了307KB,获得了64%的缩减,这还没有算上iostream增大的1.35M,O(∩_∩)O哈哈~

结论

希望这篇短文能够帮助你缩减android native libraries,毕竟android ndk的默认选项优化的太差了。但是也不要期望和我一样的减幅,这些操作都是因机而异,因code而异的。如果你有其他的缩减binary方法,在评论中分享出来吧!

附注:

使用clang编译native code,使用O3或者Oz选项基本上能一步到位,不能不说clang太牛X了,和gcc根本不在一个量级!

转载于:https://www.cnblogs.com/octave/p/4454205.html

你可能感兴趣的文章
接口的理解
查看>>
JavaScript快速入门
查看>>
python---socket与socketserver
查看>>
第五周翻译
查看>>
asp.net response.redirect 打开新窗口(转载)
查看>>
Lifting the Stone
查看>>
做前端技术方案选型的时候,你是怎么做决策的?
查看>>
hdu4289 最小割最大流 (拆点最大流)
查看>>
不推荐的书——《专注,让你不再分心、成就卓越的力量》
查看>>
Android自定义控件:动画类(九)----PropertyValuesHolder与Keyframe
查看>>
一次python 内存泄漏解决过程
查看>>
Python闭包与函数对象
查看>>
webview中文乱码
查看>>
2018GDKOI——记录
查看>>
[React] Use react-rewards to add microinteractions to React app to reward users for some actions
查看>>
[React] Style the body element with styled-components and "injectGlobal"
查看>>
[Angular2 Router] CanDeactivate Route Guard - How To Confirm If The User Wants To Exit A Route
查看>>
[Grunt] Cleaning your build folder with grunt-contrib-clean
查看>>
discuz!之XML解析错误:废弃 document 元素之后的内容错误
查看>>
jvm类加载机制
查看>>