ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Android 获取手机本地所有 PDF 文件:TaoToken 统一 Key 接入与配置骨架

2026/9/27 15:10:51 拓冰建站 浏览量
Android 获取手机本地所有 PDF 文件:TaoToken 统一 Key 接入与配置骨架 1. Android 本地 PDF 扫描到底难在哪Android 获取手机本地所有 PDF 文件本质上是两件事一是用 MediaStore 把散落在各目录的 PDF 索引出来二是把这份文件清单交给 AI 工具做后续处理摘要、分类、问答。第一件事网上教程很多第二件事才是真正卡人的地方——你本地跑通了扫描结果要接大模型时发现每个工具都要单独填 Key、单独配 Base URLCline 一套、Claude Code 一套、脚本里又一套改一次配置要翻五个文件。这篇就按「扫描 PDF → 拿到路径列表 → 用 TaoToken 统一 Key 接入 AI 工具」这条链路走一遍。适合两类人正在做 Android 文件管理类 App、需要把本地文档喂给模型的开发者以及想用 Cline、Claude Code 这类编码工具批量处理本地 PDF 资料的人。核心检索词就三个Android 本地 PDF 扫描、MediaStore 查询、TaoToken 统一 Key 接入。先说结论扫描部分用MediaStore.Files配合 MIME 过滤是最稳的路径Android 10 之后分区存储Scoped Storage让直接读绝对路径变得不可靠所以我会同时给「拿 Uri」和「拿可读路径」两种写法。AI 接入部分TaoToken 提供的是 OpenAI 兼容的统一通道一个 Key 可以给多个工具复用省掉到处填配置的麻烦。下面从权限开始一步步给可复制的代码和配置。2. TaoToken 前置统一 Key 与通道准备在写扫描代码之前先把 AI 侧的通道准备好这样扫描出结果后能立刻验证「文件清单 → 模型调用」这条链路是通的。TaoToken 的定位是一个统一 Key/API 通道官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 这个地址不加 UTM 参数配置里直接填它。你需要做的准备动作只有三步。第一步进控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建后在密钥管理页复制出来形如sk-开头的一串。第二步确认你要用的模型名模型对话页 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 里能看到当前可用的模型列表记下你打算调用的那个。第三步如果你打算长期用编码类工具Cline、Claude Code 等批量处理 PDF建议直接看 Coding Plan地址 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它比按量计费更适合高频调用场景。注意API Key 只存在本地配置文件或环境变量里不要硬编码进 Android 工程提交到 Git。下面所有配置示例里的sk-xxxx都请替换成你自己的 Key。这里有个容易混淆的点TaoToken 的 API 地址是https://taotoken.net/api在 OpenAI 兼容的客户端里Base URL 通常填这个然后客户端会自动拼/v1/chat/completions。如果你用的工具要求填完整 endpoint那就填https://taotoken.net/api/v1/chat/completions。两种写法取决于工具下面配置骨架里我会标注清楚。3. 可复制配置扫描代码 工具接入骨架3.1 Android 侧MediaStore 扫描 PDF 的完整实现先在AndroidManifest.xml里声明权限。Android 13API 33之后读媒体文件要用细分权限所以新旧版本都要覆盖uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE android:maxSdkVersion32 / uses-permission android:nameandroid.permission.READ_MEDIA_IMAGES / uses-permission android:nameandroid.permission.READ_MEDIA_VIDEO / uses-permission android:nameandroid.permission.READ_MEDIA_AUDIO /PDF 不属于图片/视频/音频这三类细分媒体在 Android 13 上通过MediaStore.Files查询application/pdf仍然可行但部分机型需要走MANAGE_EXTERNAL_STORAGE或 SAF存储访问框架才能拿到全部文件。实测下来用MediaStore.Files查 MIME 是最省事的路径先跑通它遇到权限不足再降级到 SAF。下面是 Kotlin 扫描函数返回文件名和内容 Uri 的配对列表data class PdfItem(val name: String, val uri: Uri, val size: Long) fun scanLocalPdfs(context: Context): ListPdfItem { val result mutableListOfPdfItem() val collection MediaStore.Files.getContentUri(external) val projection arrayOf( MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.SIZE, MediaStore.Files.FileColumns._ID ) val selection ${MediaStore.Files.FileColumns.MIME_TYPE} ? val selectionArgs arrayOf(application/pdf) val sortOrder ${MediaStore.Files.FileColumns.DATE_ADDED} DESC context.contentResolver.query( collection, projection, selection, selectionArgs, sortOrder )?.use { cursor - val nameIdx cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME) val sizeIdx cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE) val idIdx cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID) while (cursor.moveToNext()) { val id cursor.getLong(idIdx) val uri ContentUris.withAppendedId(collection, id) result.add( PdfItem( name cursor.getString(nameIdx), uri uri, size cursor.getLong(sizeIdx) ) ) } } return result }关键点说明MediaStore.Files.getContentUri(external)拿到外部存储的文件集合selection用 MIME 类型过滤比按扩展名.pdf匹配更准因为有些文件扩展名和实际类型不一致ContentUris.withAppendedId把_ID拼成可直接读取的content://UriAndroid 10 用这个 Uri 打开文件流最稳不要再去拼/storage/emulated/0/...这种绝对路径。如果你确实需要绝对路径比如传给某些只认路径的库可以额外查MediaStore.Files.FileColumns.DATA列但要清楚这个列在 Android 10 上可能返回空或不可读属于兼容性写法不推荐作为主路径。3.2 Cline 接入配置骨架Cline 是 VS Code 里的编码 Agent配置走 OpenAI Compatible 模式。在设置里选 Provider 为「OpenAI Compatible」然后填{ provider: openai-compatible, baseUrl: https://taotoken.net/api, apiKey: sk-xxxx, model: 你的模型名, temperature: 0.2 }baseUrl填https://taotoken.net/apiCline 会自动补/v1/chat/completions。model填你在模型列表里看到的名称。温度调低一点0.2 左右是因为处理文件清单这类任务需要稳定输出不要让它自由发挥。3.3 Claude Code 接入配置骨架Claude Code 通过环境变量读取通道配置在 shell 的~/.zshrc或~/.bashrc里加export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-xxxx改完执行source ~/.zshrc生效。Claude Code 的详细接入说明在文档页 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有不同操作系统的完整步骤。如果你用的是 Claude Code 的 Anthropic 原生通道参考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 这个页面它专门讲 Anthropic 协议下的配置差异。3.4 config.toml 通用骨架有些工具比如某些 CLI Agent用 TOML 配置骨架如下[provider] name taotoken base_url https://taotoken.net/api api_key sk-xxxx model 你的模型名 [request] timeout 60 max_retries 3timeout给 60 秒因为 PDF 内容如果较长模型响应会慢一些max_retries给 3 次网络抖动时自动重试。4. 验证请求从扫描结果到模型调用扫描代码写完后先验证文件清单是否正确。在 Activity 里调用并打印val pdfs scanLocalPdfs(this) Log.d(PDFScan, 共找到 ${pdfs.size} 个 PDF) pdfs.take(5).forEach { Log.d(PDFScan, ${it.name} | ${it.size} bytes | ${it.uri}) }跑起来看 Logcat如果数量为 0先检查权限是否授予、模拟器里是否真的存了 PDF 文件。确认清单没问题后验证 AI 通道。用 curl 发一个最小请求curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-xxxx \ -d { model: 你的模型名, messages: [ {role: user, content: 回复 OK 两个字母即可} ] }返回里能看到choices[0].message.content是OK说明通道通了。这一步很关键很多人配置完直接上复杂任务报错了分不清是通道问题还是业务问题先用最小请求把通道验证掉。通道通了之后把扫描结果和模型调用串起来。思路是把 PDF 文件名列表拼成一段文本发给模型做分类或摘要。比如val names pdfs.joinToString(\n) { it.name } val prompt 以下是本地 PDF 文件清单请按主题分类\n$names // 把 prompt 通过你的 HTTP 客户端发到 https://taotoken.net/api/v1/chat/completions如果你要读 PDF 正文内容需要先把content://Uri 转成字节流再用 PDF 解析库如 PdfBox-Android提取文本然后把文本作为 message 内容发出去。注意单次请求的 token 上限长 PDF 要分段。5. 本篇常见错排查扫描结果为 0最常见原因是权限没授予。Android 13 上READ_EXTERNAL_STORAGE已失效要动态申请READ_MEDIA_*系列或者引导用户去系统设置里开「所有文件访问权限」。另一个原因是模拟器里根本没存 PDF用adb push推一个进去再测。Uri 能拿到但打不开Android 10 分区存储下直接拼绝对路径会失败。用ContentUris.withAppendedId生成的content://Uri 配合contentResolver.openInputStream(uri)读取不要用File(path)。Cline 报 401Key 填错或没带Bearer前缀。检查apiKey字段是不是完整的sk-开头字符串有没有多余空格。Claude Code 报连接失败环境变量没生效。确认ANTHROPIC_BASE_URL拼写正确改完配置文件后重新开一个终端窗口或者source一下。如果还是不行去文档页对照你的系统版本检查。请求超时PDF 内容太长导致模型处理慢。把timeout调到 120 秒或者把 PDF 分段发送。另外确认baseUrl没有多写或少写/v1不同工具对路径的处理不一样。模型名报错填的模型名不在可用列表里。去模型对话页确认当前可用的模型名称复制粘贴不要手打。6. 后续怎么用把链路固定下来扫描 接入跑通后建议把配置固化成模板。Android 侧把scanLocalPdfs封装成工具类AI 侧把 Key 和 Base URL 放到local.properties或环境变量里不要散落在代码各处。长期做编码类任务的话Coding Plan 比按量计费更划算地址在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。需要新建或轮换 Key 时去 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入细节查文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。想先试试模型效果直接去模型对话页 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 发一条消息就能验证。最后留一个我踩过的坑MediaStore 的查询结果有缓存新推入的 PDF 文件可能不会立刻出现在列表里。测试时如果发现文件「消失」了用contentResolver.notifyChange触发一次刷新或者重启 App 再查。这个细节教程里很少提但调试时能省不少时间。