58同城厂商内置包大小减少实战

本文介绍了几种减少包大小的技术方案,希望能够帮到你。

《虞美人·春花秋月何时了》
春花秋月何时了?往事知多少。
小楼昨夜又东风,故国不堪回首月明中。
雕栏玉砌应犹在,只是朱颜改。
问君能有几多愁?恰似一江春水向东流。
-五代,李煜

前言

何为厂商内置包?厂商内置包是公司和厂商之间的一种商务合作,可以将应用内置到出厂的手机上,可以带来新增用户和提升日活。

58同城每年都会基于线上最新版本按照厂商的要求做出厂商内置包,毕竟会占用用户的存储空间,所以厂商对于包大小有着严格的要求,随着时间的推进和应用版本不断的迭代更新,应用大小变得越来越大,满足厂商包大小的要求也越来越困难。

备注:应用通常会预装到较高系统版本的手机上,例如android 9.0及以上版本,所以我们只需要考虑android 9.0及以上版本的兼容性即可。

Apk文件结构及大小占比

简单介绍下Apk文件结构:

  • AndroidManifest.xml :用于声明应用包名信息、相关的使用权限、四大组件、android sdk版本等
  • res目录:包含程序主要资源文件,包括res/layout、res/drawable等,主要是布局文件和图片等资源。
  • resources.arsc:资源映射表,包含一部分编译资源(res/value)和资源路径, 可以通过资源R.java中的ID映射到对应的资源文件或者值
  • assets目录:包含不被压缩的原始文件,主要是一些多媒体文件,或者附加文件
  • lib目录:使用到的各种.so链接库
  • classes.dex:打包好的程序代码,如果是复杂的应用会进行分包,包含多个dex文件
  • META-INF目录:主要包含APK的签名信息
  • MANIFEST.MF:清单文件,当前APK中所有文件清单及其对应hash值
  • CERT.SF:上述清单文件中的每条信息的hash值
  • CERT.RSA:对CERT.SF文件的数字签名以及签名时所用的数字证书

从上图可以清晰地看到Apk内容大小占比,主要是图片资源和lib库so文件比较大,其次是resources.arsc文件,该文件中存放了R.java文件中的id值和资源文件名及资源文件路径之间的映射,其中string和dimen及color等存放的是相应值。

保留指定资源

先来看一下如何配置,在gradle文件中配置我们需要保留的资源。

android {
    defaultConfig {
        ...
        resConfigs "zh-rCN", "xxhdpi", "ldltr", "desk"
    }
}

接下来对上述配置进行一一说明,使用Android Studio可以查看resources.arsc文件中的内容,从下图可以看到应用依赖的support库中资源竟然包含88种语言配置,显然我们只需要中文资源即可。

通过添加zh-rCN配置用于只保留中文资源,添加上这个配置后竟然减少了1M的大小。下图是应用配置后的结果:

添加xxhdpi配置用于只保留一套图片资源,例如如果有xhdpi和xxhdpi的两套图片,会只保留xxhdpi的图片,如果有hdpi和xhdpi两套图片,会只保留xhdpi的图片。

如果应用没有适配RTL(从右到左)布局,可以在清单文件添加以下属性禁止RTL布局方式:

<application
  ...
  android:supportsRtl="false" />

如果应用使用了react native,可以添加下面的代码禁用RTL布局:

//禁止RN页面RTL
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
sharedI18nUtilInstance.allowRTL(mApplication, false);

相应的RTL的资源也可以通过添加ldltr配置移除。

如果应用没有适配watch手表,可以通过添加desk配置将watch相关资源移除。

图片转为WebP

Android从4.3开始对于WebP的decode、encode是完全支持的,包括半透明的WebP图片。所以可以使用Android Studio自带的工具将工程内的图片转换为WebP,压缩比可以采用75%。

工程内的图片可以手动转换,对于依赖的第三库中图片怎么办?

可以使用开源库https://github.com/smallSohoSolo/McImage,McImage可以在打包过程中对图片进行压缩,也可以将图片转换为WebP格式。

自动化清理无用资源

通过lint可以检测出工程无用资源的结果,使用python脚本可以自动化清理无用资源。传送门:https://github.com/yuweiguocn/android-resource-remover

使用lint检查无用资源

通常我们可以使用lint检查工程内无用资源,但执行lint命令发现只检查了主Module的无用资源,并没有检查子Module的无用资源。

针对这个问题可以使用下面的配置,对应用的依赖也执行检查:

lintOptions {
    checkReleaseBuilds false
    abortOnError false
    checkDependencies true //对依赖的资源也执行检查,注意也会对引用aar检查,如果是子module打开源码依赖即可
    check "UnusedResources" //只检查无用资源,提升执行速度
}

然后执行lint命令进行检查无用资源:

./gradlew lint

然后我们可以在build下的reports目录下找到lint的结果。

配置

首先将本工程clone到你的项目工程的根目录中,然后根据你项目使用反射获取的资源在resouceCleanConfig.json文件中进行配置:

{
    "projects": [
        "/app/",
        "/push/"
    ],
    "pathIncludes": [
        ".jpg",
        ".png",
        ".xml"
    ],
    "pathExcludes": [
        "------common-config-start--------",
        "/layout/"
    ]
}
  • projects:表示要清理无用资源的路径需要包含的子Module工程名称,因为依赖检查也会对引用的所有aar进行检查,所以我们需要指定打开源码的子Module的名称。
  • pathIncludes:表示要清理的无用资源的路径需要匹配的规则。
  • pathExcludes:表示要清理的无用资源的路径不能包含的字符串,也就是白名单。

以上三个条件为并列条件,只有同时满足才会被清理。上述json配置的文件表示清理app和push Module下文件后缀为.jpg或.png或xml的资源,但不删除布局文件。

白名单

目前发现工程使用代码获取资源的方法有以下两种,代码中使用下列方法获取的图片名称需要添加配置文件白名单中。

//第一种
item.resId = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
//第二种
public static Integer getResourceDrawableId(String resource) {
    if(TextUtils.isEmpty(resource)){
        return -1;
    }
    try {
        String name = resource.trim();
        Field field = R.drawable.class.getField(name);
        return field.getInt(null);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return -1;
}

建议对新增的资源在使用代码获取时对名称添加统一前缀reflect_

自动化

此步骤请谨慎操作,删除的资源无法恢复,使用前请添加版本控制。

配置好后执行如下命令自动清理无用资源:

python android_clean_app.py --xml ../app/build/reports/lint-results.xml

参数--xml指定为你的lint结果文件的路径。

解决打包失败问题

lint的检测结果包含无用代码对资源的引用,如果只是将无用资源删除后可能会引起打包失败。
针对这个问题我们会自动添加一个unused_ids.xml文件保存删除的资源id,这不仅可以保证不会由于删除无用资源引起的打包失败,也对自动清理的无用资源进行了记录。


运行结果

资源混淆

除上述方案你也可以使用腾讯开源的资源混淆,资源混淆会对资源路径及名称进行混淆,可以减少resources.arsc文件大小,通过7zip压缩及移除重复资源减少APK大小。

采用资源混淆需要对工程内使用反射获取的资源添加白名单,如果是较小的工程修改和测试成本相对较低,如果是多业务线多Module的工程修改和测试成本相对较高,这时可以通过修改资源混淆的源码,保留资源名称,只对资源路径进行混淆,确保在减少包大小的同时不会对业务功能产生影响。

总结

58同城厂商内置包使用了上述的减少包大小的方案,配合移除so文件较大的依赖库功能,最终满足了厂商对包大小的要求。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容