Mode d’emploi
- Copiez la commande et examinez le paquet avant de l’installer dans votre environnement agent.
- Consultez le document original pour les outils, identifiants et environnements requis.
- Décrivez les entrées et le livrable souhaité ; comparez la sortie aux sources.
npx skills add https://github.com/te-quanbzhang/skills-pool --skill android-gallery-bugfix-skillAvant de commencer
- L’installation n’inclut ni abonnement au modèle, ni quota API, ni accès aux fichiers.
- Étoiles et installations reflètent l’activité, pas un test indépendant de qualité ou de sécurité.
Rendu depuis le dépôt source en conservant titres, exemples, code, tableaux, liens et images.
Android Gallery Bugfix Skill
适用范围
用于 Android 项目中 Gallery / 相册 / 图片选择 / 图片浏览 / 图片保存相关问题的排查、修复和验证,包括但不限于:
- 相册打不开
- 图片列表为空
- 图片加载慢
- 缩略图显示异常
- 图片方向错误
- 图片保存失败
- 图片删除失败
- 图片扫描不到
- 图片选择失败
- Android 10+ 分区存储问题
- MediaStore 查询异常
- Glide / Coil / Picasso 加载异常
- OOM / Bitmap 内存问题
- 大图预览卡顿
- EXIF 信息异常
- 视频和图片混合列表异常
- 权限问题
Bugfix 总体原则
相册类问题通常和以下因素相关:
- 存储权限
- Android 版本差异
- Scoped Storage / 分区存储
- MediaStore 查询
- URI 权限
- 图片解码
- 缩略图缓存
- EXIF 方向
- 文件路径和 content uri 混用
- 大图内存优化
- 异步加载和 RecyclerView 复用
处理时必须区分:
- 文件不存在
- 没有权限
- MediaStore 未扫描
- URI 不可访问
- 解码失败
- 缩略图加载失败
- UI 绑定错误
常见问题分类
1. 相册列表为空
重点检查:
- 是否有 READ 权限
- Android 13+ 是否申请 READMEDIAIMAGES / READMEDIAVIDEO
- Android 10+ 是否适配 Scoped Storage
- MediaStore 查询条件是否过窄
- selection / sortOrder 是否错误
- 查询的是 Images 还是 Files
- 是否只查外部存储
- 是否被 MIME type 过滤掉
- 文件是否已被 MediaScanner 扫描
权限差异:
Android 12 及以下:
- READ_EXTERNAL_STORAGE
Android 13+:
- READ_MEDIA_IMAGES
- READ_MEDIA_VIDEO
- READ_MEDIA_AUDIO
Android 14+:
- READ_MEDIA_VISUAL_USER_SELECTED2. 图片加载失败
重点检查:
- URI 是否有效
- 文件是否存在
- 是否有 URI 权限
- 是否在后台线程解码
- 图片是否损坏
- 是否为 HEIC / WEBP / RAW 等特殊格式
- Glide / Coil / Picasso 是否配置正确
- RecyclerView 复用导致错图
常见日志关键字:
FileNotFoundException
SecurityException
BitmapFactory
ImageDecoder
GlideException
Coil
Picasso
openInputStream
decodeStream3. 图片方向错误
重点检查:
- 是否读取 EXIF orientation
- 是否对 content uri 支持 EXIF 读取
- 是否只处理 file path,未处理 input stream
- 前置摄像头图片是否镜像
- 压缩后是否丢失 EXIF
常用类:
ExifInterface
Matrix
BitmapFactory
ImageDecoder修复建议:
- 从 InputStream 读取 EXIF
- 保存压缩图时保留必要 EXIF
- 显示层优先让图片加载库处理 orientation
- 避免重复旋转
4. 图片保存失败
重点检查:
- Android 版本
- 是否直接写公共目录
- 是否使用 MediaStore
- 是否设置 RELATIVE_PATH
- 是否设置 IS_PENDING
- OutputStream 是否 close
- 文件名是否非法
- 存储空间是否不足
- 是否需要触发媒体扫描
Android 10+ 推荐:
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
ContentValues
RELATIVE_PATH
DISPLAY_NAME
MIME_TYPE
IS_PENDING
contentResolver.openOutputStream(uri)保存后:
IS_PENDING = 05. 删除失败
重点检查:
- Android 10+ 是否需要用户授权删除
- 是否使用 ContentResolver.delete
- 是否捕获 RecoverableSecurityException
- 是否处理系统确认弹窗
- 是否有写入权限
- 是否删除后刷新列表
Android 11+ 常见方式:
MediaStore.createDeleteRequest(...)6. OOM / 加载卡顿
重点检查:
- 是否直接加载原图
- 是否根据 View 尺寸采样
- 是否在主线程解码
- RecyclerView 是否复用
- 是否开启过大的缓存
- 是否一次性查询或加载全部图片
- 是否大图预览没有 tile / subsampling
修复建议:
- 使用 Glide / Coil 缩放加载
- RecyclerView 分页
- 使用 Paging
- 大图使用 subsampling
- 避免 Bitmap 常驻内存
- onViewRecycled 中取消加载
- 使用缩略图优先
推荐排查流程
Step 1:确认复现条件
记录:
- 设备型号
- Android 版本
- 是否 Android 10+
- 是否 Android 13+
- 是否首次安装
- 权限是否授权
- 是图片、视频还是混合媒体
- 来源:相机拍摄 / 下载 / 微信 / 浏览器 / 系统截图
- 文件格式:JPG / PNG / HEIC / WEBP / GIF / RAW
- 是列表页、选择页、预览页还是保存页
- 复现概率
Step 2:收集日志
推荐命令:
adb logcat -v time > gallery_bug.log
adb shell dumpsys package your.package.name > package_info.txt
adb shell dumpsys media.provider > media_provider.txt检查文件:
adb shell ls -l /sdcard/DCIM/
adb shell ls -l /sdcard/Pictures/
adb shell ls -l /sdcard/Download/检查权限:
adb shell appops get your.package.name
adb shell dumpsys package your.package.name | grep permissionStep 3:定位层级
按照以下顺序判断:
- App 是否有读取权限
- MediaStore 是否能查询到数据
- 查询条件是否正确
- URI 是否可访问
- 图片是否可解码
- 缩略图是否正确加载
- RecyclerView 绑定是否正确
- 图片预览是否处理方向和大图
- 保存 / 删除是否符合 Android 版本要求
MediaStore 查询检查项
重点检查:
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
MediaStore.Files.getContentUri("external")
ContentResolver.query(...)必须确认:
- projection 不为空或字段存在
- selection 正确
- sortOrder 正确
- cursor 正确 close
_ID正确拼接 content uri- DATA 字段在 Android 10+ 不应强依赖
- 不要直接依赖绝对路径
推荐使用:
ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)Android 版本适配重点
Android 9 及以下
常见权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />Android 10
重点:
- Scoped Storage
- MediaStore 写入
- 不建议直接使用 file path 访问公共目录
- 可临时使用 requestLegacyExternalStorage,但不推荐长期依赖
Android 11 / 12
重点:
- 更严格的公共目录访问
- 删除 / 修改他人媒体需要用户授权
- MANAGEEXTERNALSTORAGE 不应滥用
Android 13+
重点权限:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />Android 14+
重点:
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />需要处理用户只授权部分照片的情况。
图片加载库检查
Glide
重点检查:
Glide.with(context)
.load(uri)
.thumbnail(...)
.override(width, height)
.into(imageView)注意:
- Fragment / View 生命周期
- RecyclerView 复用
- 清理请求
- 加载 content uri
- placeholder / error 图
Coil
重点检查:
imageView.load(uri) {
crossfade(true)
size(width, height)
}注意:
- lifecycle
- memory cache
- disk cache
- SVG / GIF / HEIC 支持
常见修复策略
- 修复 Android 13+ 媒体权限
- Android 14 处理部分照片授权
- 不再依赖 MediaStore DATA 字段
- 使用 content uri 替代 file path
- 查询 Cursor 后及时 close
- 图片保存改用 MediaStore
- 删除媒体时使用系统授权流程
- 图片解码增加采样
- RecyclerView 中取消旧请求
- 修复 EXIF 方向处理
- 增加空列表、权限拒绝、加载失败 UI
- 大图预览使用压缩或分块加载
- 异步加载避免阻塞主线程
代码审查重点
修复 gallery bug 时,必须检查:
- 是否兼容 Android 10+
- 是否兼容 Android 13+
- 是否兼容 Android 14 部分照片权限
- 是否处理权限拒绝
- 是否处理 URI 失效
- 是否关闭 Cursor / InputStream / OutputStream
- 是否避免主线程解码
- 是否避免 OOM
- 是否处理 RecyclerView 错图
- 是否处理图片方向
- 是否处理保存失败
- 是否处理删除授权
- 是否处理空数据状态
验证清单
至少验证:
- 首次安装进入相册
- 权限拒绝
- 权限允许
- Android 13 图片权限
- Android 13 视频权限
- Android 14 部分照片授权
- 图片列表加载
- 视频列表加载
- 混合媒体列表
- 大图预览
- HEIC 图片
- GIF 图片
- 图片方向
- 拍照后刷新相册
- 保存图片
- 删除图片
- 存储空间不足
- 快速滑动列表
- 前后台切换
- 横竖屏切换
输出 Bugfix 结论模板
Root Cause:
- 问题原因:
Fix:
- 修改内容:
Risk:
- 影响范围:
Verification:
- 已验证场景:
- 未覆盖场景:
Logs:
- 关键日志:Heuristics
- For "empty gallery" on Android 13+, always check the per-media-type granular permissions (
READ_MEDIA_IMAGES/READ_MEDIA_VIDEO) before blaming MediaStore. - For "photo not visible after save" — suspect
IS_PENDING = 1still set, or MediaStore entry created but OutputStream not flushed/closed, or the app bypassed the media scanner. - For "wrong orientation" — check EXIF read path:
ExifInterfaceworks on InputStream/Uri path but some libraries silently drop EXIF when compressing/resizing. - For "blurry / wrong image in RecyclerView" — classic missing
.into()after cancel, or Glide/Coil request not cancelled inonViewRecycled. - For "image load fails on Android 13+ but works on older" — suspect
READ_EXTERNAL_STORAGEstill used instead ofREAD_MEDIA_IMAGES, orcontent://Uri permissions not persisted across process restart. - For "image visible in file manager but not in query results" — check MediaScanner has run, and that projection/sortOrder/selection aren't excluding it by MIME type or
IS_PENDINGflag. - For "OOM in gallery list" — always suspect: no sampling on decode, loading full-res for a thumbnail slot, no Paging, no per-item Glide/Coil override to view size.
- For "Android 14 partial selection shows nothing" — the app did not handle
READ_MEDIA_VISUAL_USER_SELECTEDand didn't check Uri availability per item.
Anti-Patterns
- Catching
SecurityExceptionaround a storage call without first checking permission state — hides the real bug. - Using
MediaStore.Images.ImageColumns.DATA(absolute path) on Android 10+ — the column is deprecated/unreliable under Scoped Storage. - Opening
FileInputStream(uri.path)on acontent://Uri — must usecontentResolver.openInputStream(uri). - Loading full-resolution bitmaps into RecyclerView thumbnails instead of sampling to view dimensions.
- Holding Bitmap references in static/cache without bounds or eviction.
- Calling
ContentResolver.deletethen refreshing the list without handlingRecoverableSecurityExceptionon Android 10+ /MediaStore.createDeleteRequeston Android 11+. - Setting
IS_PENDING = 1on MediaStore insert and forgetting to flip it to0after write — leaves entries invisible to other apps/scanners. - Depending on
requestLegacyExternalStorageas a fix rather than a migration window on Android 10. - Logging user photo
Uris verbatim — leaks private picture paths; redact toid+ MIME type. - Treating gallery issues as "UI-only" and skipping permission + MediaStore + storage-layer checks.
CodeGraph Integration
CodeGraph helps ground the gallery diagnosis in the actual codebase structure. Run it before proposing a fix.
When to run CodeGraph:
- After the triage narrows the symptom to a specific gallery sub-area (list load / save / delete / orientation / OOM / picker)
- When tracing the data flow from MediaStore query → URI handling → image loading → UI binding
- When the fix may change how multiple screens consume a shared image repository
How:
codegraph explore "<GalleryRepository or MediaStore query entry>"
codegraph explore "<image-loading call site, e.g., Glide entry>"
codegraph explore "<save / delete code path>"What to look for from CodeGraph results (gallery-focused):
- Callers of the MediaStore query method — do all callers pass the same projection/selection, or are some paths using deprecated columns or wrong MIME filters?
- Uri consumer chain — who receives the Uri from the query result, and does anyone convert it back to a file path (a Scoped Storage anti-pattern)?
- Image-loading entry points — which
Glide/Coil/Picassocall sites load the suspect Uri, and which lifecycle scopes them (Fragment vs Application)? - Write/save call sites — which callers set
IS_PENDING, open the OutputStream, and flipIS_PENDINGback to0. A missing flip is a common root cause. - Delete paths — which callers use
ContentResolver.deletevsMediaStore.createDeleteRequest, and whetherRecoverableSecurityExceptionis handled. - RecyclerView adapter binding — the on-bind handler that calls into the image loader; paired with
onViewRecycledfor cancellation. - Downstream impact — screens that share the same repository; a fix that changes Uri access semantics may break the preview or share flow.
Scope note: CodeGraph does not reliably index XML layouts, res/values, AndroidManifest.xml, or Gradle build scripts. For gallery issues rooted in manifest permissions, requestLegacyExternalStorage, or network_security_config, supplement with rg/find. Also scan settings.gradle for multi-module dependencies on the image loading library.
Fallback: If codegraph explore returns no meaningful results, proceed without it — CodeGraph is an enhancement, not a blocker. Fall back to rg/grep for MediaStore / image-loader / save-path call-site search.
Guardrails
- Run the full triage classification before jumping to a code fix — gallery bugs are disproportionately driven by Android version + permission state.
- Always check Android 10 / 13 / 14 permission differences for media access before editing code.
- Always verify the fix with real devices on at least two API levels (e.g., API 29 and API 34+) — gallery behavior is highly version-sensitive.
- Always confirm the MediaStore query projection is version-appropriate —
DATAis deprecated under Scoped Storage. - Always confirm
IS_PENDINGtoggling on MediaStore insert/update is correct. - Always close
Cursor,InputStream,OutputStream— resource leaks show up as OOM / stale scans hours later. - Always use
contentResolver.openInputStream(uri)oncontent://Uris, neverFileInputStream(uri.path). - Never log photo Uris or absolute paths verbatim — redact to
id+ MIME type to avoid PII leakage. - Do not mask storage errors with a broad try/catch — find and fix the missing permission / missing scan / deprecated column first.
- If
codegraph explorereturns no meaningful results, proceed without it — CodeGraph is an enhancement, not a blocker. Fall back torg/grep.




