App 塑体最好施行 – Android – 丹佛掘金,在初阶讲消脂才能在此之前

专访DroidPlugin小编张勇:安卓黑科学技术是怎么炼成的 – Android – 丹佛掘金(Denver Nuggets)

上三个月,奇虎360在Github上发表了一个Android开源项目DroidPlugin,那是二个贯彻动态加载的Android插件框架,可避防安装、免予修业改的周转第三方APK。一时半刻间,它被誉为安卓黑科学和技术,引起行当内的关注。
据其法定文书档案介绍,DroidPlu…

近年一向在钻探有关apk节食方面包车型大巴学识,看了大多作品受益匪浅。最初的小说地址
http://tech.meituan.com/android-shrink-overall-solution.html

微信 Android 能源混淆打包工具-安装包立减1M – Android – 掘金队

上1篇作品大家讲述了Android缩短设置包体量的局地tips,本文首要对前文提到的能源混淆做3个简便的分析。微信中的资源混淆工具关键为了混淆资源ID长度(比如将res/drawable/welcome.png混淆为r/s/a.png),同时采取七z深度压缩,…

趁着专门的工作的急忙迭代拉长,美团App里不停引进新的业务逻辑代码、图片财富和第二方SDK,直接产生APK体量不断巩固。包容积拉长带来的主题材料尤为多,如CDN流量成本扩展、用户设置成功率下降,以至恐怕会影响用户的留存率。APK的减脂已经是不得不思念的作业。在品味瘦腿的经过中,大家借鉴了数不清产业界其余厂商提供的方案,同时也针对本身特点,开掘了一些新的手艺。本文将对内部的一些做详细介绍。

Android Proguard 指南 – Android – 掘金

android 混淆配置指南英文版 自己只是个名不见经传翻译的搬运工
,好啊,翻译是为了拉长英文水准。 Android Proguard 指南 Android
Proguard 混淆配置指南 ProGuard 这些ProGuard工具得以经过删…

在上马讲减重技术此前,先来讲一下APK的结合。

「个人总括」APK 减重奉行 – Android – 丹佛掘金队

因为推广的内需,公司索要把APK的大小再“减小”一下,4M以内!当到达四M之内之后,公司提议说,能或不能够再压压?2M怎么?
节食前因为平日就思考到大小的限量,所以广大做事早已做过了,如下列举以后的动静:
7.三M(Debug版本)和陆.5M(Release版本…

能够用Zip工具张开APK查看。举个例子,美团App 7.八.6的线上版本的格式是这么的:

新一代 Android 渠道打包工具:一千 个路子包只供给 伍 秒 – Android – 丹佛掘金(Denver Nuggets)

源码:https://github.com/mcxiaoke/packer-ng-plugin
最新版本 v壹.0.四 – 2016.01.1玖 – 完善获取APK路径的主意,扩充MarketInfo
v1.0.三 – 二零一六.01.1四 – …

图片 1

App 减腹最好实施 – Android – 丹佛掘金队

本文少禽不定时更新,推荐watch下项目。假诺喜欢请star,假诺感觉有纰漏请提交issue,如若你有越来越好的纽带能够交到pull
request。本文的演示代码首即使依附属小学编的经验来编排的,若您有其它的手艺和格局能够涉足进来一同完善那篇文章。
本文固定连接:ht…

能够看看APK由以下保养部分组成:

水果 价格
lib/ 存放so文件,可能会有armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips,大多数情况下只需要支持armabi与x86的架构即可,如果非必需,可以考虑拿掉x86的部分
res/ 存放编译后的资源文件,例如:drawable、layout等等
assets/ 应用程序的资源,应用程序可以使用AssetManager来检索该资源
META-INF/ 该文件夹一般存放于已经签名的APK中,它包含了APK中所有文件的签名摘要等信息
classes.dex classes文件是Java Class,被DEX编译后可供Dalvik/ART虚拟机所理解的文件格式
resources.arsc 编译后的二进制资源文件
AndroidManifest.xml Android的清单文件,格式为AXML,用于描述应用程序的名称、版本、所需权限、注册的四大组件

本来还会有壹部分别样的文本,比如上图中的org/,src/,push_version等公事或文件夹。这几个能源是Java
Resources,感兴趣的能够组成编写翻译职业流中的流程图以及MergeJavaResourcesTransform的源码看看被打入APK包中的能源都有如何,那里不做过多介绍。

在足够精通了APK种种组成都部队分以及它们的功能后,大家本着我特色开始展览通晓析和优化。下边将从Zip文件格式、classes.dex、财富文件、resources.arsc等方面来介绍下大家发掘的局地优化技能。

前面介绍了APK的文件格式以及关键组成都部队分,通过aapt l -v xxx.apk或unzip -l
xxx.apk来查看APK文件时会获得以下音信,见上面截图:

图片 2

透过上海教室能够看出APK中众多财富是以Stored来囤积的,根据Zip的文件格式中对裁减情势的叙述Compression_methods可以见到这一个文件是尚未滑坡的,这为啥它们并未有被压缩呢?从AAPT的源码中找到以下描述:

/* these formats are already compressed, or don't compress well */static const char* kNoCompressExt[] = { ".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg", ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"};

能够看到AAPT在能源管理时对那些文件后缀类型的能源是不做缩减的,那是或不是能够修改它们的收缩方式由此完结减肥的效用啊?

在介绍怎么办事先,先来大约介绍一下App的财富是怎么被打进APK包里的。Android创设筑工程具链使用AAPT工具来对能源开始展览管理,来看下图(图片来源于Build
Workflow):

图片 3经过上海教室能够看到Manifest、Resources、Assets的财富通过AAPT管理后生成安德拉.java、Proguard
Configuration、Compiled
Resources。其中牧马人.java我们都相比较熟练,那里就只是多介绍了。大家来入眼看看Proguard
Configuration、Compiled Resources都以做如何的吗?

  • Proguard
    Configuration是AAPT工具为Manifest中宣称的四大组件以及布局文件中(XML
    layouts)使用的种种Views所生成的ProGuard配置,该文件一般存放在${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/proguard-rules/${flavorName}/${buildType}/aapt_rules.txt,上边是项目中该文件的截图,红框标志出来的便是对AndroidManifest.xml、XML
    Layouts中相关Class的ProGuard配置。

    图片 4

  • Compiled
    Resources是叁个Zip格式的文件,这一个文件的渠道平日为${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/res/resources-${flavorName}-${buildType}-stripped.ap_。
    通过上边通过Zip解压后的截图,能够看来这几个文件蕴含了res,AndroidManifest.xml和resources.arsc的文件或文件夹。结合Build
    Workflow中的描述,能够观望那个文件(resources-${flavorName}-${buildType}-stripped.ap_)会被apkbuilder打包到APK包中,它实质上便是APK的“财富包”(res,AndroidManifest.xml和resources.arsc)。图片 5大家即便通过那么些文件来修改不相同后缀文件财富的回落形式来达到节食成效的,而在后边“resources.arsc的优化”一节中也是操作的那个文件。

作者在本人的体系中是由此在package${flavorName}Task(感兴趣的同窗能够查阅源码)以前举办那个操作的。

上边是部分代码片段:

appPlugin.variantManager.variantDataList.each { variantData -> variantData.outputs.each { def sourceApFile = it.packageAndroidArtifactTask.getResourceFile(); def destApFile = new File("${sourceApFile.name}.temp", sourceApFile.parentFile); it.packageAndroidArtifactTask.doFirst { byte[] buf = new byte[1024 * 8]; ZipInputStream zin = new ZipInputStream(new FileInputStream(sourceApFile)); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(destApFile)); ZipEntry entry = zin.getNextEntry(); while (entry != null) { String name = entry.getName(); // Add ZIP entry to output stream. ZipEntry zipEntry = new ZipEntry; if (ZipEntry.STORED == entry.getMethod() && !okayToCompress(entry.getName { zipEntry.setMethod(ZipEntry.STORED) zipEntry.setSize(entry.getSize zipEntry.setCompressedSize(entry.getCompressedSize zipEntry.setCrc(entry.getCrc } else { zipEntry.setMethod(ZipEntry.DEFLATED) ... } ... out.putNextEntry; out.closeEntry(); entry = zin.getNextEntry(); } // Close the streams zin.close(); out.close(); sourceApFile.delete(); destApFile.renameTo(sourceApFile); } }}

理所当然也得以在其它构建步骤中使用更加高压缩率的法子来落成塑身功效,举个例子使用7Zip压缩等等。

本工夫的运用须要小心以下难点:

  • 假设音录制财富被压缩存放在APK中的话,在选择一些旋律、摄像API时特别要留心,必要盘活充足的测试。
  • resources.arsc文件最棒永不压缩存款和储蓄,假若缩减会潜移默化确定的属性。
  • 设若想在Android
    陆.0上开启android:extractNativeLibs=”false”的话,.so
    文件也不可能被削减,android:extractNativeLibs的选择姿势看那里:App
    Manifest — application。

什么优化classes.dex的高低呢?轮廓有如下套路:

  • 无时无刻保持突出的编制程序习贯和对包体积敏锐的嗅觉,去除重复也许不用的代码,慎用第三方库,选取容积小的第2方SDK等等。
  • 张开ProGuard来举行代码压缩,通过使用ProGuard来对代码实行模糊、优化、压缩等专业。

本着第1种套路,因各种集团的品类的差距,共性的东西较少,须要case by
case的分析,那里不做过多的介绍。

可以通过开启ProGuard来促成代码压缩,能够在build.gradle文件相应的营造类型中增加minifyEnabled
true。

  • 请留意,代码压缩会拖慢营造速度,由此应当尽量幸免在调解创设中运用。不太早晚要为用于测试的最后APK启用代码压缩,假设不可能尽量地自定义要封存的代码,恐怕会引进错误。

譬如说,上边那段出自build.gradle文件的代码用于为发表创设启用代码压缩:

android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile(‘proguard-android.txt'), 'proguard-rules.pro' } } ...}

除此之外minifyEnabled属性外,还有用于定义ProGuard规则的proguardFiles属性:

  • getDefaultProguardFile(‘proguard-android.txt’)是从Android
    SDKtools/proguard/文件夹到手暗中同意ProGuard设置。
  • proguard-rules.pro文件用于增多自定义ProGuard规则。暗中同意景况下,该公文位于模块根目录(build.gradle文件旁)。

提示:要想做进一步的代码压缩,可尝试采纳位于同一职位的proguard-android-optimize.txt文件。它归纳同样的ProGuard规则,但还包括其余在字节码拔尖举办分析的优化,以特别减小APK大小和补助提高其运营速度。

在Gradle Plugin
贰.二.0及以上版本ProGuard的安顿文件会自动解压缩到${rootProject.buildDir}/${AndroidProject.FD_INTEXC60MEDIATES}/proguard-files/目录下,proguardFiles会从这么些目录来获取ProGuard配置。

每便实践完ProGuard之后,ProGuard都会在${project.buildDir}/outputs/mapping/${flavorDir}/生成以下文件:

文件名 描述
dump.txt APK中所有类文件的内部结构
mapping.txt 提供原始与混淆过的类、方法和字段名称之间的转换,可以通过proguard.obfuscate.MappingReader来解析
seeds.txt 列出未进行混淆的类和成员
usage.txt 列出从APK移除的代码

能够经过在usage.txt文件中看出哪些代码被去除了,如下图中所示android.support.multidex.MultiDex已经被删去了:

图片 6

而外对项目代码优化和开启代码压缩之外,小编在《美团Android
DEX自动拆包及动态加载简要介绍》那篇文章中涉及了通过内联哈弗 Field来化解TiggoField过多导致MultiDex
6553陆的主题材料,而这一步骤对代码消脂能够起到显然的机能。下边是作者通过字节码工具在营造流程中内联CRUISERField的代码片段(字节码的退换能够应用Javassist或然ASM,该步骤小编使用的是Javassist)。

ctBehaviors.each { CtBehavior ctBehavior -> if (!ctBehavior.isEmpty { try { ctBehavior.instrument(new ExprEditor() { @Override public void edit(FieldAccess f) { try { def fieldClassName = JavassistUtils.getClassNameFromCtClass(f.getCtClass if (shouldInlineRField(className, fieldClassName) && f.isReader { def temp = fieldClassName.substring(fieldClassName.indexOf(ANDROID_RESOURCE_R_FLAG) + ANDROID_RESOURCE_R_FLAG.length def fieldName = f.fieldName def key = "${temp}.${fieldName}" if (resourceSymbols.containsKey { Object obj = resourceSymbols.get try { if (obj instanceof Integer) { int value =  obj).intValue() f.replace("\$_=${value};") } else if (obj instanceof Integer[]) { def obj2 = ((Integer[]) obj) StringBuilder stringBuilder = new StringBuilder() for (int index = 0; index < obj2.length; ++index) { stringBuilder.append(obj2[index].intValue if (index != obj2.length - 1) { stringBuilder.append } } f.replace("\$_ = new int[]{${stringBuilder.toString } else { throw new GradleException("Unknown ResourceSymbols Type!") } } catch (NotFoundException e) { throw new GradleException(e.message) } catch (CannotCompileException e) { throw new GradleException(e.message) } } else { throw new GradleException("******** InlineRFieldTask unprocessed ${className}, ${fieldClassName}, ${f.fieldName}, ${key}") } } } catch (NotFoundException e) { } } }) } catch (CannotCompileException e) { } }}

本着代码的减重还有大多优化的才具,比如:

  • 减去ENUM的选用(实际情况能够参见:Remove
    Enumerations),每减少1个ENUM能够减去大概1.0到一.四 KB的大小;
  • 经过pmd cpd来检查重复的代码从而进行代码优化;
  • 移除掉全体无用也许功用重新的正视库。

那几个优化本领就不开始展览介绍了。

为了帮忙Android设备DPI的多种化([l|m|tv|h|x|xx|xxx]dpi)以及用户对高素质UI的指望,美团App中动用了大批量的图片,在Android下补助广大格式的图纸,例如:PNG、JPG
、WebP,那大家该怎么取舍差别类型的图片格式呢? 在谷歌 I/O
201陆中涉嫌了针对性图片格式的选拔,来看下图(图片来源Image compression
for Android developers):

图片 7通过上海图书馆能够看来1个差不离图片格式采用的法子。若是能用VectorDrawable来表示的话优先选拔VectorDrawable,假使帮忙WebP则优先用WebP,而PNG重要用在彰显透明恐怕轻便的图样,而其他场景能够选取JPG格式。针对各类图片格式也有每一项的优化花招和优化学工业具。

能够应用矢量图形来创建独立于分辨率的Logo和其他可伸缩图片。使用矢量图片能够有效的压缩App中图纸所占用的高低,矢量图形在Android中意味为VectorDrawable对象。
使用VectorDrawable对象,十0字节的文书能够生成荧屏大小的明领悟白图像,但系统渲染每种VectorDrawable对象须求大批量的岁月,相当的大的图像需求更加长的小时才干出现在显示器上。
因而唯有在显示小图像时才考虑选择矢量图形。有关使用VectorDrawable的愈来愈多音信,请参阅
Working with Drawables。

如果App的minSdkVersion高于1四(Android
四.0+)的话,能够选拔WebP格式,因为WebP在同画质下容积更加小(WebP扶助反射率,压缩比比JPEG越来越高但显示效果却不输于JPEG,官方评测quality参数等于75平衡最好),
能够透过PNG到WebP调换工具来举办更改。当然Android从肆.0才开首WebP的原生扶助,不过不帮助包括光滑度,直到Android
四.二.一+才支撑显得含反射率的WebP,在作者使用中是推断当前App的minSdkVersion以及图片文件的档案的次序来接纳是不是适用WebP。见上面包车型大巴代码片段:

boolean isPNGWebpConvertSupported() { if (!isWebpConvertEnable { return false } // Android 4.0+ return GradleUtils.getAndroidExtension.defaultConfig.minSdkVersion.apiLevel >= 14 // 4.0}boolean isTransparencyPNGWebpConvertSupported() { if (!isWebpConvertEnable { return false } // Lossless, Transparency, Android 4.2.1+ return GradleUtils.getAndroidExtension.defaultConfig.minSdkVersion.apiLevel >= 18 // 4.3}def convert() { String resPath = "${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/res/merged/${variant.dirName}" def resDir = new File("${resPath}") resDir.eachDirMatch(~/drawable[a-z0-9-]*/) { dir -> FileTree tree = project.fileTree tree.filter { File file -> return (isJPGWebpConvertSupported() && (file.name.endsWith(SdkConstants.DOT_JPG) || file.name.endsWith(SdkConstants.DOT_JPEG))) || (isPNGWebpConvertSupported() && file.name.endsWith(SdkConstants.DOT_PNG) && !file.name.endsWith(SdkConstants.DOT_9PNG)) }.each { File file -> def shouldConvert = true if (file.name.endsWith(SdkConstants.DOT_PNG)) { if (!isTransparencyPNGWebpConvertSupported { shouldConvert = !Imaging.getImageInfo.isTransparent() } } if (shouldConvert) { WebpUtils.encode(project, webpFactorQuality, file.absolutePath, webp) } } }}

能够使用pngcrush、pngquant或zopflipng等压缩工具来压缩PNG文件大小,而不会丢掉图像品质。全数这几个工具都能够减去PNG文件大小,同时保证图像品质。

pngcrush工具尤其实用:此工具在PNG过滤器和zlib参数上迭代,使用过滤器和参数的各样组合来压缩图像。然后采纳产生最小压缩输出的配备。

对于JPEG文件,你能够利用packJPG或guetzli等工具将JPEG文件收缩的越来越小,那么些工具能够在保持图片品质不改变的意况下,把图片文件收缩的越来越小。guetzli工具越来越能够在图纸质量不改变的情状下,将文件大小降低35%。

在Android塑造流程中AAPT会利用内置的压缩算法来优化res/drawable/目录下的PNG图片,但也恐怕会造费用来早就优化过的图形容积变大,能够经过在build.gradle中设置cruncherEnabled来禁止AAPT来优化PNG图片。

aaptOptions { cruncherEnabled = false}

Android的编写翻译工具链中提供了1款能源缩短的工具,能够透过该工具来减少能源,假若要启用能源缩小,能够在build.gradle文件少将shrinkResources
true。举个例子:

android { ... buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }}

亟需留意的是日前财富压缩器如今不会移除values/文件夹中定义的财富(比如字符串、尺寸、样式和颜料),有关实际情况,请参阅问题7086九。

Android营造筑工程具是经过ResourceUsageAnalyzer来检查哪些能源是行不通的,当检查到不行的能源时会把该能源替换到预订义的版本。详看上面代码片段(摘自com.android.build.gradle.tasks.ResourceUsageAnalyzer):

public class ResourceUsageAnalyzer { ... /** * Whether we should create small/empty dummy files instead of actually * removing file resources. This is to work around crashes on some devices * where the device is traversing resources. See http://b.android.com/79325 for more. */ public static final boolean REPLACE_DELETED_WITH_EMPTY = true; // A 1x1 pixel PNG of type BufferedImage.TYPE_BYTE_GRAY public static final byte[] TINY_PNG = new byte[] { -119,  80,  78,  71,  13,  10,  26,  10,  0,  0,  0,  13,  73,  72,  68,  82,  0,  0,  0,  1,  0,  0,  0,  1,  8,  0,  0,  0,  0,  58,  126, -101,  85,  0,  0,  0,  10,  73,  68,  65,  84,  120,  -38,  99,  96,  0,  0,  0,  2,  0,  1,  -27,  39,  -34,  -4,  0,  0,  0,  0,  73,  69,  78,  68,  -82,  66,  96, -126 }; public static final long TINY_PNG_CRC = 0x88b2a3b0L; // A 3x3 pixel PNG of type BufferedImage.TYPE_INT_ARGB with 9-patch markers public static final byte[] TINY_9PNG = new byte[] { -119,  80,  78,  71,  13,  10,  26,  10,  0,  0,  0,  13,  73,  72,  68,  82,  0,  0,  0,  3,  0,  0,  0,  3,  8,  6,  0,  0,  0,  86,  40,  -75,  -65,  0,  0,  0,  20,  73,  68,  65,  84,  120,  -38,  99,  96, -128, -128,  -1,  12,  48,  6,  8,  -96,  8, -128,  8,  0, -107, -111,  7,  -7,  -64,  -82,  8,  0,  0,  0,  0,  0,  73,  69,  78,  68,  -82,  66,  96, -126 }; public static final long TINY_9PNG_CRC = 0x1148f987L; // The XML document <x/> as binary-packed with AAPT public static final byte[] TINY_XML = new byte[] {  3,  0,  8,  0,  104,  0,  0,  0,  1,  0,  28,  0,  36,  0,  0,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  32,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  120,  0,  2,  1,  16,  0,  36,  0,  0,  0,  1,  0,  0,  0,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  0,  0,  0,  0,  20,  0,  20,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  1,  16,  0,  24,  0,  0,  0,  1,  0,  0,  0,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  0,  0,  0,  0 }; public static final long TINY_XML_CRC = 0xd7e65643L; ...}

地方截图中三个byte数组的定义正是能源压缩工具为无效财富提供的预订义版本,能够见见对.png提供了TINY_PNG,
对.9.png提供了TINY_9PNG以及对.xml提供了TINY_XML的预约义版本。

能源压缩工具的详实使用能够参照Shrink Your Code and
Resources。财富压缩工具私下认可是使用安全压缩方式来运行,能够通过开启严峻压缩方式来完结更好的消肉效果。

举例想清楚什么样能源是行不通的,能够透过能源压缩工具的出口日志文件${project.buildDir}/outputs/mapping/release/resources.txt来查看。如下图所示res/layout/abc_activity_chooser_viewer.xml就是船到江心补漏迟的,然后被预约义的本子TINY_XML所替换:

图片 8

能源压缩工具只是把无用财富替换到预约义相当小的版本,那我们什么删除这个不算财富呢?平日的做法是结合财富压缩工具的出口日志,找到那几个财富并把它们举行删除。但在小编的品种中很多失效财富是被其余组件或第一方SDK所引进的,假使利用那种优化措施会带来那些SDK早先时期维护资金财产的扩张,针对那种情状小编是透过利用在resources.arsc中做优化来缓慢解决的,详细情况看下边“resources.arsc的优化”一节的介绍。

遵照App自个儿补助的言语版本选拔合适的言语财富,举例使用了AppCompat,要是不做其余配置来讲,最后APK包中会包含AppCompat中国国投息的具备已翻译语言字符串,无论接纳的别的部分是不是翻译为同一语言,能够透过resConfig来配置使用什么语言,从而让营造筑工程具移除内定语言之外的具备能源。下图是现实的配备示范:

android { ... defaultConfig { ... resConfigs "zh", "zh-rCN" } ...}

本着为区别DPI所提供的图纸也能够运用同样的政策,需要针对本人的目的用户和目的设备做明确的选拔,能够参考Support
Only Specific Densities来操作。有关显示器密度的详细音信,请参阅Screen
Sizes and
Densities。对.so文件也得以使用类似的政策,比如作者的项目中只保留了armeabi版本的.so文件。

针对resources.arsc,笔者尝试过的优化花招如下:

  • 敞开能源混淆;
  • 对重复的能源进行优化;
  • 对被shrinkResources优化掉的财富拓展拍卖。

上面将分头对那一个优化花招开始展览拓展介绍。

在小编另壹篇《美团Android财富混淆爱护实行》小说中介绍了利用对能源混淆的点子来保卫安全能源的张家界,同时也关系了那种形式有断定的减肥功用。作者当时是行使修改AAPT的相关源码的章程,那种办法的痛点是每趟晋级Build
Tools都要修改1遍AAPT源码,维护性较差。近期作者利用了微信开源的能源混淆库AndResGuard,具体的法则和动用帮衬能够参考安装包立减1M–微信Android财富混淆打包工具。

在上一节中介绍了能够通过shrinkResources
true来开启能源减弱,能源压缩工具会把无用的财富替换到预约义的本子而不是移除,假如运用人造移除的方法会带来中期的维护开销,那里小编使用了1种相比取巧的法子,在Android营造筑工程具实施package${flavorName}Task从前经过修改Compiled
Resources来促成活动删除无用财富。

实际流程如下:

  • 采撷财富包(Compiled
    Resources的简称)中被交流的预约义版本的能源名称,通过查阅能源包中每种ZipEntry的CCR-VC-3贰checksum来搜寻被替换的预订义能源,预约义财富的CCR-VC-3二定义在ResourceUsageAnalyzer,下边是它们的定义。

// A 1x1 pixel PNG of type BufferedImage.TYPE_BYTE_GRAY public static final long TINY_PNG_CRC = 0x88b2a3b0L; // A 3x3 pixel PNG of type BufferedImage.TYPE_INT_ARGB with 9-patch markers public static final long TINY_9PNG_CRC = 0x1148f987L; // The XML document <x/> as binary-packed with AAPT public static final long TINY_XML_CRC = 0xd7e65643L;
  • 通过android-chunk-utils把resources.arsc中对应的概念移除;
  • 删去财富包中对应的能源文件。

眼下美团App是由种种业务公司共同开辟完成,为了便利各工作共青团和少先队的单身开拓,美团App实行了平台化更换。改造时存在许多能源文件(如:drawable、layout等)被分裂的事体公司都拷贝到本人的Library下,同时为了制止引发财富覆盖的难题,每一个事情公司都会为温馨的财富文件名增添前缀。那样就招致了这么些能源文件尽管内容同样,但因为名称的两样而不能被掩盖,最后都会被合并到APK包中,针对那种题材小编使用了和眼下“无用能源优化”1节中讲述类似的政策。

具体步骤如下:

  • 由此财富包中的各种ZipEntry的CLANDC-3贰 checksum来筛选出重新的财富;
  • 由此android-chunk-utils修改resources.arsc,把这几个再一次的财富都重定向到同二个文书上;
  • 把其余重复的财富文件从财富包中删除。

代码片段:

variantData.outputs.each { def apFile = it.packageAndroidArtifactTask.getResourceFile(); it.packageAndroidArtifactTask.doFirst { def arscFile = new File(apFile.parentFile, "resources.arsc"); JarUtil.extractZipEntry(apFile, "resources.arsc", arscFile); def HashMap<String, ArrayList<DuplicatedEntry>> duplicatedResources = findDuplicatedResources; removeZipEntry(apFile, "resources.arsc"); if (arscFile.exists { FileInputStream arscStream = null; ResourceFile resourceFile = null; try { arscStream = new FileInputStream; resourceFile = ResourceFile.fromInputStream(arscStream); List<Chunk> chunks = resourceFile.getChunks(); HashMap<String, String> toBeReplacedResourceMap = new HashMap<String, String>; // 处理arsc并删除重复资源 Iterator<Map.Entry<String, ArrayList<DuplicatedEntry>>> iterator = duplicatedResources.entrySet().iterator(); while (iterator.hasNext { Map.Entry<String, ArrayList<DuplicatedEntry>> duplicatedEntry = iterator.next(); // 保留第一个资源,其他资源删除掉 for (def index = 1; index < duplicatedEntry.value.size(); ++index) { removeZipEntry(apFile, duplicatedEntry.value.get.name); toBeReplacedResourceMap.put(duplicatedEntry.value.get.name, duplicatedEntry.value.get; } } for (def index = 0; index < chunks.size(); ++index) { Chunk chunk = chunks.get; if (chunk instanceof ResourceTableChunk) { ResourceTableChunk resourceTableChunk = (ResourceTableChunk) chunk; StringPoolChunk stringPoolChunk = resourceTableChunk.getStringPool(); for (def i = 0; i < stringPoolChunk.stringCount; ++i) { def key = stringPoolChunk.getString; if (toBeReplacedResourceMap.containsKey { stringPoolChunk.setString(i, toBeReplacedResourceMap.get; } } } } } catch (IOException ignore) { } catch (FileNotFoundException ignore) { } finally { if (arscStream != null) { IOUtils.closeQuietly(arscStream); } arscFile.delete(); arscFile << resourceFile.toByteArray(); addZipEntry(apFile, arscFile); } } }}

透过那种措施能够使得削减腹复财富对包体大小的震慑,同时那种操作方法对各专门的学问团队透明,也不会增添和睦壹致能源怎么着被区别职业团队复用的开支。

上述正是我们当前在APK减肥方面包车型客车做的有的品尝和积存,能够依据笔者情状选用使用。当然大家还能运用部分按需加载的攻略来压缩安装包的体量。最后提一点,砍掉不要求的法力才是安装包消脂的超级大招。一个好的App的标准有不知凡四个人置,但提供尽也许小的安装包是中间2个重要的方面,那也是对我们Android开辟者人士本身的建议的骨干供给,要时时保持出色的编制程序习于旧贯和对包体积敏锐的嗅觉。

相关文章

网站地图xml地图