批量勾选删除 MIUI / HyperOS 系统相册(com.miui.gallery)里的空相册的 LSPosed 模块。删除走相册自身的删除接口,云端相册会同步删除。
- 在相册「相册」Tab 的右上角(标题「相册」右边、排序图标左边)注入一个 「清理」 按钮
- 点击后弹出「清理空相册」列表,自动扫描出所有空相册(照片数为 0)
- 支持全选 / 全不选 / 单独勾选,底部显示当前勾选数量
- 批量删除,可选项同时删除仍占磁盘的空文件夹
- 列表可滚动,不会闪退
一个相册满足以下条件即判定为「空相册」,会出现在清理列表里:
- 相册对应的
album表中该相册没有被删除状态的储存项(cloud表按localGroupId统计,排除serverStatus='deleted'),即一张照片都没有 - 磁盘上对应文件夹不存在,或存在但没有任何媒体文件(jpg/png/mp4 等常见扩展名)
- 不在受保护名单内
受保护相册(永远不会出现在清理列表):
sdcard、存储卡、SD卡、最近删除、回收站、收藏、最爱,以及DCIM、DCIM/Camera、DCIM/Screenshots、DCIM/screenrecorder、Pictures、Download、Movies等系统目录相册。
- 技术路线:现代
libxposedAPI 102(io.github.libxposed.api.XposedModule),入口类com.jp.emptyalbumcleaner.ModuleEntry - 模块元数据:
META-INF/xposed/{module.prop, java_init.list, scope.list},作用域固定为com.miui.gallery - Hook 点:
AlbumTabFragment.onResume—— 每次进入相册 Tab 时把「清理」按钮挂载到宿主窗口(用独立 FrameLayout 叠加在 action bar 区域,不侵入相册视图树) - 扫描:直接在相册进程里打开
/data/data/com.miui.gallery/databases/gallery.db(同为相册 uid,无权限问题),统计cloud表各localGroupId的媒体数,再检查磁盘文件夹 - 删除:通过相册自己的 ContentProvider
content://com.miui.gallery.cloud.provider/album按_id删除,走的是与相册自身界面完全一致的删除逻辑,所以记录被干净移除,且云端相册会同步删除
说明:删除接口实测有效(记录直接从
album表移除,不留墓碑标记)。磁盘文件夹不会随记录联动删除,因此清理列表提供「同时删除空文件夹」选项,由模块自行递归删除(仅删除不含任何媒体文件的目录)。
- 在 LSPosed Manager 中启用「空相册清理」
- 作用域勾选 「系统相册」
com.miui.gallery - 强制停止相册,再重新打开
- 打开系统相册 → 切到 「相册」Tab
- 右上角标题旁点 「清理」
- 列表会显示所有空相册(名称 + 状态说明,如「云端相册 · 文件夹不存在,仅剩记录」)
- 勾选要删除的(或点「全选/全不选」全选)→ 点底部的 「删除所选(N)」
- 确认后批量删除
⚠️ 若登录了小米云,删除对应云端相册不可恢复,请谨慎勾选;已删除的相册不会进回收站。
本项目使用 build_module.sh 独立构建流水线(无需 Gradle、无需 Android Studio):
aapt2 link → javac(libxposed API 以 compileOnly stub 参与编译)→ d8 → zip 打包 → zipalign → apksigner 签名
| 组件 | 要求 | 说明 |
|---|---|---|
| Android SDK | platforms/android-36.1 + build-tools/36.1.0 |
脚本里 SDK 路径写死为 C:/Users/<你>/AppData/Local/Android/Sdk,如不同请改 build_module.sh 顶部 SDK= |
| JDK | 17(Temurin 等均可) | 脚本会找 tools/jdk-17*;没有则需你系统的 java/javac 可用 |
| Android 平台 jar | platforms/android-36.1/android.jar |
编译 classpath |
脚本假设 SDK 在默认位置。如果你的 SDK 在别处,编辑
build_module.sh第 5-6 行的SDK=与BT=、AJ=指向实际路径。
# 在项目根目录
bash build_module.sh脚本步骤:
- 编译 libxposed API stub —— 把
api_stub/(compileOnly 用,不进 APK)和tools/maven/api_src官方源码编译成build/libxposed_api.jar - aapt2 link —— 用
AndroidManifest.xml生成中间base.apk - javac —— 编译
app/src/main/java下所有模块类(classpath 含 android.jar + libxposed api jar) - d8 —— 把 classes 转 dex(
--min-api 27) - 打包 —— dex 与
META-INF/xposed/*(模块元数据)写入 APK - zipalign + apksigner 签名 —— 用
keystore/debug.keystore(首次自动生成)
产出:build/EmptyAlbumCleaner.apk
- 首次运行会自动生成调试签名
keystore/debug.keystore(别名eac,密码android) - 重新构建会复用同一密钥,所以可以覆盖安装同一台手机上的旧版
- 若要分发给别人,建议换成自己的正式签名(改
build_module.sh里 apksigner 参数)
# 检查模块元数据是否打进 APK(缺失会导致 LSPosed 不识别)
unzip -l build/EmptyAlbumCleaner.apk | grep -E "META-INF/xposed"
# 应看到:
# META-INF/xposed/module.prop
# META-INF/xposed/java_init.list
# META-INF/xposed/scope.list
# 检查签名
/你的SDK/build-tools/36.1.0/apksigner.bat verify --print-certs build/EmptyAlbumCleaner.apkadb install -r build/EmptyAlbumCleaner.apk
# 或在手机上用系统安装器打开 APK安装后在 LSPosed 管理器 → 模块 里勾选「空相册清理」,作用域勾选 com.miui.gallery,强停相册后重开即可。
| 问题 | 原因 / 解决 |
|---|---|
javac: 找不到符号 android.* |
classpath 里 android.jar 路径不对;确认 SDK/platforms/android-36.1/android.jar 存在且 build_module.sh 里 AJ= 正确 |
aapt2: error: failed processing manifest |
manifest 语法错(如 description 属性不能直接写中文字符串),检查 app/src/main/AndroidManifest.xml |
d8: 找不到类 |
build/classes 为空或 javac 没产出;先修 javac 报错 |
| 手机上模块不生效 | 确认 APK 内 META-INF/xposed/* 齐全(第 4 步验证)、模块已启用、作用域已勾选、相册已强停/重启 |
| LSPosed 管理器里看不到模块 | 你的 LSPosed 必须是 v2.x(现代 API);旧版框架不识别 API 102 模块 |
app/
└── src/main/
├── AndroidManifest.xml # 应用声明(去掉旧 xposed meta-data)
├── java/com/jp/emptyalbumcleaner/
│ ├── ModuleEntry.java # API 102 入口:hook AlbumTabFragment,挂「清理」按钮
│ ├── AlbumScanner.java # 扫描 gallery.db + 磁盘,判定空相册
│ ├── CleanDialog.java # 勾选列表 + 批量删除对话框
│ └── CleanActivity.java # 独立的说明页(桌面图标可打开)
└── resources/META-INF/xposed/ # 现代模块元数据
├── module.prop # minApiVersion=101, targetApiVersion=102, scope 固定
├── java_init.list # com.jp.emptyalbumcleaner.ModuleEntry
└── scope.list # com.miui.gallery
api_stub/ # 编译期 libxposed API stub(compileOnly,不打进 APK)
build_module.sh # 构建脚本
- 适配 LSPosed v2.x(现代 API 102 框架)。旧式 Xposed 模块(
IXposedHookLoadPackage+assets/xposed_init+xposedmodulemeta-data)在新版 LSPosed 里不会被识别为模块——早期版本即因此无法出现在管理器列表。 - 目标:小米相册
com.miui.gallery(HyperOS / MIUI)。 - 关键类(
AlbumTabFragment、gallery.db表结构)在不同相册版本可能变化,若相册大版本更新后按钮不出现或列表异常,请重新验证 Hook 点。
Q: LSPosed 管理器里看不到「空相册清理」?
A: 确认你的 LSPosed 是 v2.x(现代 API)。模块必须包含 META-INF/xposed/* 元数据,且入口类继承 XposedModule。安装后需要强制停止相册或重启手机。
Q: 点「清理」没反应 / 列表为空?
A: 检查作用域是否勾选了 com.miui.gallery;若确实没有空相册会提示「没有发现空相册」。
Q: 列表滚动会闪退?
A: 早期版本列表项复用有 bug(CheckBox 强转为 ViewGroup)。当前版本已改为每次 new 行,滚动安全。
Q: 删错了能恢复吗? A: 走相册原生删除接口,云端相册不可恢复;请务必先只勾选确认无误的相册。