1. Android 多媒体文件获取工具类为什么值得用 AI 辅助重写
MediaStoreUtil这类工具类,几乎是每个做相册、播放器、文件管理、OCR 扫描的 Android 项目都会写一遍的东西。它的核心逻辑并不复杂:通过ContentResolver查询MediaStore暴露的音频、图片、视频、缩略图数据表,把Cursor里的字段读出来,封装成List<String>、List<File>或者自定义的VideoInfo。但真正落到工程里,问题往往不在“能不能查出来”,而在“查得对不对、稳不稳、适配不适配新系统”。
我见过太多项目里的多媒体工具类长这样:Cursor用完不关、getColumnIndex拿到 -1 还硬读、DATA字段在 Android 10 之后分区存储下直接失效、缩略图查询里把Video.Thumbnails.DATA写成了Media.DATA导致永远取不到路径。这些坑单靠人肉 review 很费时间,而 AI 编程助手恰好擅长做这种“模式识别 + 批量改写”的活。
这篇要解决的问题很具体:你在本地 Android 项目里写多媒体文件获取工具类时,怎么把 AI 助手接进来,让它帮你补全查询逻辑、修Cursor泄漏、适配分区存储,并且用一套统一的 Key 和 API 通道把配置固定下来,不用每个工具各配一份。适合已经能跑 Android 工程、但对 AI 工具接入配置还不太熟的开发者。下面会给出可直接复制的settings.json和config.toml骨架,以及验证接入是否成功的操作步骤。
2. 用 TaoToken 统一 Key 接入 AI 助手的前置准备
在动手改MediaStoreUtil之前,先把 AI 通道打通。这里的思路是:不把 Key 散落在各个 IDE 插件、CLI 工具、脚本里,而是统一走一个 API 入口,这样换工具、加工具都不用重新申请和分发凭证。
TaoToken 在这里扮演的就是这个统一入口的角色。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api (这个地址不加 UTM 参数,配置里直接写它)。你需要先拿到一个 API Key,入口在控制台的 API Keys 页面:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。
拿到 Key 之后,先明确一件事:AI 助手接入分两类场景。一类是“问答/验证模型通不通”,用模型对话页面最快:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。另一类是“长期在 IDE 或 CLI 里写代码”,比如让 AI 持续帮你重构MediaStoreUtil,这种更适合用 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,配置字段有疑问时对照它。
注意:API Key 属于凭证,不要写进提交到 Git 的
settings.json里。下面配置骨架里用占位符表示,实际使用时通过环境变量或本地未跟踪文件注入。
3. 可复制的 settings.json 与 config.toml 配置骨架
不同 AI 编程工具读的配置文件不一样。VS Code 系插件通常读settings.json,一些 CLI 工具和 Agent 读config.toml。下面两份骨架你可以直接抄,改掉占位符即可。
3.1 settings.json 配置骨架
这份配置把 API 基址、Key 的读取方式、默认模型都固定下来。注意baseUrl写https://taotoken.net/api,不要带任何查询参数。
{ "aiAssistant.provider": "openai-compatible", "aiAssistant.baseUrl": "https://taotoken.net/api", "aiAssistant.apiKey": "${env:TAOTOKEN_API_KEY}", "aiAssistant.defaultModel": "claude-sonnet-4-20250514", "aiAssistant.timeoutMs": 60000, "aiAssistant.maxTokens": 8192, "aiAssistant.context.includeWorkspace": true, "aiAssistant.context.excludeGlobs": [ "**/build/**", "**/.gradle/**", "**/*.apk", "**/*.so" ] }几个字段值得说明。apiKey用${env:TAOTOKEN_API_KEY}而不是明文,是为了让 Key 留在系统环境变量里,配置文件可以安全提交。excludeGlobs把build和.gradle排除掉,否则 AI 助手在索引 Android 工程时会被编译产物拖慢,甚至把混淆后的类名当成源码读进去。defaultModel按你实际可用的模型名填,接入文档里有当前支持的模型列表。
3.2 config.toml 配置骨架
CLI 类工具和部分 Agent 用 TOML。结构上大同小异,但字段名要按工具要求来。
[provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" default_model = "claude-sonnet-4-20250514" [request] timeout_seconds = 60 max_tokens = 8192 temperature = 0.2 [workspace] root = "." include = ["app/src/main/java/**/*.java", "app/src/main/java/**/*.kt"] exclude = ["**/build/**", "**/.gradle/**", "**/test/**"] [android] language_level = "java17" media_store_target = "android14"temperature设 0.2 是因为改工具类这种任务要的是稳定输出,不是发散创意。include只圈住源码目录,让 AI 的上下文聚焦在MediaStoreUtil所在的包路径上。media_store_target是我自己加的一个约定字段,用来提醒 AI 当前适配到哪个 Android 版本,避免它给出已经被废弃的DATA字段方案。
4. 让 AI 辅助重写 MediaStoreUtil 的实操步骤
配置接好之后,进入正题:用 AI 帮你把那个老工具类改对。下面按步骤来,每步都有可复制的提示词和预期产出。
4.1 第一步:把现有工具类喂给 AI 并说明问题
不要一上来就让 AI “重写这个类”,那样它容易自由发挥。先给它现状和约束。提示词可以这样写:
下面是一个 Android MediaStoreUtil 工具类,存在以下问题: 1. 所有 Cursor 查询后没有 close(),存在泄漏; 2. getColumnIndex 可能返回 -1,没有做防御; 3. 使用了 MediaStore.Images.Media.DATA 字段,在 Android 10+ 分区存储下不可靠; 4. getThumbNames 里查询的是 Thumbnails 表,却用 Media.DISPLAY_NAME 取列,列名不匹配; 5. getVideoInfo 里缩略图路径取的是 cursor 而不是 thumbCursor。 请先不要改代码,逐条确认这些问题是否成立,并指出还有哪些我没发现的问题。这一步的目的是让 AI 先做诊断,而不是直接产出代码。实测下来,它通常能补出你没注意的点,比如getBitmaps里Thumbnails.getThumbnail返回 null 没处理、VideoInfo内部类字段没有 getter 导致外部无法访问等。
4.2 第二步:让 AI 产出适配分区存储的查询方案
确认问题后,再让它改。关键约束是:Android 10 及以上用MediaStore的RELATIVE_PATH和DISPLAY_NAME组合,不再依赖DATA;Android 9 及以下保留DATA作为回退。提示词:
基于上面的诊断,重写这个工具类。要求: - 所有 Cursor 用 try-with-resources 或 finally 关闭; - 用 getColumnIndexOrThrow 替代 getColumnIndex,并在列不存在时给出明确日志; - 图片和视频路径改为:Android 10+ 用 RELATIVE_PATH + DISPLAY_NAME 拼接,Android 9- 用 DATA; - 保留原有方法签名,方便调用方不改代码; - 缩略图查询修正列名,并处理 getThumbnail 返回 null 的情况。产出的代码里,路径拼接部分大概会是这样:
private static String resolvePath(Context context, Cursor cursor, String dataColumn, String relativeColumn, String nameColumn) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { String relative = cursor.getString(cursor.getColumnIndexOrThrow(relativeColumn)); String name = cursor.getString(cursor.getColumnIndexOrThrow(nameColumn)); return (relative == null ? "" : relative) + name; } else { return cursor.getString(cursor.getColumnIndexOrThrow(dataColumn)); } }这段逻辑你让 AI 生成后,自己再对着MediaStore.MediaColumns的文档核一遍列名,因为不同 Android 版本列名有细微差异。
4.3 第三步:补上权限与线程约束
MediaStore查询在 Android 13+ 需要READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO,而不是老的READ_EXTERNAL_STORAGE。让 AI 帮你把权限判断也加进工具类:
给工具类加一个 checkMediaPermission(Context, MediaType) 方法, 根据 Build.VERSION.SDK_INT 返回需要申请的权限数组: - Android 13+:按类型返回 READ_MEDIA_IMAGES / READ_MEDIA_VIDEO / READ_MEDIA_AUDIO; - Android 12-:返回 READ_EXTERNAL_STORAGE。 同时给所有查询方法加一个 @WorkerThread 注解提示,避免在主线程调用。@WorkerThread这个注解很实用,配合 Android Studio 的 lint 能在编译期提示调用方别在主线程查大表。
5. 验证 AI 工具是否成功接入的操作步骤
配置写完、代码改完,怎么确认 AI 通道真的通了?按下面三步验证。
5.1 用模型对话页面做最小连通性测试
先不碰 IDE,直接打开模型对话页面:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。在输入框里发一句和 Android 相关的问题,比如“MediaStore.Images.Media.DATA 在 Android 10 之后为什么不可靠”。如果能在几秒内返回一段有内容的回答,说明 Key 和 API 通道是通的。这一步排除的是凭证和网络层问题。
5.2 在 IDE 里触发一次真实补全
回到 Android Studio 或你用的编辑器,打开MediaStoreUtil.java,把光标放在某个方法里,触发 AI 补全。如果配置里的baseUrl和apiKey正确,补全请求会走https://taotoken.net/api返回结果。如果补全没反应,先看 IDE 的输出面板里有没有 401 或 404,401 通常是 Key 没读到环境变量,404 通常是baseUrl多写了路径。
5.3 用一次完整重构验证上下文能力
最后做一次端到端验证:让 AI 基于当前打开的MediaStoreUtil.java和VideoInfo内部类,生成一个调用示例。如果它能正确引用你工程里的类名和方法签名,说明include和exclude配置生效,上下文索引正常。这一步过了,才算真正接入完成。
6. 本篇常见错排查
接入和改写过程中,下面几个错我踩过,列出来帮你省时间。
报错一:getColumnIndexOrThrow抛 IllegalArgumentException。原因通常是查询投影里没包含你要取的列。比如你查MediaStore.Images.Media.EXTERNAL_CONTENT_URI,投影里只写了_ID和DISPLAY_NAME,却去取RELATIVE_PATH,就会抛。解决方法是把投影数组和取值列一一对齐,或者改用getColumnIndex并判 -1。
报错二:Android 13 上查询返回空 Cursor。不是代码问题,是权限没申请。Android 13 把媒体权限拆成了三个,你只申请了READ_MEDIA_IMAGES,查视频自然是空的。对照第 4.3 步的权限方法检查。
报错三:AI 补全一直转圈然后超时。先确认timeoutMs是不是设得太短,Android 工程上下文大,首次索引慢。其次检查excludeGlobs有没有把build排掉,没排的话 AI 可能在读几万个编译产物。如果还不行,去接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 核对字段名。
报错四:config.toml里api_key_env读不到值。环境变量要在启动 IDE 或 CLI 的同一个 shell 里 export,GUI 启动的 IDE 可能读不到你终端里设的变量。可以在 IDE 的终端里echo $TAOTOKEN_API_KEY确认。
报错五:缩略图路径取出来是 null。回到第 4.1 步诊断里提到的,getVideoInfo里缩略图路径要从thumbCursor取,不是从主cursor取。这个错很隐蔽,因为不报异常,只是值为 null。
如果你是要长期在 IDE 里做这类重构,建议走 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,比每次单独配 Key 省事。Key 的管理统一在控制台:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。Claude Code 这类 CLI 工具的接入配置可以参考:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite 。
最后留一个我自己的习惯:每次让 AI 改完MediaStoreUtil,我都会在真机上跑一遍getImages和getVideoInfo,用Log.d打出前三条路径,确认拼接结果符合预期再提交。AI 给的代码逻辑通常对,但列名和版本分支这种细节,真机跑一次比看十遍代码都管用。