最佳实践 —— 详细谈谈如何减少APK体积

转载请注明出处: http://www.cnblogs.com/soaringEveryday/p/5254520.htmlhtml

 

随着Android移动开发的需求愈来愈复杂,咱们不可避免的遇到发布出去的apk体积愈来愈大的问题,目前超过10MB、20MB的apk已是很常见的事情了,可是依然可以看到一些apk的体积控制的很小。apk体积增大源于:android

  • 新需求不断的提出
  • 须要支持高分辨率的屏幕而加入了高分图片
  • 依赖了更多的第三方库

本文将从我本身的经历项目中探讨如何有效减少apk的体积。减少Apk体积是一件颇有用处的android优化手段,下降了用户须要下载的比特数,同时也下降了分发安装时失败的几率。git

 

将apk解压后发现,体积占大头的分辨是lib文件夹、res文件夹和dex文件。因此咱们的下降apk体积的策略也应当从如何缩减so文件、资源图片、控制代码质量上来入手。github

 

使用Progruard安全


 

Proguard是Android很早就使用的代码混淆工具,除了用于混淆代码提升安全性之外,他在代码编译的时候也会经过遍历代码的方式来发现没有被调用的代码,从而将其在打包成apk时剔除,最终必定程度上下降了apk的大小。架构

可是Proguard使用时候是要注意的,由于代码中利用反射机制的地方会被Proguard工具破坏,因此要慎重的编写混淆例外文件,同时对于混淆后打包出来的apk要从新充分回归测试下。框架

 

使用Android Lint工具


 

Proguard提供了代码的缩减方式,而Lint对于res下面的资源进行了充分的优化,他会提供一份报告给你,从而通知你哪些资源没有被用到,显然剔除这些资源是能够减小apk体积的。这些资源包括res文件夹下全部的内容,好比图片、字串、尺寸等等。如今Android Lint已经集成到了Android Studio中,用法很简单。测试

进入Android Studio的菜单中选择Analyze->Inspecting Code便可gradle

 

分析完毕后在Inspection选项卡中会有一份详细的报告,找到Android Lint项目

 

拉到下面Unused resource这一栏打开,便是未被使用的资源列表,用户能够参照来手动删除资源

 

清理Assert文件夹


Assert文件夹常常会放置一些不被编译的资源,时间久了,里面可能一些文件或者资源已经不用了,然而这个文件夹也是会被打包到apk里面的。因此按期清理这个里面的内容也是减少apk体积的重要一步。

 

用代码代替图片


开发的时候有些地方能用代码作出来的就尽可能不用图片来渲染,这样子能够减小图片资源的数量从而减小体积,这里举几个例子。

  • 用shape代替背景图
  • 用RotateDrawable代替仅仅是方向不一样的“内容相同”的图片
  • 用layer-list来制做多层图片从而达到复用
  • 使用属性动画而不是多图片连续播放的帧动画

用shape代替背景图

不少背景图好比按钮的背景、纯色背景都是能够用shape来制做的,这样子仅用xml代码就代替了png资源。好比这么要给按钮背景图(纯色背景、带边框、圆角)能够用shape而不是Png图片来制做

<?xml version="1.0" encoding="utf-8"?>
<selector
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <stroke android:width="0.5dp" android:color="@color/white"/>
            <gradient android:startColor="#ffffff" android:endColor="#ffffff" android:angle="0.0" />
            <corners android:topLeftRadius="4dp" android:topRightRadius="0dp" android:bottomLeftRadius="4dp" android:bottomRightRadius="0dp" />
        </shape>
    </item>
</selector>

 

用RotateDrawable代替仅仅是方向不一样的“内容相同”的图片

这里两个图片是两个按钮箭头,可是仅仅方向不一样而已,其实能够只用其中一个图片便可,而另外一个用RotateDrawable来让其“调转”180度

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_arrow_left"
    android:fromDegrees="180"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="180" />

 

用layer-list来制做多层图片从而达到复用

有些需求中须要一种图片,可是明显这个图片是其余几个图片简单叠加而已,那么可使用layer-list来达到目的

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <!-- 最底层的图片,以x,y轴坐标为中心进行旋转-->
        <rotate android:pivotX="0" android:pivotY="0"
                android:fromDegrees="-10" android:toDegrees="-10">
            <bitmap android:src="@drawable/chatting_bg_default_thumb"/>
         </rotate>
    </item>
    <!-- 第二层的图片,以x,y轴坐标为中心进行旋转-->
    <item>
        <rotate android:pivotX="0" android:pivotY="0"
                android:fromDegrees="15" android:toDegrees="15">
            <bitmap android:src="@drawable/chatting_bg_purecolor_thumb"/>
        </rotate>
    </item>
    <!-- 最上层的图片,以x,y轴坐标为中心进行旋转-->
    <item>
        <rotate android:pivotX="0" android:pivotY="0"
                android:fromDegrees="35" android:toDegrees="55">
            <bitmap android:src="@drawable/mark"/>
        </rotate>
    </item>
</layer-list>

 

使用属性动画而不是多图片连续播放的帧动画

这里就不过多解释了,好比你作一个图片的旋转,经过属性动画便可用代码搞定。若是你要用多张图片“连续播放”的话,那要用不少张预备图片才行,无疑加大了apk的体积

 

放弃一些图片资源


咱们知道Android是支持多分辨率的,提供了多套图片适配的机制,咱们根据不一样尺寸的屏幕来放入多套的图片,好比ldpi、mdpi、hdpi、xhdpi等。可是实际上有些图片你多是不须要放置的。

  • 对于ldpi,系统会自动的将hdpi的图片缩放到达到目的,因此你就不须要把ldpi的图片拷贝到你的res文件夹下了
  • 有些分辨率的图片你多是用不到的,好比ldpi和xxxhdpi。前者的手机目前不多了,然后者是针对2k屏幕的,目前尚未普及开。因此对于这点,你能够考虑下是否须要放入这两套图片资源

此外,咱们所引入的第三方包中可能也引用了资源图片,可是其中的某些咱们多是不想要的,好比ldpi和xxxhdpi,那么是否能够设置什么东西来让打包的时候剔除他们呢?是能够的,能够配置下build.gradle

defaultConfig {
    // ...

    resConfigs "en", "de", "fr", "it"
    resConfigs "hdpi", "xhdpi", "xxhdpi"
}

defaultConfig提供了resConfig这个flavor来指定打包出只打包某些资源,好比字串、图片等等

 

压缩图片


图片从美工那边拿到的时候是比较漂亮的,可是代价是size一般也高居不下,咱们能够用一些工具来“近乎无损”的缩小图片资源,同时不下降图片效果。这里推荐pngquant这个工具,能够参考个人上篇博客

http://www.cnblogs.com/soaringEveryday/p/5148881.html

 

so的优化


咱们在接入百度地图的时候,发现须要引入不少不少so

这些so文件占了不少大致积,若是你不加控制,全部的so都会打包到你的apk了,最后发现这些so文件尽然占了咱们apk的近乎三分之一的体积。然而考虑下咱们的用户,基本都是跑在手机上的(没有人跑在模拟器上),因此明显x86和x86_64的so是不须要支持,那么咱们能够经过配置gradle来制定只打包某些so,依然是在defualtConfig中:

defaultConfig {
        
        ... ...

        ndk {
            //设置支持的SO库架构
            abiFilters 'arm64-v8a', 'armeabi' //, 'x86', , 'x86_64', 'arm64-v8a'
        }
    }

最后打包出来的apk真的是减小了好几个MB,这是太好了

 

固然若是有一些so是大家本身开发的,那么能够参考这个文章来参考若是用ndk开发的时候减小so自己的体积,这里就不过多介绍了

https://blog.algolia.com/android-ndk-how-to-reduce-libs-size/

 

对第三方库进行从新定制(从新打jar包)


 

开发中引入大量的第三方开发库也是一个增长apk体积的重要缘由,由于你把人家的代码和资源全给包含进来了。可是想一想人家的代码,并不必定全要的,是否能够只引入人家的一部分代码,而不是在build.gradle中仅仅添加一行“compile”来所有依赖呢?答案是能够得!这里举一个例子

咱们开发中有一个需求是将数据经过图标的方式显示出来,这里咱们站在巨人的肩膀上,使用了MPAndroidChart这个优秀的开源项目(https://github.com/PhilJay/MPAndroidChart),可是发现他们的东西太多了,咱们仅仅须要使用其中一种chart。若是在build.gradle里面加一句:

dependencies {
    compile 'com.github.PhilJay:MPAndroidChart:v2.2.3'
}

这要把他们的库全给引用过来了。想到他们是开源的,代码有,因此咱们仅仅把他们的咱们所用到的代码给剥离出来,单独打包了一个jar包引入到咱们的项目里面,就OK了,减小了大量的无用依赖代码!

 

动态加载技术(插件化)


如今大型互联网移动App不少都采用了动态加载的技术,由于他们的业务需求太大,经过动态加载技术能够将一部分业务模块独立出来,以插件的方式分割出去,这样子主apk的体积就大大减少。当用户安装主apk后,静默的在后台下载插件apk,当用户点击使用到相关的子模块项目时候,动态的加载插件apk。

 

动态加载技术无疑从根本上减小了apk的体积,可是引入这个技术是有代价的,增长了项目的维护难度和开发难度。因此该技术适用于大型的移动应用,当你的业务大到不分开模块难以高效率开发维护的时候,再考虑动态加载技术吧,不然若是小规模应用,仍是老老实实考虑传统的android官方推荐的开发方式。

这里推荐几个比较好的动态加载开源框架项目供你们研究

相关文章
相关标签/搜索