. 使用 strip

使用 NDK toolchain 可以把调试的 C++ 符号表(Symbol Table)中数据删除,我们一般我们打成 APK 会自动帮我们做这个工作,当然也可以手动设置:

手动的在链接选项中加入 strip 参数,配置如下所示:

SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,-s")

也可以手动执行 ndk 提供的aarch64-linux-android-strip命令移除动态库中的调试信息,这种方式除了前面方法外优化体积最高的方式,比如 libLibSampleApp.so 从 48M 直接优化到了 992k。

. 设置编译器的优化 flag

编译器有个优化 flag 可以设置,分别是-Os(体积最小),-O3(性能最优)等。这里将编译器的优化 flag 设置为-Os,以便减少体积。

CMake:

set(CMAKE_C_FLAGS "\({CMAKE_C_FLAGS} -Os")
set(CMAKE_CXX_FLAGS "\){CMAKE_C_FLAGS}")

Android.mk

LOCAL_CPPFLAGS += -Os

LOCAL_CFLAGS += -Os

除了直接删除占用体积较大的模块外,编译器优化是排下来优化空间最大的方法。设置完-Os后占用提交较大的前几个库体积对比:

. 使用 gc-sections 去除没有用到的函数

有些时候代码量比较大的时候我们没办法手动发现无用的函数,这个时候可以可以开启编译器的 gc-sections 选项,让编译器自动的帮你做到这一点。

编译器可以配置自动去除未使用的函数和变量,以下是配置方式:

CMake:

去除未使用函数与变量

set(CMAKE_C_FLAGS "\({CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS "\){CMAKE_C_FLAGS}")

设置去除未使用代码的链接flag

SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,--gc-sections")

Android.mk:

OCAL_CPPFLAGS += -ffunction-sections -fdata-sections

LOCAL_CFLAGS += -ffunction-sections -fdata-sections

LOCAL_LDFLAGS += -Wl,--gc-sections

. 设置编译器的 Visibility Feature

Visibility Feature 就是用来控制在哪些函数可以在符号表中被输入,由于 C++并不是完全面向对象的,非类的方法并没有 public 这种修饰符,因此,要用 Visibility Feature 来控制哪些函数可以被外部调用。而 JNI 提供了一个宏-JNIEXPORT 来控制这点。所以只要对函数加上这个宏,像这样:

// JNIEXPORT就是控制可见的宏

// JNICALL在NDK这里没有什么意义,只是个标识宏

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

然后在编译器的 FLAGS 选项开启 -fvisibility = hidden 就可以。这样,不仅可以控制函数的可见性,并且可以减少包体的大小。

. 去除 C++代码中的 iostream 等直接 IO 相关代码

使用 STL 中的 iostream 相关库会明显的增加包的体积,而 Android 本身是有预编译库(android/log.h)可以代替输入到控制台的工具的。在我们的 SDK 中由于之前是控制台程序所以用到了输入输出,编译的时候没有把这块排除出去,造成了一定的体积冗余。

. STL 的使用方式

对于 C++的 library,引用方式有 2 种:

静态方式(static)

动态方式(shared)

其中,静态方式在编译时会将用到的相关代码直接复制到目的文件中;而动态方式则会将相关的代码打成 so 文件,以便多次引用。由于编译器在编译时并不能知道所有被引用的地方,所以同时会打入了很多不相关的代码。

所以,如果项目中引用 library 的函数较多时,用动态方式可以避免多次拷贝,节省空间。相反,则直接使用静态方式会更节省空间。由于我们 SDK 的模块特别多,再加上整体 APK 里面已经有其他业务引入了动态库,所以我们用动态库的方式。

. 不使用 Exception 和 RTTI

关于这两点在网上看到的没有实践过,不过拿过来可以作为包体积持续优化的参考。

  • RTTI

    通过 RTTI,能够通过基类的指针或引用来检索其所指对象的实际类型,即运行时获取对象的实际类型。C++通过下面两个操作符提供 RTTI。

    (1)typeid:返回指针或引用所指对象的实际类型。

    (2)dynamic_cast:将基类类型的指针或引用安全的转换为派生类型的指针或引用。

    RTTI 的选项是默认关闭的的,而代码中其实并没有用到相关的功能,这里可以直接关闭。
  • Exception

    使用 C++的 exception 会增加包的大小,而目前 JNI 对 C++的 exception 的支持是有 bug 的,比如下面这段代码就会引起程序的 crash(对于低版本的 android NDK)。因此要在程序中引入 exception 要自己实现相关逻辑,但是这样又会增加包体大小。对于开发者来说,exception 可以帮助快速定位问题,而对于使用者并不是那么重要,这里可以去掉。

NDK 减少 so 库体积方法总结的更多相关文章

  1. 【Android】Eclipse自动编译NDK/JNI的三种方法

    [Android]Eclipse自动编译NDK/JNI的三种方法 SkySeraph Sep. 18th  2014 Email:skyseraph00@163.com 更多精彩请直接访问SkySer ...

  2. 【Android】Eclipse自己主动编译NDK/JNI的三种方法

    [Android]Eclipse自己主动编译NDK/JNI的三种方法 SkySeraph Sep. 18th  2014 Email:skyseraph00@163.com 一.Eclipse关联cy ...

  3. linux减少服务器带宽的方法

    linux减少服务器带宽的方法用百度静态资源公共库http://cdn.code.baidu.com/ 不仅可以不使用服务器流量 而且还有cdn加速比方说http://apps.bdimg.com/l ...

  4. mac下使用glew库,方法

    mac下使用glew库,方法 分类: OpenGL2015-01-15 15:52 210人阅读 评论(0) 收藏 举报   目录(?)[+]   主要参考http://www.cnblogs.com ...

  5. Xcode6.1标准Framework静态库制作方法。工程转Framework,静态库加xib和图片。完美解决方案。

    http://www.cocoachina.com/bbs/read.php?tid-282490.html Xcode6.1标准Framework静态库制作方法.工程转Framework,静态库加x ...

  6. paip.ikanalyzer 重加载词库的方法.

    paip.ikanalyzer 重加载词库的方法. 作者Attilax  艾龙,  EMAIL:1466519819@qq.com  来源:attilax的专栏 地址:http://blog.csdn ...

  7. 详细地jsoncpp编译方法 和 vs2010中导入第三方库的方法

    详细地jsoncpp编译方法 和 vs2010中导入第三方库的方法 一 编译链接 1 在相应官网下载jsoncpp 2 解压得到jsoncpp-src-0.5.0文件 3 打开jsoncpp-src- ...

  8. Android中集成第三方库的方法和问题

    Android中集成第三方库的方法和问题 声明: 1. 本文參考了网上同学们的现有成果,在此表示感谢,參考资料在文后有链接. 2. 本文的重点在第三部分,是在开发中遇到的问题及解决的方法.第一,第二部 ...

  9. 用NDK调用第三方库

    用NDK调用第三方库遇到不少坑,总结一下. 1.添加JNI目录 参考: http://www.cnblogs.com/lanqie/p/7442668.html 2.文件介绍: 其中:JniFacto ...

  10. golang 编译或链接 c语言动态、静态库的方法, golang 与 c语言 相互调用

    1.golang链接c静态库的方法可以见这个示例: https://github.com/atercattus/go-static-link-example https://github.com/sh ...

随机推荐

  1. Spring MVC + Webapp 项目显示 404 错误

    这是因为 Tomcat 版本,把 Tomcat10 换成 Tomcat9 就可以解决这个问题了.下面是我正在做的一个 Spring MVC 入门案例,因为 Tomcat 10,DispatcherSe ...

  2. MySQL 中的锁有哪些类型,MySQL 中加锁的原则

    MySQL 中的锁理解 锁的类型 全局锁 缺点 适用范围 表级锁 表锁 元数据锁 意向锁 自增锁 行锁 Record Lock Gap Lock Next-Key Lock 插入意向锁 加锁的原则 1 ...

  3. Java语言输出菱形图型

    package fuxi;public class Diamond {    public static void main(String[] args) {        printHollowRh ...

  4. 【MySQL】导出到CSV

    http://www.yiidian.com/mysql/mysql-export-table-to-cvs.html 要将表导出为 CSV 文件,我们将使用SELECT INTO....OUTFIL ...

  5. 存储型XSS的利用

    一.存储型XSS漏洞利用原理 攻击者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码.意味着只要访问了这个页面的访客,都有可能会执行这段恶意脚本. 二.漏洞利 ...

  6. react 02 组件state click

    一,组件 import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; // 函数式组件 ret ...

  7. 四大组件之广播接收者BroadcastReceiver

    参考:Android开发基础之广播接收者BroadcastReceiver 什么是广播接收者? 我们小时候都知道,听广播,收听广播!什么是收听广播呢?打开收音机,调频就可以收到对应的广播节目了.其实我 ...

  8. 数据每三位增加一个逗号(即千分符) js

    使用  toLocaleString()  另一种使用场景

  9. docker-compose简易编写和模板命令

    version: '3' service: mysql: #指定容器名称 restart: always # 代表知道docker 启动那么这个容器就跟着一起启动 image: daocloud.io ...

  10. Linuxt通过命令lsof或者extundelete工具恢复误删除的文件或者目录

    Linux不像windows有那么显眼的回收站,不是简单的还原就可以了.linux删除文件还原可以分为两种情况,一种是删除以后在进程存在删除信息,一种是删除以后进程都找不到,只有借助于工具还原.这里分 ...