我们之前分析过瘦身优化:App瘦身优化
这篇文章我们继续分析瘦身优化
“每个字节都应该为用户体验服务,冗余即是对用户的不敬”
一、整体思路 & 优化流程
目标明确
明确一个目标:例如 安装包 ≤ 50MB 或 *相比当前减少 30%*。区分:安装包体积(APK/AAB**)** vs 安装后占用空间,手段有所不同。 量化分析
使用工具:
(Android Studio 内置):看各目录和 .dex 的体积占比。
Analyze APK /
apktool 解包进一步分析。 先找“大头”:一般是
bundletool
(so库)
lib/(图片、音视频、字体)
res/(代码)assets(离线包、H5、模型文件等) 优先级
classes*.dex
P0:资源 & 多余依赖(通常最容易减 20~40%)P1:代码混淆压缩 & 多dex瘦身P2:架构升级(Android App Bundle**、动态特性模块拆分)**P3:深度方案(重构架构、替换大模块)
二、基础配置层面(低成本高收益)
使用 Android App Bundle(AAB)
如果还在发 APK,建议改为 AAB + Google Play/各应用市场的分发优化:
好处:
自动做 ABI 分包(只下当前CPU架构的 so)自动做 语言资源拆分(只下当前语言)自动做 密度拆分(按屏幕分发资源) 配置(示意):
android {
bundle {
language {
enableSplit = true
}
density {
enableSplit = true
}
abi {
enableSplit = true
}
}
}
启用 R8 / ProGuard 混淆与压缩
确保开启:
buildTypes {
release {
minifyEnabled true // 开启代码压缩+混淆
shrinkResources true // 删除未使用的资源
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
注意:
线上一定要配好 规则,避免反射导致崩溃。可以开启
keep 辅助分析为啥某些类没被清掉。
-whyareyoukeeping
三、资源瘦身(通常收益最大)
图片资源优化
格式选择
优先使用 矢量图(Vector Drawable) 替代多分辨率 PNG 图标(尤其是图标、简单图形)。位图资源优先考虑:
WebP(有损/无损)适当场景考虑 AVIF(Android 12+,视用户覆盖情况)
压缩图片
使用专业工具离线批量压缩:
TinyPNG、ImageOptim 等(出包前 pipeline 中跑一遍)。 控制图片分辨率:UI 不需要 4K 大图的话就不要用。
去掉冗余分辨率
只保留必要的 /
xxxhdpi 等,避免同一资源多个密度重复。使用 App Bundle 的 density split 后,可以适度减少本地冗余。
xxhdpi
音视频资源
优先采用 在线加载+缓存,避免大体积音视频直接打进包。必须内置的音频:
采用较高压缩率格式(如 、
ogg)。控制长度,避免整首歌打入包。 必须内置的视频:
aac
降低分辨率、码率,或做短片/循环片段。考虑 H.265/VP9(取决于最低兼容版本)。
字体资源
如果使用自定义字体:
尽量只内嵌 1~2 种主要字体。使用 子集化字体(只保留需要的字符范围,例如 、
latin),减少TTF/OTF大小。多语言字体尽量分拆,通过在线下载或按需包加载。
basic CJK
多语言资源(strings.xml)
若使用 AAB,可以开启语言拆分,安装时只下当前语言。评估是否真的需要支持所有语言:
去掉几乎没有用户的语言资源。或采用在线配置方式加载部分文案,避免全部内置。
移除无用资源
开启 后,配合工具排查:
shrinkResources
Android Studio → 查找 unused resources。注意:被反射 / 动态拼接引用的资源,要通过
Analyze → Inspect Code 或 proguard keep 保护。 清理:
tools:keep
不再使用的旧图标、老主题资源、废弃布局文件。旧的引导页图片、运营活动资源等。
四、代码维度瘦身(dex / 依赖)
依赖梳理与瘦身
检查 Gradle 依赖树
./gradlew app:dependencies
找出:
重复的库(如同一个功能有多个不同依赖)只用到一个小功能却引入非常大的库(如整套 analytics / IM / video SDK)
替换/删除大依赖
大而全的 SDK → 精简版 SDK 或自研轻量实现。多个 JSON 库 → 统一为一个(如 Gson / Moshi),去掉其他。多个网络库 → 统一到 OkHttp/Retrofit 一套。
使用 ** 而非
implementation**
api
避免传递性依赖膨胀其他 module。
代码结构优化
尽量降低方法数量(方法数过多会产生 multi-dex,dex 体积上升):
清理未使用的老代码(弃用模块、旧业务逻辑)。将一些复杂工具类拆分为模块依赖,按需加载或仅在特定版本使用。 对 插件化/热修复框架 做取舍:
部分框架自身体积不小,要评估是否仍有必要。
代码依赖检查方法
在 Android Studio 中检查项目模块(Module)之间的依赖关系,主要有 可视化界面查看、Gradle 命令分析、第三方插件辅助 三种方式,覆盖从快速预览到详细分析的场景,以下是具体操作步骤:
1)、可视化界面:快速查看模块依赖(最常用)
Android Studio 内置了依赖关系可视化工具,支持直观查看模块间的直接 / 间接依赖,操作简单:
方式 1:通过「Project Structure」查看
适合快速核对单个模块的依赖(包括本地模块 + 远程库):
打开 Android Studio,进入项目;两种打开方式:
顶部菜单栏 → (快捷键:
File → Project Structure / Mac
Ctrl+Alt+Shift+S);右侧「Gradle」面板 → 选中项目根目录 → 右键 →
Cmd+Alt+Shift+S; 在左侧面板选择「Modules」,然后选中你要检查的模块(如
Open Module Settings);切换到右侧「Dependencies」标签页:
app
列表中「Module dependency」类型即为 本地模块依赖(如 、
:moduleA);其他类型(如
:library)为远程库依赖,可忽略; 若要查看其他模块的依赖,切换左侧模块列表即可。
Library dependency
方式 2:通过「Dependency Graph」可视化依赖树
适合查看 整个项目的所有模块依赖关系(包括模块间的间接依赖),图形化展示更清晰:
顶部菜单栏 → (打开 Gradle 面板);在 Gradle 面板中,展开你的项目 → 选中要分析的模块(如
View → Tool Windows → Gradle)→ 展开
app;右键点击
Tasks → help 任务 → 选择
dependencies(如
Run '模块名:dependencies');执行完成后,底部「Run」面板会输出文本格式的依赖树;若要可视化图形:
Run 'app:dependencies'
顶部菜单栏 → ;在弹出的窗口中,选择要分析的模块(可多选,如
Analyze → Analyze Dependencies...、
app),点击「OK」;生成依赖关系图,支持:
moduleA
拖动节点调整布局;右键节点 → 查看该模块的详细依赖;筛选「Only Modules」只显示本地模块依赖(隐藏远程库)。
Show Dependencies
2)、Gradle 命令:详细分析依赖(支持过滤和导出)
如果需要更精确的依赖分析(如查找重复依赖、间接依赖来源),可通过 Gradle 命令行执行,Android Studio 内置终端直接支持:
打开终端
底部面板 → 切换到「Terminal」标签(若未显示,通过 打开)。
View → Tool Windows → Terminal
核心命令(适用于绝大多数场景)
在终端中输入以下命令,按回车执行:
| 命令(Windows/Mac/Linux 通用) | 作用 |
|---|---|
| ./gradlew :app:dependencies | 查看 app 模块的所有依赖(本地模块 + 远程库),按配置(debug/release)分类 |
| ./gradlew :moduleA:dependencies | 查看指定模块(如 moduleA)的依赖 |
| ./gradlew dependencies | 查看整个项目所有模块的依赖(输出较多,不推荐) |
过滤筛选(关键!只看模块依赖)
默认输出包含大量远程库依赖,可通过 (Mac/Linux)或
grep(Windows)过滤,只保留本地模块依赖(模块名通常以
findstr 开头,如
:):
:moduleA
Mac**/Linux**:
./gradlew :app:dependencies | grep --color ":module" # 筛选包含 ":module" 的依赖(模块名通常含 module)# 或精确匹配本地模块(格式为 :模块名)
./gradlew :app:dependencies | grep -E ":([a-zA-Z0-9_]+)" --color
Windows(CMD** 终端)**:
gradlew :app:dependencies | findstr ":module"
Windows(PowerShell** 终端)**:
.gradlew :app:dependencies | Select-String ":module"
导出依赖报告(便于分享 / 存档)
将依赖分析结果导出为文本文件,方便后续查看:
Mac/Linux:
./gradlew :app:dependencies > app_dependencies.txt
Windows:
gradlew :app:dependencies > app_dependencies.txt
执行后,项目根目录会生成 文件,包含完整依赖信息。
app_dependencies.txt
3)、第三方插件:增强型依赖分析(复杂项目推荐)
如果项目模块多、依赖关系复杂,可使用第三方插件提供更强大的分析功能:
推荐插件:
Dependency-Check
Dependency-Check
功能:可视化依赖树、检测重复依赖、冲突依赖报警、导出详细报告;安装步骤:
顶部菜单栏 → (Mac:
File → Settings → Plugins);搜索「Dependency-Check」,点击「Install」安装,重启 Android Studio; 使用:
Android Studio → Settings → Plugins
右键项目根目录 → ;生成交互式依赖报告,支持筛选模块、查看依赖链路、定位冲突依赖。
Dependency Check → Analyze Project
其他插件:
:快速搜索依赖、查看最新版本、一键替换依赖;
Gradle Dependency Helper:分析依赖对应的方法数(辅助优化包体积)。
Dexcount Gradle Plugin
4)、关键说明
模块依赖格式:本地模块依赖在 中表现为
build.gradle(Android Gradle Plugin 3.0+),旧版本可能是
implementation project(':moduleA');间接依赖:若
compile project(':moduleA') 依赖
app,
moduleA 依赖
moduleA,则
moduleB 间接依赖
app,通过「Dependency Graph」或 Gradle 命令可查看这种传递依赖;依赖冲突:若多个模块依赖同一库的不同版本,Gradle 会默认选择最高版本,可通过
moduleB 查看冲突解决结果(标注
./gradlew :app:dependencies)。
(variant: ...)
通过以上方法,可全面覆盖从「快速预览」到「详细分析」的模块依赖检查需求,根据项目复杂度选择对应方式即可~
五、Native so 库瘦身
只保留必要 ABI
常见 ABI:,
armeabi-v7a,
arm64-v8a,
x86。实战策略:
x86_64
绝大多数线上版本可以只保留 +
armeabi-v7a。x86 系列设备占比非常低,可改为通过渠道单独提供。 Gradle 配置示例:
arm64-v8a
android {
defaultConfig {
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
}
第三方 SDK 的 so 管理
许多 IM、音视频、地图 SDK 默认集成全 ABI 的 so。
尽量使用官方提供的 精简包 或“按需模块化包”。如果 SDK 允许手动删去某些 ABI 目录,可在构建前处理。
自研 Native 代码优化
编译选项优化:
打开 或
-Os(以减小体积为目标)。去掉不必要的调试符号。 删除无用的 native 方法、旧接口。
-Oz
六、模块拆分与动态交付(Dynamic Feature)
如果 App 已经比较大(>100MB),可以考虑:
按业务模块拆分
核心流程(登录/首页/基础浏览)作为 base module。低频/重度功能做 Dynamic Feature Module(例如:AR 模块、重度编辑器、游戏、视频编辑等)。 触发更新或安装
用户进入对应页面前再下载对应动态模块。 拆分原则
按“使用频次”和“体积大小”做划分:
高频 + 小体积:放 base。低频 + 大体积:拆动态模块。 避免过度拆分导致开发和维护成本极高。
七、启动后按需加载 / 延迟加载
减少首包必须内容
把不影响“首屏体验”的功能延后加载或在线下发:
运营活动资源辅助工具模块(分享、扫一扫、埋点可稍晚初始化) 在线资源 + 本地缓存
部分资源(尤其大图 / 大配置)改为首启后后台下载到本地。注意控制失败回退策略,防止无网时完全不可用。
八、构建流程中的自动化检查
持续集成(CI**)里增加体积阈值检查**
每次构建统计:
包体积各目录大小(lib/res/assets/dex) 超过阈值时报警,避免长期“野蛮生长”。 自动压缩/转换 Pipeline
在打包前自动执行:
图片压缩脚本字体子集化删除不再使用的资源(配合白名单)
九、具体优化措施
1、assets目录优化
1)、删除字体文件,字体文件由系统预置 (缩减16MB)

把app的这两个字体文件删除,由系统去集成预置。两个字体文件大约16MB
2、代码依赖优化
使用AS的gradle检查项目依赖树:
如:
+--- project :FlutterHome (*)
+--- org.jetbrains.kotlin:kotlin-parcelize-runtime:1.8.22
| --- org.jetbrains.kotlin:kotlin-stdlib:1.8.22
| +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22
| --- org.jetbrains:annotations:13.0
+--- androidx.databinding:viewbinding:4.2.2
| --- androidx.annotation:annotation:1.0.0 -> 1.4.0
| --- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22
| +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22
| --- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 (*)
+--- androidx.room:room-ktx:2.4.3
| +--- androidx.room:room-common:2.4.3
| | --- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| +--- androidx.room:room-runtime:2.4.3 -> 2.4.0
| | +--- androidx.room:room-common:2.4.0 -> 2.4.3 (*)
| | +--- androidx.sqlite:sqlite-framework:2.2.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | --- androidx.sqlite:sqlite:2.2.0
| | | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | --- androidx.sqlite:sqlite:2.2.0 (*)
| +--- org.jetbrains.kotlin:kotlin-stdlib:1.7.0 -> 1.8.22 (*)
| --- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2 -> 1.6.0
| +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0 -> 1.6.1
| | --- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.1
| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.1
| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1 -> 1.6.0 (c)
| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.1 (c)
| | | --- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1 (c)
| | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0 -> 1.8.22 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-common:1.6.0 -> 1.8.22
| +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.0 -> 1.6.1 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0 -> 1.8.22 (*)
+--- cn.therouter:router:1.2.2
| +--- com.google.code.gson:gson:2.9.1 -> 2.10.1
| +--- androidx.appcompat:appcompat:1.3.0 -> 1.4.1
| | +--- androidx.annotation:annotation:1.3.0 -> 1.4.0 (*)
| | +--- androidx.core:core:1.7.0 -> 1.8.0
| | | +--- androidx.annotation:annotation:1.2.0 -> 1.4.0 (*)
| | | +--- androidx.annotation:annotation-experimental:1.1.0
| | | +--- androidx.lifecycle:lifecycle-runtime:2.3.1 -> 2.5.1
| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | +--- androidx.arch.core:core-common:2.1.0
| | | | | --- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | --- androidx.lifecycle:lifecycle-common:2.5.1
| | | | --- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | --- androidx.versionedparcelable:versionedparcelable:1.1.1
| | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | --- androidx.collection:collection:1.0.0 -> 1.2.0
| | | --- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | +--- androidx.cursoradapter:cursoradapter:1.0.0
| | | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | +--- androidx.activity:activity:1.2.4 -> 1.5.1
| | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | +--- androidx.core:core:1.8.0 (*)
| | | +--- androidx.lifecycle:lifecycle-runtime:2.5.1 (*)
| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.5.1
| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | --- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | | +--- androidx.core:core-ktx:1.2.0 -> 1.8.0
| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | | +--- androidx.core:core:1.8.0 (*)
| | | | | --- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.5.1
| | | | | --- androidx.lifecycle:lifecycle-common:2.5.1 (*)
| | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.5.1 (*)
| | | | +--- androidx.savedstate:savedstate:1.2.0
| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | | --- org.jetbrains.kotlin:kotlin-stdlib:1.6.20 -> 1.8.22 (*)
| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| | | | --- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1 -> 1.6.0 (*)
| | | +--- androidx.savedstate:savedstate:1.2.0 (*)
| | | --- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| | +--- androidx.fragment:fragment:1.3.6 -> 1.5.4
| | | +--- androidx.activity:activity:1.5.1 (*)
| | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | +--- androidx.annotation:annotation-experimental:1.0.0 -> 1.1.0
| | | +--- androidx.collection:collection:1.1.0 -> 1.2.0 (*)
| | | +--- androidx.core:core-ktx:1.2.0 -> 1.8.0 (*)
| | | +--- androidx.lifecycle:lifecycle-livedata-core:2.5.1 (*)
| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.5.1 (*)
| | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1 (*)
| | | +--- androidx.loader:loader:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | | +--- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | | | +--- androidx.lifecycle:lifecycle-livedata:2.0.0 -> 2.5.1
| | | | | +--- androidx.arch.core:core-runtime:2.1.0
| | | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | | | --- androidx.arch.core:core-common:2.1.0 (*)
| | | | | --- androidx.lifecycle:lifecycle-livedata-core:2.5.1 (*)
| | | | --- androidx.lifecycle:lifecycle-viewmodel:2.0.0 -> 2.5.1 (*)
| | | +--- androidx.savedstate:savedstate:1.2.0 (*)
| | | +--- androidx.viewpager:viewpager:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | | +--- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | | | --- androidx.customview:customview:1.0.0 -> 1.1.0
| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | --- androidx.core:core:1.3.0 -> 1.8.0 (*)
| | | --- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| | +--- androidx.appcompat:appcompat-resources:1.4.1
| | | +--- androidx.annotation:annotation:1.2.0 -> 1.4.0 (*)
| | | +--- androidx.core:core:1.0.1 -> 1.8.0 (*)
| | | +--- androidx.vectordrawable:vectordrawable:1.1.0
| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | +--- androidx.core:core:1.1.0 -> 1.8.0 (*)
| | | | --- androidx.collection:collection:1.1.0 -> 1.2.0 (*)
| | | --- androidx.vectordrawable:vectordrawable-animated:1.1.0
| | | +--- androidx.vectordrawable:vectordrawable:1.1.0 (*)
| | | +--- androidx.interpolator:interpolator:1.0.0
| | | | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | --- androidx.collection:collection:1.1.0 -> 1.2.0 (*)
| | +--- androidx.drawerlayout:drawerlayout:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | +--- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | | --- androidx.customview:customview:1.0.0 -> 1.1.0 (*)
| | --- androidx.savedstate:savedstate:1.1.0 -> 1.2.0 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21 -> 1.8.22 (*)
+--- :jxwCwSdk_1.1.1
+--- com.youth.banner:banner:2.0.11
+--- com.github.bumptech.glide:glide:4.11.0
| +--- com.github.bumptech.glide:gifdecoder:4.11.0
| | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| +--- com.github.bumptech.glide:disklrucache:4.11.0
| +--- com.github.bumptech.glide:annotations:4.11.0
| +--- androidx.fragment:fragment:1.0.0 -> 1.5.4 (*)
| +--- androidx.vectordrawable:vectordrawable-animated:1.0.0 -> 1.1.0 (*)
| --- androidx.exifinterface:exifinterface:1.0.0 -> 1.3.2
| --- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
+--- com.squareup.retrofit2:adapter-rxjava2:2.3.0
| +--- com.squareup.retrofit2:retrofit:2.3.0 -> 2.9.0
| | --- com.squareup.okhttp3:okhttp:3.14.9 -> 3.14.8
| | --- com.squareup.okio:okio:1.17.2 -> 1.17.5
| --- io.reactivex.rxjava2:rxjava:2.0.0 -> 2.2.6
| --- org.reactivestreams:reactive-streams:1.0.2 -> 1.0.4
+--- io.reactivex.rxjava2:rxandroid:2.0.1
| --- io.reactivex.rxjava2:rxjava:2.0.1 -> 2.2.6 (*)
+--- com.trello.rxlifecycle2:rxlifecycle:2.1.0
| --- io.reactivex.rxjava2:rxjava:2.1.0 -> 2.2.6 (*)
+--- com.trello.rxlifecycle2:rxlifecycle-components:2.1.0
| +--- com.trello.rxlifecycle2:rxlifecycle-android:2.1.0
| | +--- com.trello.rxlifecycle2:rxlifecycle:2.1.0 (*)
| | +--- io.reactivex.rxjava2:rxandroid:2.0.1 (*)
| | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| +--- io.reactivex.rxjava2:rxjava:2.1.0 -> 2.2.6 (*)
| --- androidx.appcompat:appcompat:1.0.0 -> 1.4.1 (*)
+--- org.greenrobot:eventbus:3.0.0
+--- com.ccos.cooui:cache:0.0.1-tzl-10
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20 -> 1.8.22 (*)
+--- com.ccos.cooui:component:0.0.1-tzl-8 -> project :CoreComponent
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- com.ccos.cooui:uiengine:1.8-test-1 -> project :CoreUIEngine
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- com.ccos.cooui:logger:1.7-test-1
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20 -> 1.8.22 (*)
+--- swaiotos.service:push:1.0.636
+--- ccos.core:push-lib:4.1.7
+--- com.coocaa.player:plugin-sdk:2.0.0.1-SNAPSHOT
| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20 -> 1.8.22 (*)
| +--- com.tencent.shadow2.core:common:2.5.0.1-8a951ac7-SNAPSHOT
| +--- com.tencent.shadow2.dynamic:host:2.5.0.1-8a951ac7-SNAPSHOT
| | +--- com.tencent.shadow2.dynamic:apk:2.5.0.1-8a951ac7-SNAPSHOT
| | | --- com.tencent.shadow2.core:common:2.5.0.1-8a951ac7-SNAPSHOT
| | +--- com.tencent.shadow2.core:common:2.5.0.1-8a951ac7-SNAPSHOT
| | --- com.tencent.shadow2.core:utils:2.5.0.1-8a951ac7-SNAPSHOT
| +--- com.tencent.shadow2.dynamic:host-multi-loader-ext:2.5.0.1-8a951ac7-SNAPSHOT
| | +--- com.tencent.shadow2.core:common:2.5.0.1-8a951ac7-SNAPSHOT
| | --- com.tencent.shadow2.dynamic:host:2.5.0.1-8a951ac7-SNAPSHOT (*)
| +--- swaiotos:sal:1.0.601 -> 1.0.718
| | +--- swaiotos.base:skysdk:1.0.718
| | +--- com.alibaba:fastjson:1.1.71.android -> 1.2.32
| | +--- ccos.sdk:rgb-system-sdk:1.0.29
| | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| +--- com.alibaba:fastjson:1.2.32
| +--- com.github.Justson:Downloader:v5.0.4-androidx
| +--- androidx.legacy:legacy-support-v4:1.0.0
| | +--- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | +--- androidx.media:media:1.0.0 -> 1.4.1
| | | --- androidx.core:core:1.6.0 -> 1.8.0 (*)
| | +--- androidx.legacy:legacy-support-core-utils:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | +--- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | | +--- androidx.documentfile:documentfile:1.0.0
| | | | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | +--- androidx.loader:loader:1.0.0 (*)
| | | +--- androidx.localbroadcastmanager:localbroadcastmanager:1.0.0 -> 1.1.0
| | | | --- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | --- androidx.print:print:1.0.0
| | | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | +--- androidx.legacy:legacy-support-core-ui:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | +--- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | | +--- androidx.legacy:legacy-support-core-utils:1.0.0 (*)
| | | +--- androidx.customview:customview:1.0.0 -> 1.1.0 (*)
| | | +--- androidx.viewpager:viewpager:1.0.0 (*)
| | | +--- androidx.coordinatorlayout:coordinatorlayout:1.0.0 -> 1.1.0
| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | +--- androidx.core:core:1.1.0 -> 1.8.0 (*)
| | | | +--- androidx.customview:customview:1.0.0 -> 1.1.0 (*)
| | | | --- androidx.collection:collection:1.0.0 -> 1.2.0 (*)
| | | +--- androidx.drawerlayout:drawerlayout:1.0.0 (*)
| | | +--- androidx.slidingpanelayout:slidingpanelayout:1.0.0 -> 1.2.0
| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | | | --- androidx.customview:customview:1.1.0 (*)
| | | +--- androidx.interpolator:interpolator:1.0.0 (*)
| | | +--- androidx.swiperefreshlayout:swiperefreshlayout:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | | +--- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | | | --- androidx.interpolator:interpolator:1.0.0 (*)
| | | +--- androidx.asynclayoutinflater:asynclayoutinflater:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | | --- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | | --- androidx.cursoradapter:cursoradapter:1.0.0 (*)
| | --- androidx.fragment:fragment:1.0.0 -> 1.5.4 (*)
| +--- com.coocaa.player:sdk-lite:1.0.27.exo2118.aios-SNAPSHOT
| | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50 -> 1.8.22 (*)
| | +--- com.google.android.exoplayer:exoplayer-core:2.11.8
| | +--- com.google.android.exoplayer:exoplayer-hls:2.11.8
| | +--- com.google.android.exoplayer:exoplayer-dash:2.11.8
| | --- com.google.android.exoplayer:exoplayer-smoothstreaming:2.11.8
| +--- com.google.protobuf:protobuf-java:3.5.1
| +--- com.tencent.shadow2.dynamic:manager-multi-loader-ext:2.5.0.1-8a951ac7-SNAPSHOT
| | +--- com.tencent.shadow2.core:manager:2.5.0.1-8a951ac7-SNAPSHOT
| | | +--- com.tencent.shadow2.core:load-parameters:2.5.0.1-8a951ac7-SNAPSHOT
| | | --- com.tencent.shadow2.core:utils:2.5.0.1-8a951ac7-SNAPSHOT
| | +--- com.tencent.shadow2.dynamic:loader:2.5.0.1-8a951ac7-SNAPSHOT
| | --- com.tencent.shadow2.dynamic:manager:2.5.0.1-8a951ac7-SNAPSHOT
| | +--- com.tencent.shadow2.core:manager:2.5.0.1-8a951ac7-SNAPSHOT (*)
| | --- com.tencent.shadow2.dynamic:loader:2.5.0.1-8a951ac7-SNAPSHOT
| +--- com.tencent.shadow2.dynamic:manager:2.5.0.1-8a951ac7-SNAPSHOT (*)
| +--- com.tencent.shadow2.core:manager:2.5.0.1-8a951ac7-SNAPSHOT (*)
| +--- com.tencent.shadow2.dynamic:loader:2.5.0.1-8a951ac7-SNAPSHOT
| +--- com.coocaa.player:plugin-api:1.0.0
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20 -> 1.8.22 (*)
| --- androidx.multidex:multidex:2.0.0
+--- project :LibNetwork
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- project :LibUtil
| +--- com.blankj:utilcodex:1.31.1
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- project :LibPush
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- project :LibCommon
| +--- com.ccos.cooui:component:0.0.1-tzl-8 -> project :CoreComponent (*)
| +--- com.ccos.cooui:logger:1.7-test-1 (*)
| +--- com.ccos.cooui:uiengine:1.8-test-1 -> project :CoreUIEngine (*)
| +--- com.ccos.cooui:imageloader:0.0.2-tzl-1 -> project :CoreImageLoader
| | +--- androidx.databinding:viewbinding:4.2.2 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
| +--- com.ccos.cooui:cache:0.0.1-tzl-10 (*)
| +--- project :LibNetwork (*)
| +--- com.ccos.cooui:user:0.0.1-tzl-1 -> project :LibUser
| | +--- androidx.databinding:viewbinding:4.2.2 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
| +--- project :LibUtil (*)
| +--- project :LibOperateAppUI
| | +--- androidx.databinding:viewbinding:4.2.2 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
| +--- project :LibEventTracking
| | +--- :gitv-license-online-1.0.22-release
| | +--- :newtv-license-submit
| | +--- com.coocaa.support:bee-tv:2.0.6
| | | --- com.coocaa.support:bee:2.0.6
| | +--- androidx.databinding:viewbinding:4.2.2 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
| +--- project :LibDomainVerify
| | +--- androidx.databinding:viewbinding:4.2.2 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
| +--- com.ccos.cooui:apkdownload:0.0.1-tzl-1
| +--- project :LibPlugin
| | +--- androidx.databinding:viewbinding:4.2.2 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
| +--- androidx.multidex:multidex:2.0.0
| +--- androidx.appcompat:appcompat:1.4.1 (*)
| +--- androidx.core:core-ktx:1.8.0 (*)
| +--- androidx.activity:activity-ktx:1.5.0
| | +--- androidx.activity:activity:1.5.0 -> 1.5.1 (*)
| | +--- androidx.core:core-ktx:1.1.0 -> 1.8.0 (*)
| | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.5.0 -> 2.5.1
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| | | +--- androidx.lifecycle:lifecycle-runtime:2.5.1 (*)
| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| | | --- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1 -> 1.6.0 (*)
| | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0 -> 2.5.1
| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.5.1 (*)
| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| | | --- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1 -> 1.6.0 (*)
| | +--- androidx.savedstate:savedstate-ktx:1.2.0
| | | +--- androidx.savedstate:savedstate:1.2.0 (*)
| | | --- org.jetbrains.kotlin:kotlin-stdlib:1.6.20 -> 1.8.22 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| +--- cn.therouter:router:1.2.2 (*)
| +--- swaiotos:sal:1.0.718 (*)
| +--- swaiotos.service:user:1.0.636
| | --- swaiotos.base:skysdk:1.0.636 -> 1.0.718
| +--- androidx.constraintlayout:constraintlayout:2.1.4
| +--- ccos.sdk:multimodelsdk:2.0.100
| +--- com.squareup.okhttp3:okhttp:3.14.8 (*)
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- project :LibAiCommon
| +--- project :LibSmartCoolVoice
| | +--- com.blankj:utilcodex:1.31.1
| | +--- androidx.databinding:viewbinding:4.2.2 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
| +--- project :LibFlutterEngine
| | +--- androidx.databinding:viewbinding:4.2.2 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- project :LibWebview
| +--- com.blankj:utilcodex:1.31.1
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- project :StudyGradeSolve
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22 (*)
+--- swaiotos:sal:1.0.718 (*)
+--- com.coocaa.support:bee-tv:2.0.6 (*)
+--- androidx.constraintlayout:constraintlayout:2.1.4
+--- androidx.recyclerview:recyclerview:1.2.1
| +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| +--- androidx.core:core:1.3.2 -> 1.8.0 (*)
| --- androidx.customview:customview:1.0.0 -> 1.1.0 (*)
+--- com.coocaa.sdk:protocol:2.1.6
| +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1 -> 1.6.0 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10 -> 1.8.22 (*)
+--- ccos.sdk:softsettingssdk:1.3.2
+--- ccos.sdk:twocentersdk:1.2.4
+--- androidx.localbroadcastmanager:localbroadcastmanager:1.1.0 (*)
+--- com.tencent.tav:libpag:4.3.62
+--- androidx.viewpager2:viewpager2:1.0.0
| +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| +--- androidx.fragment:fragment:1.1.0 -> 1.5.4 (*)
| +--- androidx.recyclerview:recyclerview:1.1.0 -> 1.2.1 (*)
| +--- androidx.core:core:1.1.0 -> 1.8.0 (*)
| --- androidx.collection:collection:1.1.0 -> 1.2.0 (*)
+--- com.github.yannecer:NCalendar:6.0.0
+--- com.squareup.retrofit2:retrofit:2.9.0 (*)
+--- com.ccos.cooui:http:0.0.1-tzl-10
| +--- androidx.databinding:viewbinding:4.2.2 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20 -> 1.8.22 (*)
+--- com.squareup.okhttp3:logging-interceptor:3.4.1
| --- com.squareup.okhttp3:okhttp:3.4.1 -> 3.14.8 (*)
+--- com.localebro:okhttpprofiler:1.0.8
+--- com.github.youlookwhat:ByRecyclerView:1.1.6
+--- com.airbnb.android:lottie:5.2.0
+--- com.squareup.okhttp3:okhttp-sse:3.14.8
| --- com.squareup.okhttp3:okhttp:3.14.8 (*)
+--- org.jetbrains.anko:anko-sdk15:0.9.1
| --- org.jetbrains.anko:anko-common:0.9.1
+--- androidx.work:work-runtime-ktx:2.7.1
| +--- androidx.work:work-runtime:2.7.1
| | +--- androidx.annotation:annotation-experimental:1.0.0 -> 1.1.0
| | +--- com.google.guava:listenablefuture:1.0 -> 9999.0-empty-to-avoid-conflict-with-guava
| | +--- androidx.lifecycle:lifecycle-livedata:2.1.0 -> 2.5.1 (*)
| | --- androidx.startup:startup-runtime:1.0.0 -> 1.1.1
| +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> 1.8.22 (*)
| --- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0 -> 1.6.0 (*)
+--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1 (*)
+--- androidx.fragment:fragment-ktx:1.5.4
| +--- androidx.activity:activity-ktx:1.5.1 -> 1.5.0 (*)
| +--- androidx.collection:collection-ktx:1.1.0 -> 1.2.0
| | +--- androidx.collection:collection:1.2.0 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib:1.5.31 -> 1.8.22 (*)
| +--- androidx.core:core-ktx:1.2.0 -> 1.8.0 (*)
| +--- androidx.fragment:fragment:1.5.4 (*)
| +--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.5.1
| | +--- androidx.lifecycle:lifecycle-livedata-core:2.5.1 (*)
| | --- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
| +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1 (*)
| +--- androidx.savedstate:savedstate-ktx:1.2.0 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.8.22 (*)
+--- androidx.lifecycle:lifecycle-runtime-ktx:2.5.1 (*)
+--- com.google.android.material:material:1.3.0 -> 1.4.0
| +--- androidx.annotation:annotation:1.0.1 -> 1.4.0 (*)
| +--- androidx.appcompat:appcompat:1.1.0 -> 1.4.1 (*)
| +--- androidx.cardview:cardview:1.0.0
| | --- androidx.annotation:annotation:1.0.0 -> 1.4.0 (*)
| +--- androidx.coordinatorlayout:coordinatorlayout:1.1.0 (*)
| +--- androidx.constraintlayout:constraintlayout:2.0.1 -> 2.1.4
| +--- androidx.core:core:1.5.0 -> 1.8.0 (*)
| +--- androidx.dynamicanimation:dynamicanimation:1.0.0
| | +--- androidx.core:core:1.0.0 -> 1.8.0 (*)
| | +--- androidx.collection:collection:1.0.0 -> 1.2.0 (*)
| | --- androidx.legacy:legacy-support-core-utils:1.0.0 (*)
| +--- androidx.annotation:annotation-experimental:1.0.0 -> 1.1.0
| +--- androidx.fragment:fragment:1.0.0 -> 1.5.4 (*)
| +--- androidx.lifecycle:lifecycle-runtime:2.0.0 -> 2.5.1 (*)
| +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.2.1 (*)
| +--- androidx.transition:transition:1.2.0 -> 1.4.1
| | +--- androidx.annotation:annotation:1.1.0 -> 1.4.0 (*)
| | --- androidx.core:core:1.1.0 -> 1.8.0 (*)
| +--- androidx.vectordrawable:vectordrawable:1.1.0 (*)
| --- androidx.viewpager2:viewpager2:1.0.0 (*)
+--- com.squareup.leakcanary:leakcanary-android:2.10
| --- com.squareup.leakcanary:leakcanary-object-watcher-android:2.10
| +--- com.squareup.leakcanary:leakcanary-object-watcher-android-core:2.10
| | +--- com.squareup.leakcanary:leakcanary-object-watcher:2.10
| | | --- com.squareup.leakcanary:shark-log:2.10
| | --- com.squareup.leakcanary:leakcanary-android-utils:2.10
| | --- com.squareup.leakcanary:shark-log:2.10
| --- org.jetbrains.kotlin:kotlin-stdlib:1.4.21 -> 1.8.22 (*)
+--- com.coocaa.terminal:leakcanary-core:0.0.1-tzl-10
| +--- com.squareup.leakcanary:shark-android:2.10
| | --- com.squareup.leakcanary:shark:2.10
| | --- com.squareup.leakcanary:shark-graph:2.10
| | --- com.squareup.leakcanary:shark-hprof:2.10
| | --- com.squareup.leakcanary:shark-log:2.10
| +--- com.squareup.leakcanary:leakcanary-object-watcher-android-core:2.10 (*)
| +--- com.squareup.leakcanary:leakcanary-object-watcher-android-androidx:2.10
| | --- com.squareup.leakcanary:leakcanary-object-watcher-android-core:2.10 (*)
| +--- com.squareup.leakcanary:leakcanary-object-watcher-android-support-fragments:2.10
| | --- com.squareup.leakcanary:leakcanary-object-watcher-android-core:2.10 (*)
| --- org.jetbrains.kotlin:kotlin-stdlib:kotlinLib -> 1.8.22 (*)
+--- com.guolindev.glance:glance:1.1.0
| --- androidx.databinding:viewbinding:4.2.1 -> 4.2.2 (*)
+--- project :CoreComponent (*)
+--- project :CoreUIEngine (*)
+--- project :CoreImageLoader (*)
+--- project :LibUser (*)
+--- org.jetbrains.kotlin:kotlin-parcelize-runtime:{strictly 1.8.22} -> 1.8.22 (c)
+--- androidx.databinding:viewbinding:{strictly 4.2.2} -> 4.2.2 (c)
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:{strictly 1.8.22} -> 1.8.22 (c)
+--- com.squareup.leakcanary:leakcanary-android:{strictly 2.10} -> 2.10 (c)
+--- com.coocaa.terminal:leakcanary-core:{strictly 0.0.1-tzl-10} -> 0.0.1-tzl-10 (c)
+--- com.guolindev.glance:glance:{strictly 1.1.0} -> 1.1.0 (c)
+--- androidx.recyclerview:recyclerview:{strictly 1.2.1} -> 1.2.1 (c)
+--- androidx.lifecycle:lifecycle-runtime-ktx:{strictly 2.5.1} -> 2.5.1 (c)
+--- androidx.lifecycle:lifecycle-viewmodel-ktx:{strictly 2.5.1} -> 2.5.1 (c)
+--- androidx.constraintlayout:constraintlayout:{strictly 2.1.4} -> 2.1.4 (c)
+--- com.google.android.material:material:{strictly 1.4.0} -> 1.4.0 (c)
+--- androidx.localbroadcastmanager:localbroadcastmanager:{strictly 1.1.0} -> 1.1.0 (c)
+--- androidx.viewpager2:viewpager2:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.room:room-ktx:{strictly 2.4.3} -> 2.4.3 (c)
+--- cn.therouter:router:{strictly 1.2.2} -> 1.2.2 (c)
+--- com.youth.banner:banner:{strictly 2.0.11} -> 2.0.11 (c)
+--- com.github.bumptech.glide:glide:{strictly 4.11.0} -> 4.11.0 (c)
+--- com.squareup.retrofit2:adapter-rxjava2:{strictly 2.3.0} -> 2.3.0 (c)
+--- com.squareup.retrofit2:retrofit:{strictly 2.9.0} -> 2.9.0 (c)
+--- io.reactivex.rxjava2:rxandroid:{strictly 2.0.1} -> 2.0.1 (c)
+--- com.trello.rxlifecycle2:rxlifecycle:{strictly 2.1.0} -> 2.1.0 (c)
+--- com.trello.rxlifecycle2:rxlifecycle-components:{strictly 2.1.0} -> 2.1.0 (c)
+--- org.greenrobot:eventbus:{strictly 3.0.0} -> 3.0.0 (c)
+--- com.ccos.cooui:cache:{strictly 0.0.1-tzl-10} -> 0.0.1-tzl-10 (c)
+--- swaiotos:sal:{strictly 1.0.718} -> 1.0.718 (c)
+--- com.ccos.cooui:logger:{strictly 1.7-test-1} -> 1.7-test-1 (c)
+--- swaiotos.service:push:{strictly 1.0.636} -> 1.0.636 (c)
+--- ccos.core:push-lib:{strictly 4.1.7} -> 4.1.7 (c)
+--- com.coocaa.player:plugin-sdk:{strictly 2.0.0.1-SNAPSHOT} -> 2.0.0.1-SNAPSHOT (c)
+--- com.squareup.okhttp3:logging-interceptor:{strictly 3.4.1} -> 3.4.1 (c)
+--- com.ccos.cooui:http:{strictly 0.0.1-tzl-10} -> 0.0.1-tzl-10 (c)
+--- com.localebro:okhttpprofiler:{strictly 1.0.8} -> 1.0.8 (c)
+--- com.coocaa.support:bee-tv:{strictly 2.0.6} -> 2.0.6 (c)
+--- ccos.sdk:softsettingssdk:{strictly 1.3.2} -> 1.3.2 (c)
+--- ccos.sdk:twocentersdk:{strictly 1.2.4} -> 1.2.4 (c)
+--- com.tencent.tav:libpag:{strictly 4.3.62} -> 4.3.62 (c)
+--- org.jetbrains.anko:anko-sdk15:{strictly 0.9.1} -> 0.9.1 (c)
+--- androidx.work:work-runtime-ktx:{strictly 2.7.1} -> 2.7.1 (c)
+--- androidx.fragment:fragment-ktx:{strictly 1.5.4} -> 1.5.4 (c)
+--- com.squareup.okhttp3:okhttp-sse:{strictly 3.14.8} -> 3.14.8 (c)
+--- com.coocaa.sdk:protocol:{strictly 2.1.6} -> 2.1.6 (c)
+--- com.github.yannecer:NCalendar:{strictly 6.0.0} -> 6.0.0 (c)
+--- com.github.youlookwhat:ByRecyclerView:{strictly 1.1.6} -> 1.1.6 (c)
+--- com.airbnb.android:lottie:{strictly 5.2.0} -> 5.2.0 (c)
+--- org.jetbrains.kotlin:kotlin-stdlib:{strictly 1.8.22} -> 1.8.22 (c)
+--- androidx.annotation:annotation:{strictly 1.4.0} -> 1.4.0 (c)
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:{strictly 1.8.22} -> 1.8.22 (c)
+--- androidx.room:room-common:{strictly 2.4.3} -> 2.4.3 (c)
+--- androidx.room:room-runtime:{strictly 2.4.0} -> 2.4.0 (c)
+--- org.jetbrains.kotlinx:kotlinx-coroutines-android:{strictly 1.6.0} -> 1.6.0 (c)
+--- com.google.code.gson:gson:{strictly 2.10.1} -> 2.10.1 (c)
+--- androidx.appcompat:appcompat:{strictly 1.4.1} -> 1.4.1 (c)
+--- com.github.bumptech.glide:gifdecoder:{strictly 4.11.0} -> 4.11.0 (c)
+--- com.github.bumptech.glide:disklrucache:{strictly 4.11.0} -> 4.11.0 (c)
+--- com.github.bumptech.glide:annotations:{strictly 4.11.0} -> 4.11.0 (c)
+--- androidx.fragment:fragment:{strictly 1.5.4} -> 1.5.4 (c)
+--- androidx.vectordrawable:vectordrawable-animated:{strictly 1.1.0} -> 1.1.0 (c)
+--- androidx.exifinterface:exifinterface:{strictly 1.3.2} -> 1.3.2 (c)
+--- io.reactivex.rxjava2:rxjava:{strictly 2.2.6} -> 2.2.6 (c)
+--- com.trello.rxlifecycle2:rxlifecycle-android:{strictly 2.1.0} -> 2.1.0 (c)
+--- com.tencent.shadow2.core:common:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.tencent.shadow2.dynamic:host:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.tencent.shadow2.dynamic:host-multi-loader-ext:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.alibaba:fastjson:{strictly 1.2.32} -> 1.2.32 (c)
+--- com.github.Justson:Downloader:{strictly v5.0.4-androidx} -> v5.0.4-androidx (c)
+--- androidx.legacy:legacy-support-v4:{strictly 1.0.0} -> 1.0.0 (c)
+--- com.coocaa.player:sdk-lite:{strictly 1.0.27.exo2118.aios-SNAPSHOT} -> 1.0.27.exo2118.aios-SNAPSHOT (c)
+--- com.google.protobuf:protobuf-java:{strictly 3.5.1} -> 3.5.1 (c)
+--- com.tencent.shadow2.dynamic:manager-multi-loader-ext:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.tencent.shadow2.dynamic:manager:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.tencent.shadow2.core:manager:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.tencent.shadow2.dynamic:loader:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.coocaa.player:plugin-api:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.multidex:multidex:{strictly 2.0.0} -> 2.0.0 (c)
+--- com.blankj:utilcodex:{strictly 1.31.1} -> 1.31.1 (c)
+--- com.ccos.cooui:apkdownload:{strictly 0.0.1-tzl-1} -> 0.0.1-tzl-1 (c)
+--- androidx.core:core-ktx:{strictly 1.8.0} -> 1.8.0 (c)
+--- androidx.activity:activity-ktx:{strictly 1.5.0} -> 1.5.0 (c)
+--- swaiotos.service:user:{strictly 1.0.636} -> 1.0.636 (c)
+--- ccos.sdk:multimodelsdk:{strictly 2.0.100} -> 2.0.100 (c)
+--- com.squareup.okhttp3:okhttp:{strictly 3.14.8} -> 3.14.8 (c)
+--- swaiotos.base:skysdk:{strictly 1.0.718} -> 1.0.718 (c)
+--- ccos.sdk:rgb-system-sdk:{strictly 1.0.29} -> 1.0.29 (c)
+--- com.coocaa.support:bee:{strictly 2.0.6} -> 2.0.6 (c)
+--- androidx.core:core:{strictly 1.8.0} -> 1.8.0 (c)
+--- androidx.customview:customview:{strictly 1.1.0} -> 1.1.0 (c)
+--- androidx.collection:collection:{strictly 1.2.0} -> 1.2.0 (c)
+--- org.jetbrains.anko:anko-common:{strictly 0.9.1} -> 0.9.1 (c)
+--- androidx.work:work-runtime:{strictly 2.7.1} -> 2.7.1 (c)
+--- androidx.lifecycle:lifecycle-viewmodel:{strictly 2.5.1} -> 2.5.1 (c)
+--- androidx.collection:collection-ktx:{strictly 1.2.0} -> 1.2.0 (c)
+--- androidx.lifecycle:lifecycle-livedata-core-ktx:{strictly 2.5.1} -> 2.5.1 (c)
+--- androidx.savedstate:savedstate-ktx:{strictly 1.2.0} -> 1.2.0 (c)
+--- androidx.lifecycle:lifecycle-runtime:{strictly 2.5.1} -> 2.5.1 (c)
+--- androidx.cardview:cardview:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.coordinatorlayout:coordinatorlayout:{strictly 1.1.0} -> 1.1.0 (c)
+--- androidx.dynamicanimation:dynamicanimation:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.annotation:annotation-experimental:{strictly 1.1.0} -> 1.1.0 (c)
+--- androidx.transition:transition:{strictly 1.4.1} -> 1.4.1 (c)
+--- androidx.vectordrawable:vectordrawable:{strictly 1.1.0} -> 1.1.0 (c)
+--- com.squareup.leakcanary:leakcanary-object-watcher-android:{strictly 2.10} -> 2.10 (c)
+--- com.squareup.leakcanary:shark-android:{strictly 2.10} -> 2.10 (c)
+--- com.squareup.leakcanary:leakcanary-object-watcher-android-core:{strictly 2.10} -> 2.10 (c)
+--- com.squareup.leakcanary:leakcanary-object-watcher-android-androidx:{strictly 2.10} -> 2.10 (c)
+--- com.squareup.leakcanary:leakcanary-object-watcher-android-support-fragments:{strictly 2.10} -> 2.10 (c)
+--- org.jetbrains.kotlin:kotlin-stdlib-common:{strictly 1.8.22} -> 1.8.22 (c)
+--- org.jetbrains:annotations:{strictly 13.0} -> 13.0 (c)
+--- androidx.sqlite:sqlite-framework:{strictly 2.2.0} -> 2.2.0 (c)
+--- androidx.sqlite:sqlite:{strictly 2.2.0} -> 2.2.0 (c)
+--- org.jetbrains.kotlinx:kotlinx-coroutines-core:{strictly 1.6.1} -> 1.6.1 (c)
+--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:{strictly 1.6.1} -> 1.6.1 (c)
+--- androidx.cursoradapter:cursoradapter:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.activity:activity:{strictly 1.5.1} -> 1.5.1 (c)
+--- androidx.appcompat:appcompat-resources:{strictly 1.4.1} -> 1.4.1 (c)
+--- androidx.drawerlayout:drawerlayout:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.savedstate:savedstate:{strictly 1.2.0} -> 1.2.0 (c)
+--- androidx.lifecycle:lifecycle-livedata-core:{strictly 2.5.1} -> 2.5.1 (c)
+--- androidx.lifecycle:lifecycle-viewmodel-savedstate:{strictly 2.5.1} -> 2.5.1 (c)
+--- androidx.loader:loader:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.viewpager:viewpager:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.interpolator:interpolator:{strictly 1.0.0} -> 1.0.0 (c)
+--- org.reactivestreams:reactive-streams:{strictly 1.0.4} -> 1.0.4 (c)
+--- com.tencent.shadow2.dynamic:apk:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.tencent.shadow2.core:utils:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- androidx.media:media:{strictly 1.4.1} -> 1.4.1 (c)
+--- androidx.legacy:legacy-support-core-utils:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.legacy:legacy-support-core-ui:{strictly 1.0.0} -> 1.0.0 (c)
+--- com.google.android.exoplayer:exoplayer-core:{strictly 2.11.8} -> 2.11.8 (c)
+--- com.google.android.exoplayer:exoplayer-hls:{strictly 2.11.8} -> 2.11.8 (c)
+--- com.google.android.exoplayer:exoplayer-dash:{strictly 2.11.8} -> 2.11.8 (c)
+--- com.google.android.exoplayer:exoplayer-smoothstreaming:{strictly 2.11.8} -> 2.11.8 (c)
+--- com.tencent.shadow2.core:load-parameters:{strictly 2.5.0.1-8a951ac7-SNAPSHOT} -> 2.5.0.1-8a951ac7-SNAPSHOT (c)
+--- com.squareup.okio:okio:{strictly 1.17.5} -> 1.17.5 (c)
+--- androidx.versionedparcelable:versionedparcelable:{strictly 1.1.1} -> 1.1.1 (c)
+--- com.google.guava:listenablefuture:{strictly 9999.0-empty-to-avoid-conflict-with-guava} -> 9999.0-empty-to-avoid-conflict-with-guava (c)
+--- androidx.lifecycle:lifecycle-livedata:{strictly 2.5.1} -> 2.5.1 (c)
+--- androidx.startup:startup-runtime:{strictly 1.1.1} -> 1.1.1 (c)
+--- androidx.arch.core:core-common:{strictly 2.1.0} -> 2.1.0 (c)
+--- androidx.lifecycle:lifecycle-common:{strictly 2.5.1} -> 2.5.1 (c)
+--- com.squareup.leakcanary:shark:{strictly 2.10} -> 2.10 (c)
+--- com.squareup.leakcanary:leakcanary-object-watcher:{strictly 2.10} -> 2.10 (c)
+--- com.squareup.leakcanary:leakcanary-android-utils:{strictly 2.10} -> 2.10 (c)
+--- org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:{strictly 1.6.1} -> 1.6.1 (c)
+--- androidx.documentfile:documentfile:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.print:print:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.slidingpanelayout:slidingpanelayout:{strictly 1.2.0} -> 1.2.0 (c)
+--- androidx.swiperefreshlayout:swiperefreshlayout:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.asynclayoutinflater:asynclayoutinflater:{strictly 1.0.0} -> 1.0.0 (c)
+--- androidx.arch.core:core-runtime:{strictly 2.1.0} -> 2.1.0 (c)
+--- com.squareup.leakcanary:shark-graph:{strictly 2.10} -> 2.10 (c)
+--- com.squareup.leakcanary:shark-log:{strictly 2.10} -> 2.10 (c)
--- com.squareup.leakcanary:shark-hprof:{strictly 2.10} -> 2.10 (c)
1)、剔除多余的子模块 (缩减4M)

剔除多余模块,不参与编译打包,大概减小4M
2)、剔除多余的子模块 (缩减6M)

剔除多余模块,不参与编译打包,大概减小6M
3、资源优化
1)、删除未使用的资源 (400K)
资源文件夹上右键—>Refactor—>Remove Unused Resource,之后选择Preview


切记先选Preview进行预览,否则AS会直接进行删除,有可能会误删除资源。


这些就是未使用的资源,不过最好手动校验需不需要删除。
2)、压缩图片资源
AS gradle中实现task压缩图片:
task convertToWebPRealAuto {
doLast {
println("开始真实的WebP转换优化...")
def resDir = file("src/jxw/res")
if (!resDir.exists()) {
println("资源目录不存在: ${resDir.absolutePath}")
return
}
// 创建备份目录
def backupDir = new File(resDir.parentFile, "res_backup_${System.currentTimeMillis()}")
if (!backupDir.exists()) {
backupDir.mkdirs()
}
def convertedFiles = []
def backedUpFiles = []
def originalFilesDeleted = []
def skippedFiles = []
def failedFiles = []
def ignoredFiles = [] // 新增:记录被忽略的文件(转换后变大的文件)
def totalSizeSaved = 0L
def totalOriginalSize = 0L
// 改进的cwebp工具检测
def cwebpAvailable = false
def cwebpPath = ""
println("🔍 正在检测cwebp工具...")
// 方法1: 直接尝试执行cwebp命令(最可靠的方法)
try {
def testProcess = ["cwebp", "-version"].execute()
testProcess.waitFor()
if (testProcess.exitValue() == 0) {
cwebpPath = "cwebp"
cwebpAvailable = true
println("✅ 方法1: 通过直接执行找到cwebp工具")
}
} catch (Exception e) {
println("❌ 方法1失败: ${e.message}")
}
// 方法2: 如果方法1失败,尝试使用where命令(Windows专用)
if (!cwebpAvailable) {
try {
def whereProcess = ["cmd", "/c", "where", "cwebp"].execute()
whereProcess.waitFor()
if (whereProcess.exitValue() == 0) {
def output = whereProcess.text.trim()
if (output && !output.contains("INFO:") && output.contains("cwebp")) {
cwebpPath = output.split("
")[0].trim()
cwebpAvailable = true
println("✅ 方法2: 通过where命令找到cwebp工具: $cwebpPath")
}
}
} catch (Exception e) {
println("❌ 方法2失败: ${e.message}")
}
}
// 方法3: 检查常见安装路径
if (!cwebpAvailable) {
def commonPaths = [
"C:\libwebp\bin\cwebp.exe",
"C:\Program Files\libwebp\bin\cwebp.exe",
"C:\Program Files (x86)\libwebp\bin\cwebp.exe",
"D:\libwebp-1.6.0-windows-x64\bin\cwebp.exe",
"/usr/local/bin/cwebp",
"/usr/bin/cwebp",
"/opt/homebrew/bin/cwebp" // M1 Mac
]
for (path in commonPaths) {
def file = new File(path)
if (file.exists()) {
cwebpPath = path
cwebpAvailable = true
println("✅ 方法3: 通过路径检查找到cwebp工具: $path")
break
}
}
}
// 方法4: 搜索整个系统(最后的手段)
if (!cwebpAvailable) {
println("🔍 正在搜索系统cwebp工具...")
try {
// 在Windows上搜索
def searchProcess
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
searchProcess = ["cmd", "/c", "dir", "/s", "/b", "cwebp.exe"].execute()
} else {
searchProcess = ["find", "/", "-name", "cwebp", "-type", "f", "2>/dev/null"].execute()
}
searchProcess.waitFor(5000) // 最多等待5秒
if (searchProcess.exitValue() == 0) {
def output = searchProcess.text.trim()
if (output) {
def paths = output.split("
")
if (paths.size() > 0) {
cwebpPath = paths[0].trim()
cwebpAvailable = true
println("✅ 方法4: 通过系统搜索找到cwebp工具: $cwebpPath")
}
}
}
} catch (Exception e) {
println("❌ 方法4失败: ${e.message}")
}
}
if (!cwebpAvailable) {
println("❌ 未找到cwebp工具,无法进行真实的WebP转换")
println("")
println("💡 请按以下步骤安装WebP工具:")
println("1. 下载 libwebp: https://developers.google.com/speed/webp/download")
println("2. 解压到 C:\libwebp")
println("3. 将 C:\libwebp\bin 添加到系统PATH环境变量")
println("4. 重启命令行或IDE")
println("5. 验证安装: 在命令行输入 'cwebp -version'")
println("")
println("或者运行: ./gradlew installWebPTools 查看详细安装指南")
return
}
// 验证cwebp工具是否真正可用
println("🔍 验证cwebp工具功能...")
try {
def verifyProcess
if (cwebpPath.endsWith(".exe")) {
verifyProcess = ["cmd", "/c", cwebpPath, "-version"].execute()
} else {
verifyProcess = [cwebpPath, "-version"].execute()
}
verifyProcess.waitFor()
if (verifyProcess.exitValue() == 0) {
println("✅ cwebp工具验证成功: ${verifyProcess.text.trim()}")
} else {
println("❌ cwebp工具验证失败")
cwebpAvailable = false
}
} catch (Exception e) {
println("❌ cwebp工具验证异常: ${e.message}")
cwebpAvailable = false
}
if (!cwebpAvailable) {
println("❌ cwebp工具不可用,请检查安装")
return
}
// 遍历所有资源目录
resDir.eachDir { dir ->
if (dir.name.startsWith('mipmap-') || dir.name.startsWith('drawable-')) {
println("处理目录: ${dir.name}")
// 创建对应的备份目录
def backupSubDir = new File(backupDir, dir.name)
if (!backupSubDir.exists()) {
backupSubDir.mkdirs()
}
dir.eachFile { file ->
if (file.name.endsWith('.png') || file.name.endsWith('.jpg') || file.name.endsWith('.jpeg')) {
def fileName = file.name.substring(0, file.name.lastIndexOf('.'))
def webpFile = new File(dir, "${fileName}.webp")
def fileSize = file.length()
totalOriginalSize += fileSize
// 先备份原始文件
def backupFile = new File(backupSubDir, file.name)
try {
file.withInputStream { input ->
backupFile.withOutputStream { output ->
output << input
}
}
backedUpFiles.add(file.name)
println("✓ 已备份: ${file.name}")
} catch (Exception e) {
println("⚠ 备份失败: ${file.name} - ${e.message}")
return // 跳过这个文件
}
// 如果WebP文件已存在且有效,跳过转换
if (webpFile.exists() && webpFile.length() > 100) {
def webpSize = webpFile.length()
skippedFiles.add([file: file.name, reason: "有效的WebP文件已存在(${webpSize} bytes)"])
println("⏭ 跳过: ${file.name} (有效的WebP文件已存在)")
return
}
println("🔄 开始转换: ${file.name} → ${webpFile.name}")
try {
// 使用cwebp进行真实的WebP转换
def cmd = []
if (cwebpPath.endsWith(".exe")) {
cmd = ["cmd", "/c", cwebpPath, "-q", "80", file.absolutePath, "-o", webpFile.absolutePath]
} else {
cmd = [cwebpPath, "-q", "80", file.absolutePath, "-o", webpFile.absolutePath]
}
println(" 执行命令: ${cmd.join(' ')}")
def process = cmd.execute()
process.waitFor()
if (process.exitValue() == 0) {
// 检查生成的WebP文件是否有效
if (webpFile.exists() && webpFile.length() > 100) {
def webpSize = webpFile.length()
def savings = fileSize - webpSize
// 关键修复:检查转换后文件是否变大
if (webpSize < fileSize) {
// 转换后文件变小,保留WebP文件
totalSizeSaved += savings
convertedFiles.add(file.name)
println("✅ 转换成功: ${file.name} (${fileSize} → ${webpSize} bytes, 节省 ${String.format("%.1f", savings/1024.0)} KB)")
// 转换成功后删除原始文件
if (file.delete()) {
originalFilesDeleted.add(file.name)
println(" 🗑️ 已删除原始文件")
} else {
println(" ⚠️ 删除原始文件失败")
}
} else {
// 转换后文件变大,忽略转换结果
def increase = webpSize - fileSize
ignoredFiles.add([file: file.name, originalSize: fileSize, webpSize: webpSize, increase: increase])
println("⚠️ 忽略转换: ${file.name} (转换后变大: ${fileSize} → ${webpSize} bytes, 增加 ${String.format("%.1f", increase/1024.0)} KB)")
// 删除生成的WebP文件,保留原始文件
if (webpFile.delete()) {
println(" 🗑️ 已删除无效的WebP文件")
}
}
} else {
failedFiles.add([file: file.name, reason: "生成的WebP文件无效或太小"])
println("❌ 转换失败: 生成的WebP文件无效")
if (webpFile.exists()) {
webpFile.delete() // 清理无效文件
}
}
} else {
def errorOutput = process.err.text ?: process.text ?: "未知错误"
failedFiles.add([file: file.name, reason: "cwebp转换失败: ${errorOutput.trim()}"])
println("❌ 转换失败: ${errorOutput.trim()}")
if (webpFile.exists()) {
webpFile.delete() // 清理失败的文件
}
}
} catch (Exception e) {
failedFiles.add([file: file.name, reason: "执行错误: ${e.message}"])
println("❌ 转换异常: ${e.message}")
}
}
}
}
}
// 统计结果
def backupFileCount = 0
backupDir.eachDirRecurse { dir ->
backupFileCount += dir.listFiles().size()
}
// 输出详细报告
println("
" + "="*60)
println("📊 真实WebP转换报告")
println("="*60)
println("🛠️ 使用的工具: $cwebpPath")
println("📁 处理目录: ${resDir.absolutePath}")
println("💾 原始总大小: ${String.format("%.2f", totalOriginalSize/1024/1024)} MB")
println("💰 实际节省: ${String.format("%.2f", totalSizeSaved/1024/1024)} MB")
if (totalOriginalSize > 0) {
println("📈 压缩率: ${String.format("%.1f", totalSizeSaved*100/totalOriginalSize)}%")
}
println("✅ 成功转换: ${convertedFiles.size()} 个文件")
println("⚠️ 忽略转换: ${ignoredFiles.size()} 个文件(转换后变大)")
println("❌ 转换失败: ${failedFiles.size()} 个文件")
println("⏭ 跳过文件: ${skippedFiles.size()} 个")
println("💾 备份文件: ${backupFileCount} 个")
if (ignoredFiles.size() > 0) {
println("
🟡 忽略文件详情(转换后变大):")
ignoredFiles.take(5).each { ignore ->
println(" - ${ignore.file}: ${ignore.originalSize} → ${ignore.webpSize} bytes (增加 ${String.format("%.1f", ignore.increase/1024.0)} KB)")
}
if (ignoredFiles.size() > 5) {
println(" ... 还有 ${ignoredFiles.size() - 5} 个文件被忽略")
}
}
if (failedFiles.size() > 0) {
println("
🔴 失败文件详情:")
failedFiles.take(5).each { fail ->
println(" - ${fail.file}: ${fail.reason}")
}
if (failedFiles.size() > 5) {
println(" ... 还有 ${failedFiles.size() - 5} 个文件转换失败")
}
}
if (backupFileCount > 0) {
println("
✅ 备份完成!目录: ${backupDir.absolutePath}")
println("💡 如需恢复原始文件,可从备份目录复制")
} else {
backupDir.deleteDir()
println("ℹ️ 备份目录已清理")
}
}
}
图片压缩功能:忽略掉转换后反而变大的情况
新增忽略文件统计:添加了列表来记录转换后变大的文件文件大小比较逻辑:在转换成功后检查
ignoredFiles,只有当WebP文件确实更小时才保留智能处理:如果转换后文件变大,会自动删除生成的WebP文件,保留原始PNG/JPG文件详细报告:在统计报告中显示被忽略的文件数量和详情清晰的日志:用”⚠️ 忽略转换”标识这类情况,并显示具体增加的大小
webpSize < fileSize
现在当遇到类似这种转换后反而变大的情况时,系统会自动忽略转换结果,保留原始文件,避免不必要的体积增加。
mask_voice.png
运行结果如下:
> Task :Study:convertToWebPRealAuto
开始真实的WebP转换优化...
🔍 正在检测cwebp工具...
❌ 方法1失败: Cannot run program "cwebp": CreateProcess error=2, 系统找不到指定的文件。
✅ 方法3: 通过路径检查找到cwebp工具: D:libwebp-1.6.0-windows-x64incwebp.exe
🔍 验证cwebp工具功能...
✅ cwebp工具验证成功: 1.6.0
libsharpyuv: 0.4.2
处理目录: drawable-hdpi
✓ 已备份: account_avatar_default.png
🔄 开始转换: account_avatar_default.png → account_avatar_default.webp
执行命令: cmd /c D:libwebp-1.6.0-windows-x64incwebp.exe -q 80 E:projectstudyV3.0-Study-AitutorappStudysrcjxw
esdrawable-hdpiaccount_avatar_default.png -o E:projectstudyV3.0-Study-AitutorappStudysrcjxw
esdrawable-hdpiaccount_avatar_default.webp
✅ 转换成功: account_avatar_default.png (22009 → 8414 bytes, 节省 13.3 KB)
🗑️ 已删除原始文件
......
✓ 已备份: ic_gm_home_popu.png
🔄 开始转换: ic_gm_home_popu.png → ic_gm_home_popu.webp
执行命令: cmd /c D:libwebp-1.6.0-windows-x64incwebp.exe -q 80 E:projectstudyV3.0-Study-AitutorappStudysrcjxw
esdrawable-hdpiic_gm_home_popu.png -o E:projectstudyV3.0-Study-AitutorappStudysrcjxw
esdrawable-hdpiic_gm_home_popu.webp
⚠️ 忽略转换: ic_gm_home_popu.png (转换后变大: 129 → 154 bytes, 增加 0.0 KB)
🗑️ 已删除无效的WebP文件
......
✓ 已备份: zwzx.png
🔄 开始转换: zwzx.png → zwzx.webp
执行命令: cmd /c D:libwebp-1.6.0-windows-x64incwebp.exe -q 80 E:projectstudyV3.0-Study-AitutorappStudysrcjxw
esdrawable-xhdpizwzx.png -o E:projectstudyV3.0-Study-AitutorappStudysrcjxw
esdrawable-xhdpizwzx.webp
✅ 转换成功: zwzx.png (19853 → 2978 bytes, 节省 16.5 KB)
🗑️ 已删除原始文件
============================================================
📊 真实WebP转换报告
============================================================
🛠️ 使用的工具: D:libwebp-1.6.0-windows-x64incwebp.exe
📁 处理目录: E:projectstudyV3.0-Study-AitutorappStudysrcjxw
es
💾 原始总大小: 7.11 MB
💰 实际节省: 4.47 MB
📈 压缩率: 62.8%
✅ 成功转换: 379 个文件
⚠️ 忽略转换: 18 个文件(转换后变大)
❌ 转换失败: 0 个文件
⏭ 跳过文件: 0 个
💾 备份文件: 397 个
十、总结
| 序号 | 瘦身优化说明 | 大小 MB |
|---|---|---|
| 1 | 瘦身前 | 117.5 |
| 2 | 剔除字体文件以及压缩部分png图片 | 99.3 |
| 3 | 剔除多余模块参与编译打包 | 95.4 |
| 4 | 剔除多余模块参与编译打包 | 89.6 |
| 5 | 核心子模块压缩图片及删除不用资源 | 88.5 |
| 6 | 删除不用的资源 | 88.1 |



