致Android开发者:APP 瘦身经验总结

   2016-11-03 0
核心提示:小编说:随着移动端产品功能的逐渐增加,APP 的体积也不可避免地呈现上升趋势,如果不加以重视,几个版本迭代下来,可能你的 APP 体积会达到用户不能忍受的程度。 如果你是 SDK 开发者,你的 SDK 包大小是用户决定是否采用的关键因素;如果你的APP 想要预装到

小编说:随着移动端产品功能的逐渐增加,APP 的体积也不可避免地呈现上升趋势,如果不加以重视,几个版本迭代下来,可能你的 APP 体积会达到用户不能忍受的程度。

如果你是 SDK 开发者,你的 SDK 包大小是用户决定是否采用的关键因素;如果你的APP 想要预装到某款手机或者某款 Android 系统中,APP 的体积也会受到很严格的限制。

因此,APP 的瘦身是每个移动端产品都会遇到的一个普遍问题,本文选自《Android高级进阶》将从不同的角度切入,全面介绍APP 瘦身相关知识。

致Android开发者:APP 瘦身经验总结

APP 为什么变胖了

在 Android 出现的最初几年时间里,除了游戏,我们很难看到动辄十兆或者几十兆的APP,但现在你只要到应用市场上去走一圈,就能发现十兆以上大小的应用比比皆是,究其原因,主要有以下几种。

随着 Android 系统版本的碎片化发展以及手机类型的极大丰富,每个 APP 要支持的主流 dpi 分类越来越多,从最初的 ldpi、mdpi、hdpi,到后来的 xhdpi、xxhdpi、xxxhdpi、tvdpi 等。

随着 Android 生态系统的不断发展成熟,出现了很多方便开发者的函数库和 SDK,随着引入的函数库和 SDK 数量的增多,不可避免的引入很多重复功能代码以及资源文件。

用户对 APP 视觉要求的不断提高,APP 提供的资源细节越来越丰富,占用的体积也不断上升。

从 APK 文件的结构说起

到应用市场上面随便下载一个 APK 文件,由于 APK 本身是一个压缩文件,因此我们可以将后缀名由 .apk 改为 .zip,然后解压该文件,一般情况下,如果开发者在发布 APK 时没有对其进行加固等特殊处理,我们应该能够得到如下图所示的文件夹内容。

致Android开发者:APP 瘦身经验总结

在进一步分析如何给 APP 瘦身之前,我们首先来了解下一个 APK 文件所包含的东西,这样才能有的放矢地进行优化。

AndroidManifest.xml:Android 项目的系统清单文件,用来控制 Android 应用的名称、桌面图标、访问权限等全局属性。此外,Android 应用的四大组件 Activity、Service、BroadcastReceiver 和 ContentProvider 也需要在这个文件中声明和配置。

assets:该目录用来存放需要打包到 Android 应用程序的静态资源文件,例如图片资源文件、JSON 配置文件、渠道配置文件、二进制数据文件、HTML5离线资源文件等。与res/raw 目录不同的是,assets 目录支持任意深度的子目录,同时该目录下面的文件不会生成资源ID。下图所示是某个 APP 的 assets 目录内容,可以一窥该目录的作用。

致Android开发者:APP 瘦身经验总结

classes.dex:应用程序的可执行文件。Android 代码都打包在这种类型的文件中,可以通过反编译工具反编译后进行查看,在上图中可以看到,这个 APP 有 classes.dex、classes2.dex 和 classes3.dex 三个 dex 文件,这是因为这个 APP 的方法数已经超过 65K 的限制,需要进行分包,对于一般的 APP 来说,方法数目没有超过 65K,那么打包后只会存在一个 classes.dex 文件。

lib:该目录存放的是应用程序依赖的不同 ABI 类型的 .so 文件,如图所示。

致Android开发者:APP 瘦身经验总结

res:该目录存放的都是应用的资源文件,包括图片资源、字符串资源、颜色资源、尺寸资源等,这个目录下面的资源都会出现在资源清单文件 R.java 的索引中。

致Android开发者:APP 瘦身经验总结

resources.arsc:资源索引表,用来描述具有ID值的资源的配置信息。

META-INF:该目录存放的是签名相关的信息,用于验证 APK 包的完整性以及保证系统的安全。主要包含三个文件:

① MANIFEST.MF:主要存放 APK 包中每个文件的名字及每个文件的 SHA1 哈希值,内容如下。

致Android开发者:APP 瘦身经验总结

② CERT.SF:通常每个 APP 会有一个特定的名字,例如 BDMOBILE.SF、NETDISK_.SF等, 它保存的是 MANIFEST.MF 的哈希值以及 MANIFEST.MF 文件中每一个哈希项的哈希值。

致Android开发者:APP 瘦身经验总结

③ CERT.RSA:这个文件保存了 APK 包的签名和证书的公钥信息。

从上面的分析可以看出,一个 APK 包的组成中,影响到最终的 APK 包大小的文件可分为三种类型,分别如下。

Java 代码文件:classes*.dex。

Native 代码文件:lib目录下面的 .so 文件。

资源文件:包括 assets 目录、res 目录以及 resources.arsc 索引表文件。

下面介绍的 APP 瘦身方法都是基于如何减少以上三种类型文件的大小得出的方案。

优化图片资源占用的空间

目前移动端 Android 平台原生支持的图片格式主要有:JPEG、PNG、GIF、BMP 和 WebP ( 自从 Android 4.0 开始支持),但是在 Android 应用开发中能够使用的编解码格式只有其中的三种:

JPEG、PNG、WebP,这可以通过查看 Bitmap 类的 CompressFormat 枚举值来确定。

public static enum CompressFormat {

JPEG,

PNG,

WEBP;

private CompressFormat() {

}

}

如果要在应用层使用 GIF 格式图片,那么需要自己引入第三方函数库进行支持。在对应用中的图片资源进行压缩和优化之前,我们有必要对这几种常见的图片格式做一个简单的了解和区分。

JPEG:JPEG(发音为/jay-peg/)是一种广== 泛使用的有损压缩图像的标准格式,它不支持透明和多帧动画,一般摄影类作品最终都是以 JPEG 格式展示。通过控制压缩比,可以调整图片的大小。

PNG:PNG 是一种无损压缩图片格式,它支持完整的透明通道,从图像处理领域讲,JPEG 只有 RGB 三个通道,而 PNG 有 ARGB 四个通道。由于是无损压缩,因此 PNG 图片一般占用空间比较大,会无形中增加最终 APP的大小,我们在做 APP 瘦身时一般都要对 PNG 图片进行处理以降低其大小。

GIF:GIF 是一种古老的图片格式,它诞生于 1987 年,随着初代互联网流行开来。它的特点是支持多帧动画。大家对这种格式肯定不陌生,社交平台上面发送的各种动态表情,大部分都是基于 GIF 来实现的。

WebP:相比前面几种图片格式,WebP(发音为 /weppy/)算是一个初生儿了,它由Google在 2010 年发布,它支持有损和无损压缩、支持完整的透明通道、也支持多帧动画,是一种比较理想的图片格式。目前国内很多主流 APP 都已经应用了 WebP,例如微信、微博、淘宝等。在既保证图片质量又要限制图片大小的需求下,WebP 应该是首选。

目前无论 Android 平台还是 iOS 平台,大多数 APP 在搭建界面时使用的几乎都是 PNG 格式图片资源,除非你的项目已经全面支持 WebP 格式,否则你都会面临对 PNG 图片瘦身的要求。

在这里,我们可以通过几个工具对 PNG 图片进行压缩来达到瘦身的目的。

1 . 无损压缩 [ImageOptim]

ImageOptim 是一个无损的压缩工具,它通过优化 PNG 压缩参数,移除冗余元数据以及非必需的颜色配置文件等方式,在不牺牲图片质量的前提下,既减小了 PNG 图片占用的空间,又提高了加载的速度。

2 . 有损压缩 [ImageAlpha]

ImageAlpha 是 ImageOptim 作者的一个有损的 PNG 压缩工具,相比较而言,图片大小得到极大的减低,当然同时图片质量也会受到一定程度的影响,经过该工具压缩的图片,需要经过设计师的检视才能最终上线,否则可能会影响到整个 APP 的视觉效果。

3 . 有损压缩 [TinyPNG]

TinyPNG 也是比较知名的有损 PNG 压缩工具,它以 Web 站点的形式提供,没有独立的APP 安装包,同所有的有损压缩工具一样,经过压缩的图片,需要经过设计师的检视才能最终上线,否则可能会影响到整个 APP 的视觉效果。

当然还有很多无损压缩工具,例如 [JPEGMini]4、[MozJPEG]5 等,大家可以从中选择适合自己项目的一个就行,主要是在图片大小和图片质量之间找到一个折衷点。

4 . PNG/JPEG 转换为 WebP

如果你的APP 最低支持到 Android 4.0,那么可以直接使用系统提供的能力来支持 WebP,如果是 4.0 以下的系统,也可以通过在 APP 中集成第三方函数库,例如 [webp-android-backport]来实现对 WebP 的支持。

根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使这些PNG 文件经过其他压缩工具例如 ImageOptim 压缩之后,WebP 依然可以减少约 28% 的文件大小。

需要注意的是,对于具有 Alpha 通道的 PNG 图片来说,如果需要在 Android 4.2.1 之前的系统上运行,那么不能转换成 WebP 格式,因为只有在 Android 4.2.1 以上的系统中,才能解析具有 Alpha 通道的 WebP 图片。

WebP 转换的工具可以选择 [智图] 和 [iSparta]等。

5 . 尽量使用 NinePatch 格式的 PNG 图

.9.png 图片格式简称 NinePatch 图,本质上仍然是 PNG 格式图片,它是针对 Android 平台的一种特殊 PNG 图片格式,可以在图片指定位置拉伸或者填充内容。NinePatch 图的优点是体积小、拉伸不变形、能够很好的适配 Android 各种机型。Android SDK 自带了 NinePatch 图的编辑工具,位于 sdk/tools/draw9patch 中,点击即可启动。当然,Android Studio 也集成了 PNG转 NinePatch 的功能,我们只需右键点击某个需要转换的PNG 图片,在弹出的对话框中选择Create 9-Patch File… 即可自动完成转换。

最后我们以 TinyPng 为例来直观地观察压缩工具对图片的压缩效果。

致Android开发者:APP 瘦身经验总结

使用 Lint 删除无用资源

Proguard 只会对 Java 代码起作用,对于 res/drawable* 目录中的图片,如果没有使用到,Proguard 只会移除该图片在 R 类中的引用,不会删除该图片。这时就需要用到 Android Lint了。Android Lint 天然集成在Android Studio 中,它会分析 res 目录下面的资源文件,但不会分析 assets 目录下面的资源文件。具体使用方法可以参考本书第49 章的相关介绍。对工程执行Android Lint,结果出来后,查看 Android Lint-Unused resources 选项即可得到哪些资源是多余的。当然,我们不能过度依赖工具,还需要人工确认是否真的是多余的,例如某些资源是通过 Java 反射机制来使用的,这时 Android Lint 还是会检查出该资源没有被使用到。

致Android开发者:APP 瘦身经验总结

利用 Android Gradle 配置

在 Android Studio 工程的 app/build.gradle 文件中进行一些配置可以进一步缩减最终生成的APK 大小,它们分别如下。

1 . minifyEnable

标识是否开启 Proguard 混淆,设置为 true 时需要同时设置 Proguard 配置文件名和规则。Proguard 的作用不仅仅是混淆,它还具有压缩、优化等功能。它会遍历所有代码并找出没有引用到的代码,这些无用代码在生成最终的 APK 文件之前会被剔除掉。同时 Proguard 会使用简短的字母组合替换原来的类名、属性名等,这些都能在一定程度上减少 APP 的大小。

2 . shrinkResources

标识是否去除无用的resource 文件。需要注意的是,shrinkResources 依赖于minifyEnable, 需要 minifyEnable 设置为 true 时才会生效。同时,shrinkResources 需要慎重使用,某些资源是通过反射获取的,这类资源可能也会被删除掉,从而在运行时会报Resources$NotFoundException 异常。

以上两种方式的代码示例如下。

android {

// 省略其他

buildTypes {

release {

minifyEnabled true

shrinkResources true

proguardFiles getDefaultProguardFile(‘proguard-android.txt’),

‘proguard-rules.pro’

}

}

}

3 . resConfigs

Android 开发过程中不可避免的会引入第三方开源函数库或者 SDK,在不修改它们的前提下,在最终生成的 APK 中,我们可能会引入很多其实不需要使用到的资源文件,主要可以分为两种。

DPI 目录:Android 从出现到现在,历经了多个版本,支持多种不同类型的设备,屏幕密度、屏幕形状、屏幕大小等都差别很大,支持的屏幕密度就有 ldpi、mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi 等多种类型。在实际项目开发中,我们当然不可能为每一种屏幕密度提供对应的一套资源文件,这不仅没必要而且会显著增加 APP 的体积,我们需要调研产品的目标用户以及目前市场上主流的手机设备屏幕密度,满足这些用户和设备即可。因此需要剔除引用的第三方函数库或者 SDK 中可能存在的不需要的 DPI 目录及其文件。

国际化文件:Android 开发中不可避免地会引用外部的函数库,这些函数库是为了全世界开发人员服务的,不可避免地会存在很多国际化文件,而对于普通的 APP 来说,可能只需要支持本国语言就可以了。因此,也需要剔除无用的国际化文件。

在 Android Studio 中,我们可以通过在 Gradle 中进行配置,来选择最终打包到 APK 中的资源,这一步是通过指定 resConfig 或者 resConfigs 的取值,通过如下 DSL(Domain SpecificLanguage)防止 AAPT(Android Asset Packaging Tool)打包不需要的资源。

android {

// 省略其他

defaultConfig {

// 省略其他

resConfigs “en”, “fr”

resConfigs “nodpi”, “hdpi”, “xhdpi”, “xxhdpi”, “xxxhdpi”

}

4 . ndk.abiFilters

在工程的 build.gradle 文件中增加 ndk.abiFilters 配置,可以指定我们需要的 ABI 类型,从而可以过滤掉不需要的 ABI 类型的 .so 文件。

android {

// 省略其他

defaultConfig {

// 省略其他

ndk {

abiFilters “armeabi-v7a”, “x86”

}

}

5 . 重构和优化代码

保持良好的编码习惯,对你的代码进行持续的优化或者重构,减少重复代码,实现代码复用,这方面可以多读一读 Martin Fowler 的《重构- 改善既有代码的设计》 一书。在项目开发中,建议抽出一个基础库,提供基础的功能,例如网络、数据库、加解密、utils 工具包等,实现不同模块间复用基础的功能,甚至在公司层面维护一个公共库,在不同产品线之间共享。这样如果不同产品之间需要相互集成,复用一套公共库,能在很大程度上减少重复的代码。

6 .资源混淆

资源文件的混淆方案目前有 [ 美团] 和 [ 微信] 两种,前者是通过修改 AAPT 在处理资源文件相关的源码达到资源文件名的替换;后者是通过直接修改 resources.arsc 文件达到资源文件名的混淆。相比之下,微信的方案更优,而且微信已经将这个方案开源出来,地址在[AndResGuard],感兴趣的同学通过上面的 README.md 介绍可以很容易集成到自己的项目中。

7 . 插件化

插件化开发也是减少 APP 体积的一个可行的途径,不过首先你需要实现一个插件化的框架,用来在线动态的下载并加载各个插件。

致Android开发者:APP 瘦身经验总结
 
标签: 安卓开发
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 安卓中通知功能的具体实现
    安卓中通知功能的具体实现
    通知[Notification]是Android中比较有特色的功能,当某个应用程序希望给用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知实现。使用通知的步骤1、需要一个NotificationManager来获得NotificationManager manager = (NotificationManager
    02-05 安卓开发
  • Android view系统分析-setContentView
    Android view系统分析-setContentView
    第一天上班,列了一下今年要学习的东西。主要就是深入学习Android相关的系统源代码,夯实基础。对于学习Android系统源代码,也没什么大概,就从我们平常使用最基础的东西学起,也就是从view这个切入点开始学习Android的源码,在没分析源码之前,我们有的时候
    02-05 安卓开发
  • 如何进行网络视频截图/获取视频的缩略图
    如何进行网络视频截图/获取视频的缩略图
    小编导读:获取视频的缩略图,截图正在播放的视频某一帧,是在音视频开发中,常遇到的问题。本文是主要用于点播中截图视频,同时还可以获取点播视频的缩略图进行显示,留下一个问题,如下图所示, 如果要获取直播中节目视频缩略图,该怎么做呢?(ps:直播是直
  • Android NDK 层发起 HTTP 请求的问题及解决
    Android NDK 层发起 HTTP 请求的问题及解决
    前言新的一年,大家新年快乐~~鸡年大吉!本次给大家带来何老师的最新文章~虽然何老师还在过节,但依然放心不下广大开发者,在此佳节还未结束之际,给大家带来最新的技术分享~ 事件的起因不说了,总之是需要实现一个 NDK 层的网络请求。为了多端适用,还是选择
  • Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突
    Android插件化(六): OpenAtlasの改写aapt以防
    引言Android应用程序的编译中,负责资源打包的是aapt,如果不对打包后的资源ID进行控制,就会导致插件中的资源ID冲突。所以,我们需要改写aapt的源码,以达到通过某种方式传递资源ID的Package ID,通过aapt打包时获取到这个Package ID并且应用才插件资源的命名
    02-05 安卓开发
  • Android架构(一)MVP架构在Android中的实践
    Android架构(一)MVP架构在Android中的实践
    为什么要重视程序的架构设计 对程序进行架构设计的原因,归根结底是为了 提高生产力 。通过设计是程序模块化,做到模块内部的 高聚合 和模块之间的 低耦合 (如依赖注入就是低耦合的集中体现)。 这样做的好处是使得程序开发过程中,开发人员主需要专注于一点,
    02-05 安卓开发
  • 安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件 作者: 飞龙 这个教程中我们要分析一个锁机软件。像这种软件都比较简单,完全可以顺着入口看下去,但我这里还是用关键点来定位。首先这个软件的截图是这样,进入这个界面之后,除非退出模拟器,否则没办法回到桌面。上面那
    02-05 安卓开发
  • Android插件化(二):OpenAtlas插件安装过程分析
    Android插件化(二):OpenAtlas插件安装过程分析
    在前一篇博客 Android插件化(一):OpenAtlas架构以及实现原理概要 中,我们对应Android插件化存在的问题,实现原理,以及目前的实现方案进行了简单的叙述。从这篇开始,我们要深入到OpenAtlas的源码中进行插件安装过程的分析。 插件的安装分为3种:宿主启动时立
    02-05 安卓开发
  • [译] Android API 指南
    [译] Android API 指南
    众所周知,Android开发者有中文网站了,API 指南一眼看去最左侧的菜单都是中文,然而点进去内容还是很多是英文,并没有全部翻译,我这里整理了API 指南的目录,便于查看,如果之前还没有通读,现在可以好好看一遍。注意,如果标题带有英文,说明官方还没有翻
  • 使用FileProvider解决file:// URI引起的FileUriExposedException
    使用FileProvider解决file:// URI引起的FileUri
    问题以下是一段简单的代码,它调用系统的相机app来拍摄照片:void takePhoto(String cameraPhotoPath) {File cameraPhoto = new File(cameraPhotoPath);Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);takePhotoIntent.putExtra(Medi
    02-05 安卓开发
点击排行