news 2026/9/26 9:30:04

从 SIM 卡读取联系人:TaoToken 统一 Key 通道下的配置与验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从 SIM 卡读取联系人:TaoToken 统一 Key 通道下的配置与验证

1. 从 SIM 卡读取联系人到底难在哪

Android 里读取 SIM 卡联系人,说穿了就是通过ContentResolver去查一个特定的 URI,把游标里的tag(姓名)和number(号码)取出来。听起来三行代码就能搞定,但真正在工程里落地时,坑往往不在查询本身,而在“工具链怎么配、权限怎么给、不同 ROM 的 URI 差异怎么兜底、以及怎么把这类系统级能力接到一个统一的调用通道里”。

这篇聚焦的场景很具体:Android / 嵌入式设备上读取 SIM 卡联系人,同时用 TaoToken 的统一 Key 通道把工具侧(CC Switch、Cline 这类编码助手)配置好,让整个开发过程可复现、可验证。适合两类人:一是做 Android 系统集成、需要操作 SIM 通讯录的工程师;二是用 AI 编码工具辅助写这类系统代码、但被 Key 管理和工具配置卡住的开发者。

核心检索词先摆出来:SIM 卡联系人读取靠的是content://icc/adn这个 URI(注意,很多老资料写的是content://icc/and,这是历史拼写,实际以adn为准,下面会讲怎么兼容)。能做什么?查询、插入、更新、删除 SIM 卡上的联系人条目。适合谁?需要直接读写 SIM 通讯录、又不想依赖第三方 App 的开发者。

我试过在真机上直接跑查询,结果第一次就返回空游标——不是代码错,是权限和 URI 都没对上。所以这篇不会只给你一段查询代码,而是把“配置 → 请求 → 验证 → 排错”整条链路走一遍。

2. TaoToken 统一 Key 通道的前置准备

在写 SIM 卡联系人代码之前,先把工具侧的调用通道理顺。TaoToken 在这里扮演的角色是:给 CC Switch、Cline 这类编码工具提供一个统一的 Key 和 API 入口,你不用在每个工具里分别填不同的密钥,改一处即可。

先拿到 Key。打开控制台创建 API Key:

https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=console

创建完 Key 后,接入文档在这里,配置格式、字段含义都以它为准:

https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc

API 的基础地址是https://taotoken.net/api(这个不加 UTM,直接用于代码里的 base_url)。如果你只是想在写代码时让模型帮你补全 SIM 联系人相关的逻辑,用模型对话入口验证一下模型是否正常响应就够了:

https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=models

如果你是要长期用编码助手(比如让 Cline 持续帮你写 Android 系统代码),那更适合走 Coding Plan,额度和管理都更省心:

https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding-plan

Key 的管理页面统一在 API Keys:

https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api-keys

注意:Key 只创建一次就够,多个工具共用同一个 Key,改 Key 时只改一处,这是统一通道最大的好处。别把 Key 硬编码进提交到仓库的代码里。

3. 可复制的工具配置骨架

这一节给两份可直接抄的配置:一份是 CC Switch 的settings.json骨架,一份是 Cline 的config.toml片段。字段名以你本地工具版本为准,但结构可以直接用。

3.1 CC Switch 的 settings.json

{ "provider": "taotoken", "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key填这里", "model": "claude-sonnet", "timeout": 60, "retry": { "max_attempts": 3, "backoff_ms": 800 }, "tools": { "cline": { "enabled": true, "auto_apply": false } } }

几个字段说明:base_url固定用https://taotoken.net/api,不要带路径后缀;timeout给 60 秒,系统级代码补全有时响应偏慢;retry建议保留,网络抖动时自动重试比手动重跑省事。auto_apply设成false,让模型给建议、你来确认,避免它直接改你的 SIM 联系人代码。

3.2 Cline 的 config.toml 片段

[provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key = "sk-你的Key填这里" [model] default = "claude-sonnet" max_tokens = 8192 [workspace] root = "./android-sim-demo" context_files = ["app/src/main/java/**/*.java", "app/src/main/AndroidManifest.xml"]

context_files这一项很关键:把 AndroidManifest 和 Java 源码目录纳入上下文,模型在补全 SIM 联系人逻辑时才能看到你的权限声明和包结构,不然它给的代码可能缺READ_CONTACTS声明。

3.3 Android 侧的权限声明

工具配好了,代码侧别漏权限。在AndroidManifest.xml里加:

<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />

运行时还要动态申请,Android 6.0 以上不动态申请会直接抛 SecurityException。查询 SIM 联系人属于读通讯录,READ_CONTACTS是必须的。

4. 读取 SIM 联系人的完整实现与验证

现在进入正题。SIM 卡联系人的 URI,标准写法是content://icc/adn。老代码里常见的content://icc/and是拼写遗留,部分 ROM 仍兼容,但新设备建议以adn为主、and作兜底。

4.1 查询联系人

public List<SimContact> querySimContacts(Activity activity) { List<SimContact> result = new ArrayList<>(); Uri uri = Uri.parse("content://icc/adn"); Cursor cursor = null; try { cursor = activity.getContentResolver().query(uri, null, null, null, null); if (cursor == null) { return result; } int nameIdx = cursor.getColumnIndex("tag"); int numberIdx = cursor.getColumnIndex("number"); while (cursor.moveToNext()) { String name = nameIdx >= 0 ? cursor.getString(nameIdx) : ""; String number = numberIdx >= 0 ? cursor.getString(numberIdx) : ""; result.add(new SimContact(name, number)); } } catch (Exception e) { Log.e("SimContact", "query failed: " + e.getMessage()); } finally { if (cursor != null) { cursor.close(); } } return result; }

注意列名用的是tag和number,不是People.NAME。老资料里用People.NAME是因为早期 API 的常量映射,现在直接取字符串列名更稳。getColumnIndex返回 -1 时要兜底,否则getString(-1)会崩。

4.2 插入联系人

public Uri insertSimContact(Activity activity, String name, String number) { Uri uri = Uri.parse("content://icc/adn"); ContentValues values = new ContentValues(); values.put("tag", name); values.put("number", number); return activity.getContentResolver().insert(uri, values); }

插入返回的 Uri 就是新条目的地址,可以拿它做后续更新或删除。

4.3 更新联系人

public int updateSimContact(Activity activity, String oldName, String oldNumber, String newName, String newNumber) { Uri uri = Uri.parse("content://icc/adn"); ContentValues values = new ContentValues(); values.put("tag", oldName); values.put("number", oldNumber); values.put("newTag", newName); values.put("newNumber", newNumber); return activity.getContentResolver().update(uri, values, null, null); }

更新时旧值和新值都要给:tag/number定位旧条目,newTag/newNumber是目标值。返回受影响行数,为 0 说明没匹配到。

4.4 删除联系人

public int deleteSimContact(Activity activity, String name, String number) { Uri uri = Uri.parse("content://icc/adn"); String where = "tag=? AND number=?"; String[] args = new String[]{name, number}; return activity.getContentResolver().delete(uri, where, args); }

用参数化where而不是字符串拼接,避免姓名里带单引号导致语句出错。

4.5 验证动作与预期结果

写完别急着跑,按这个顺序验证:

第一步,确认权限已授予。在onCreate里检查checkSelfPermission(READ_CONTACTS),没授予就requestPermissions。

第二步,插一张有联系人的 SIM 卡(或先用插入接口写两条测试数据),调用querySimContacts,预期返回的 List 里能看到你写入的姓名和号码。

第三步,调用insertSimContact("TestA", "10086"),预期返回非空 Uri;再查询,预期多出一条TestA / 10086。

第四步,调用updateSimContact("TestA", "10086", "TestB", "10010"),预期返回 1;再查询,预期TestA变成TestB、号码变成10010。

第五步,调用deleteSimContact("TestB", "10010"),预期返回 1;再查询,预期该条消失。

如果查询一直返回空,先看权限,再看 URI 是不是该用and兜底,最后确认 SIM 卡本身是否真的存了联系人(有些卡是空的)。

5. 本篇常见错误排查

错误一:查询返回 null 游标。多半是权限没给,或者 URI 写错。先确认READ_CONTACTS已动态授予,再把content://icc/adn换成content://icc/and试一次。

错误二:getColumnIndex返回 -1 导致崩溃。不同 ROM 的列名可能不完全一致,务必对 -1 做兜底,取不到就跳过该行,别直接getString。

错误三:插入成功但查询看不到。有些设备 SIM 联系人写入后需要重新插拔或重启才刷新,先确认写入返回的 Uri 非空,再重启设备复测。

错误四:工具侧请求报 401。这是 Key 问题,不是代码问题。去 API Keys 页面确认 Key 有效、没被删,再检查settings.json里api_key有没有多余空格。接入细节对照接入文档。

错误五:Cline 补全的代码缺权限声明。说明context_files没把 AndroidManifest 纳入上下文,补上后重新让模型生成。

错误六:更新返回 0。旧条目的tag/number和实际存储值不完全一致(比如号码带了空格或国家码),先查询打印出真实值,再用真实值去更新。

6. 把通道和代码一起收尾

SIM 卡联系人这套读写逻辑本身不复杂,真正花时间的是权限、URI 兼容和工具链配置。把 TaoToken 的统一 Key 通道配好之后,CC Switch 和 Cline 共用一份 Key,改配置只改一处,写这类系统代码时模型能直接看到你的 Manifest 和源码结构,补全质量会明显好一截。

如果你还在调工具接入,先去 API Keys 页面把 Key 管好,再对着接入文档核对字段;如果只是想验证模型能不能帮你写 SIM 联系人逻辑,用模型对话入口试一轮最快;要是打算长期用编码助手持续写 Android 系统代码,Coding Plan 更合适。代码跑通之后,记得把测试用的 SIM 联系人清掉,别把测试数据留在卡里。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 9:29:45

jd-gui全攻略:Java反编译工具下载、乱码解决与命令行批量反编译

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 9:28:08

从本地编译到官方索引:ROS2包发布全流程与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 9:26:42

Excel中用SUM函数做分数段统计的实战方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 9:26:39

群联PS2251-19主控U盘量产修复实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 9:26:36

EPLAN部件库建立与更改全攻略:从入门到高效管理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华