最近我们看到很多开发者对在app中使用Realm对于apk文件的大小影响特别关注。在今天发布的0.79版本的realm中,我们对此作出了重大改进。


一个正常的啥都没有且没有使用任何其他类库,运行在ARM架构上的Android app一般最小为907kb(当然,在某些情况下,最低可以达到50kb,具体看这儿 https://realm.io/news/reducing-apk-size-native-libraries/#feb-17-update)。当使用0.78版本的realm时,app大概是3.9Mb,但是在使用0.79版本的realm时,apk的大小可以减小到1.6Mb,减小了70%。


下面来看下为什么以及如何实现


为什么你应该关心apk的大小


节省几Mb可能对现代设备的来说可能看起来不太多,但是在Android手机中有大量的旧的或低端设备并没有太大的剩余空间来使用。事实上,由于Android在新兴经济体尤其受欢迎,大多数运行Android app的设备看起来不像您的iPhones或Nexus - 有些只有几十兆字节的空间用来于所有的app,这意味着安装一个几兆的app 通常需要删除其他app。通常,app是否多几Mb对于那些空间有限的用户来说意味着是否去选择安装。


Native Libraries的解决办法
如果你下载了0.78版本的realm并且深入了解了你可以看到:

$ du -h -d1
 28K    ./com
468K    ./io
9.1M    ./lib <--- Native libraries
 16K    ./META-INF
9.6M



哎呀! Native libraries总共9MB,超过我们的jar大小的94%。 反过来,这将是用的apk文件增大超过3MB(压缩后)。  


如果你读了FAQhttp://realm.io/docs/java#faq,你应该知道下载下来的realm jar包大小和添加realm到你的app中增加的apk文件大小并不相同。这是因为realm必须为手机的每个架构(例如arm,armv7-a,arm64)包含一个相同的本地库的副本。现在,你通常需要为ARM,ARMv7-A,ARM64,MIPS&x86,这5中架构的手机做适配,这也就意味着平均每个字节的native代码都要乘以5。




现在Android安装过程非常聪明,可以丢弃非相关CPU的native libraries。 例如,如果在MIPS设备上运行,它将只保留库的MIPS副本,并删除其他。 所以一旦你的app安装在设备上,Realm实际上会占用比你的APK看起来少很多的空间。你的app在应用商店显示的大小很大,进而会造成下载困难。




优化Native Libraries


假设我们忽略jar包中的Native code有多个副本的事实,仍然值得尝试看看可以做什么来优化每个编译版本的本机库的大小。




看看我们jar的lib文件夹中的文件,你可以看到我们支持的每个独立架构so库的大小。 (ps:在0.78中还不支持ARM64)


$ ls -lh lib/
lib/armeabi:
total 3776
-rwxr-xr-x@ 1 emanuelez  staff   1.8M Jan 22 14:30 libtightdb-jni.so


lib/armeabi-v7a:
total 3672
-rwxr-xr-x@ 1 emanuelez  staff   1.8M Jan 22 14:31 libtightdb-jni.so


lib/mips:
total 6376
-rwxr-xr-x@ 1 emanuelez  staff   3.1M Jan 22 14:32 libtightdb-jni.so


lib/x86:
total 4888
-rwxr-xr-x@ 1 emanuelez  staff   2.4M Jan 22 14:31 libtightdb-jni.so




(libtightdb是我们的C++核心库的名字,源于我们公司的以前名称“TightDB”;该项目现在正在重命名为realm-core。)




你可以看到ARM库要小得多。 这是因为他们可以利用Thumb指令集,而最棒的是,大多数Android设备运行在ARM处理器。(ps:thumb指令集是arm指令集的一个子集,运行在arm处理器上)




在这一点上,我们的核心(libtightdb)和JNI代码都是使用带有-Os标志的GCC编译的,它尝试在保持良好性能的同时优化库的大小。 我们还启用了-visibility=hidden来隐藏大部分不需要的ELF符号。




工具链技巧


那么能做些什么来改进呢? 我们尝试了几个设置并取得了很好的效果。 现在使用GCC工具链的两个额外的特性:


Link Time Optimization 


GC Sections 


这让我们得到如下结果:


realm/build/intermediates/bundles/release/jni/armeabi:
total 1624
-rwxr-xr-x  1 emanuelez  staff   809K Feb 11 09:30 libtightdb-jni.so


realm/build/intermediates/bundles/release/jni/armeabi-v7a:
total 1592
-rwxr-xr-x  1 emanuelez  staff   793K Feb 11 09:30 libtightdb-jni.so


realm/build/intermediates/bundles/release/jni/mips:
total 3448
-rwxr-xr-x  1 emanuelez  staff   1.7M Feb 11 09:30 libtightdb-jni.so


realm/build/intermediates/bundles/release/jni/x86:
total 2640
-rwxr-xr-x  1 emanuelez  staff   1.3M Feb 11 09:30 libtightdb-jni.so




我们将库的大小减半了! 这使jar文件的总大小从3.1 MB到2.1 MB。 相当大的改进!




APK拆分


但还有一个事情,Android开发可以做到更好:APK拆分。 这是添加到Android Gradle插件中一个相对较新的功能,它保证为每个CPU提供一个APK,只包含相关的本机库。




不幸的是,当native libraries捆绑在jar文件中时,此功能不起作用,但是有一个可以使用的快速解决方法。




我们的发行包(网站目录:Download->Java)包含一个名为“eclipse”的文件夹。 此文件夹包含Realm库的拆分版本。 所有你需要做的是将jar文件复制到app的libs文件夹中,并复制src / main / jniLibs目录中的四个文件夹。 现在您可以在build.gradle文件中启用ABI拆分,如下所示:


android {
    // Some other configuration here...


    splits {
        abi {
            enable true
            reset()
            include 'x86', 'armeabi', 'armeabi-v7a', 'mips'
            universalApk false
        }
    }
}




现在Gradle将生成一个APK每个CPU减小的尺寸更进一步!




总结回顾:


把这一切放在一起,对于大多数(基于ARM)的设备来说,较小的编译大小和APK拆分意味着Realm现在只会让你的apk增加约730KB。 这比以前的3MB领域的76%少了会添加到你的应用程序过去! 


我们将不断尝试将这个数字降低,我们期待帮助您保持您的应用程序精益和干净,无论你部署在哪种架构的设备上。


所以综上得出结论:

第一步:

defaultConfig {
        applicationId "xxx"
        minSdkVersion 19
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
        ndk {
            //设置支持的cpu
            abiFilters 'armeabi' //'armeabi-v7a', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
        }

第二步:

splits {
        abi {
            enable true
            reset()
            include 'armeabi'
            universalApk false
        }
    }

完毕!



pps:realm 2.x版本对于‘armeabi’不支持,所以在build.gradle文件中要做如下修改

ndk { abiFilters 'armeabi' ,'armeabi-v7a' }

参考:https://github.com/realm/realm-java/issues/3484