一、 前言
在做安卓项目中不可避免地遇到非常多需要增删改查图片资源的情况,如果这个时候图片资源命名混乱不堪,毫无规则就会让自己写得很难受,别人看得更难受,后人改得脸消瘦。所以,良好的图片资源命名规范与资源优化对于项目的维护和开发效率至关重要。因此本篇文章会着重讲解安卓图片素材资源的规范与优化混淆等。
二、 安卓资源文件目录解析
在Android开发中,res
文件夹是一个非常重要的目录,它用于存储所有的非代码资源,比如布局(layouts)、字符串(strings)、图片(drawables)和动画(animations)等。这些资源被组织在不同的子文件夹中,每种类型的资源对应一个特定的子文件夹。以下是res
文件夹下一些常见的子文件夹及其含义: 1. drawable文件夹: 用于存放图片资源文件,如PNG、JPG、SVG等。可以有多个drawable文件夹,以支持不同的屏幕密度(如drawable-hdpi, drawable-mdpi)。
-
layout文件夹: 存放布局文件,这些文件通常是XML文件,定义了应用界面的结构。
-
values文件夹: 包含多种资源文件,通常是XML格式。这些文件包括:
-
strings.xml: 存放应用中使用的所有字符串,便于本地化和修改。
-
colors.xml: 定义应用中使用的颜色。
-
styles.xml: 定义应用中使用的样式和主题。
-
dimens.xml: 定义应用中使用的尺寸信息,如边距和字体大小。
-
mipmap文件夹: 类似于drawable,但主要用于存放应用图标,支持不同的屏幕密度。
-
anim文件夹: 存放动画资源文件,通常是XML格式,定义了视图动画。
-
menu文件夹: 存放定义应用菜单的XML文件。
-
raw文件夹: 用于存放任何原始文件,如音频或视频文件。
-
xml:文件夹 存放其他类型的XML资源,例如设备配置文件。
这些资源文件夹允许开发者以更模块化的方式管理应用资源,并且可以根据设备的不同特性(如屏幕大小和语言)提供不同的资源版本,而我们本篇文章主要讲解的图片资源基本都是放在drawable文件夹和mipmap文件夹下。
三、安卓图片资源命名规范:
● 首先我们 Android 图片资源最基本的是命名规则如下:
○ 只能包含英文字母、数字和下划线("_")。
○ 不能以下划线或数字开头。
例如:
● 除了这些最基础的命名规则,还有一些常见的命名模板:
○ 通常的命名模板为:前缀缩写_主界面_功能部分_[后缀状态]。
○ 以下是一些常见的前缀缩写及其含义:
ic: 图标icon
bg: 背景background
di: 分隔线divider
sl: 选择器selector
cl: 颜色color
bt(btm): 按钮button
○ 以下是一些常见的后缀缩写及其含义,主要表示状态:
h:表示高亮状态
press:表示按下状态
select(check):表示被选中
unselect(checked):表示未被选中
对于会发生状态变化的图片,及可以在命名中添加状态后缀,,例如:
ic_rb_recharge_check: 代表选中状态
ic_rb_recharge_checked: 代表已选中状态
注意后缀命名非必要可以不添加。
○ 对于"主界面"这部分的命名可以以Activity为主,图片在哪个界面下用到就用哪个界面的命名,如mainActivity等。
○ 对于"功能部分"的命名额可以以所在主界面的对应的功能命名,例如:菜单menu、下载download、勾选框checkbox等。
结合以上,我们这个时候如果想要命名一个在主界面的下载按钮的图片,我们可以这样命 名:bt_mainActivity_download。如果这个时候下载的按钮的图片还分按下的状态和未按 下状态两个图片,就可以将被按下状态的下载按钮图片名称命名为 bt_mainActivity_download_press。
四、 安卓资源压缩与混淆
我们想要管理好安卓图片资源,仅仅是靠规范资源命名还是不够的,我们还需要做好资源压缩与混淆。做好资源的压缩是为了减少APK文件大小,在保持应用功能的同时,减少应用的存储占用。做好资源混淆是为了增加应用被逆向工程的难度从而提高包体的安全性。
首先从图片的压缩来说,我们可以使用使用WebP格式替代PNG和JPEG格式,因为Webp格式在保持相同图片质量的情况下,文件大小更小,或者是使用使用矢量图形,对于简单图形,使用SVG矢量图形代替位图,可以大幅减小资源大小,同时保持良好的图形质量。
但是在现实项目中,我们往往需要面对成百上千个图片资源文件,我们不可能手动地将已有的资源文件全部一个个优化压缩,因为这是一件费力还有风险的事情,万一有一个资源的优化过程中出现问题但是又没有测试出来,那就出bug了。所以面对现有项目我们最好的资源压缩与混淆方案应该是使用工具整体压缩与混淆。
● apk资源混淆
apk的资源混淆比较常用的是AndResGuard。AndResGuard是微信团队为Android开发的apk资源混淆工具,优点是使用起来简单,混淆速度快,能压缩资源大小,并且可以直接在Android studio里面使用。 使用方法:
在项目Gradle中添加以下配置:
js
apply plugin: 'AndResGuard'
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.21'
}
}
andResGuard {
// mappingFile = file("./resource_mapping.txt")
mappingFile = null
use7zip = true
useSign = true
// It will keep the origin path of your resources when it's true
keepRoot = false
// If set, name column in arsc those need to proguard will be kept to this value
fixedResName = "arg"
// It will merge the duplicated resources, but don't rely on this feature too much.
// it's always better to remove duplicated resource from repo
mergeDuplicatedRes = true
whiteList = [
// your icon
"R.drawable.icon",
// for fabric
"R.string.com.crashlytics.*",
// for google-services
"R.string.google_app_id",
"R.string.gcm_defaultSenderId",
"R.string.default_web_client_id",
"R.string.ga_trackingId",
"R.string.firebase_database_url",
"R.string.google_api_key",
"R.string.google_crash_reporting_api_key",
"R.string.project_id",
]
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
]
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.2.21'
//path = "/usr/local/bin/7za"
}
}
然后在Android Studio的组中找到生成任务选项andresguard执行打包指令即可。
关于andResGuard的详细用法可以查看他的官方github:GitHub - shwenzhang/AndResGuard: proguard resource for Android by wechat team
● aab资源混淆
想对aab包进行资源混淆可以使用另一个出名的混淆工具:AabResGuard。AabResGuard是字节跳动团队开发的资源混淆工具,专门可以给aab包使用,优点和andResGuard相识,但是AabResGuard还能做到合并重复资源和增量混淆等。
使用方法:
同样需要在根目录build.gradle文件里添加依赖:
js
dependencies {
classpath "com.bytedance.android:aabresguard-plugin:0.1.0"
}
并且在项目的build.gradle中配置如下:
js
apply plugin: "com.bytedance.android.aabResGuard"
aabResGuard {
mappingFile = file("mapping.txt").toPath() // Mapping file used for incremental obfuscation
whiteList = [ // White list rules
"*.R.raw.*",
"*.R.drawable.icon"
]
obfuscatedBundleFileName = "duplicated-app.aab" // Obfuscated file name, must end with '.aab'
mergeDuplicatedRes = true // Whether to allow the merge of duplicate resources
enableFilterFiles = true // Whether to allow filter files
filterList = [ // file filter rules
"*/arm64-v8a/*",
"META-INF/*"
]
enableFilterStrings = false // switch of filter strings
unusedStringPath = file("unused.txt").toPath() // strings will be filtered in this file
languageWhiteList = ["en", "zh"] // keep en,en-xx,zh,zh-xx etc. remove others.
}
然后可以通过执行原始打包命令来混淆:
js
./gradlew clean :app:bundleDebug --stacktrace
AabResGuard除了可以通过Android studio去执行资源混淆,还可以通过执行命令行的方式进行资源混淆:
首先可以在AabResGuard的官方github中下载执行命令行要用的jar包
Releases · bytedance/AabResGuard · GitHub
然后需要执行aab资源混淆就执行如下命令就好:
js
aabresguard obfuscate-bundle --bundle=app.aab --output=obfuscated.aab --config=config.xml --mapping=mapping.txt
--merge-duplicated-res=true
--storeFile=debug.store
--storePassword=android
--keyAlias=android
--keyPassword=android
这里需要注意的是执行该命令需要准备mapping.txt文件和config.xml文件,mapping.txt文件新建一个空的文件夹也行,配置文件config.xml可以复制以下代码创建:
js
<?xml version="1.0" encoding="UTF-8"?>
<resproguard>
<issue id="whitelist" isactive="true">
<path value="com.ss.android.ugc.aweme.R.raw.*" />
</issue>
<filter isactive="false">
<rule value="*/arm64-v8a/*" />
<rule value="META-INF/*.RSA" />
</filter>
</resproguard>
资源混淆结果图:
用命令行的方式执行混淆可以方便我们在出包后执行混淆操作,这怎么能少得了脚本建设呢,最后奉上aab混淆的python脚本:
js
def obfuscate_aab(aab: str, obfuscated_aab: str) -> bool:
path_AabResGuard = "aabResGuard的路径"
path_config = "config.xml的路径"
path_mapping = "mapping.txt的路径"
cmd = f"java -jar {path_AabResGuard} obfuscate-bundle --bundle={aab} --output={obfuscated_aab}" \
f" --config={path_config} --mapping={path_mapping} --merge-duplicated-res=true"
print(cmd)
with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=True,
universal_newlines=True) as p:
timer = Timer(100, p.kill)
try:
timer.start()
for line in p.stdout:
print(line, end='')
code = p.wait(timeout=100) if p else -1
except subprocess.TimeoutExpired or BaseException or Exception:
print("execute[command_time_out]: " + cmd)
return False
finally:
timer.cancel()
return bool(code == 0)
以下是AabResGuard的官方github,有兴趣可以再深入了解一下: github.com/shwenzhang/...