ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

ContentProvider 自动补全联系人,让 Codex 走 TaoToken 对照 READ_CONTACTS 权限链路

ContentProvider 自动补全联系人,让 Codex 走 TaoToken 对照 READ_CONTACTS 权限链路 如果你正在排查 AutoCompleteTextView 输入“张”却没有任何下拉联系人补全先别急着把锅甩给 ContentProvider。这类问题在 Android 里通常卡在三条链路AndroidManifest.xml 是否声明 READ_CONTACTS、Android 6.0 动态权限回调有没有真正进入 initAutoComplete、以及 query 返回的 Cursor 是否包含 _ID 并被 SimpleCursorAdapter 正确绑定。本文用 TaoToken 配合 Codex 做逐段代码对照TaoToken 只作为 Codex 的 Key 来源和模型通道不替代 ContactsContract 查询也不替代 ContentProvider。先打开 TaoToken 官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 创建 Key然后把 Codex 的 Base URL 配成 https://taotoken.net/api注意不要加 /v1也不要填官网地址。接下来让 Codex 对照原文 Step 2 的 onRequestPermissionsResult、Step 3 的 projection/orderBy/query 以及 SimpleCursorAdapter 绑定逐段排查最后回到 AutoCompleteTextView 输入“张”验证下拉补全是否出现。一、原问题与场景AutoCompleteTextView 输入“张”不弹补全先查 READ_CONTACTS 权限链路这个场景本身并不复杂用户在一个输入框里刚输入“张”下方应该根据联系人数据库弹出“张三”“张伟”“张无忌”之类的候选。对应控件是 AutoCompleteTextView数据来源是联系人 ContentProvider查询 URI 是ContactsContract.CommonDataKinds.Phone.CONTENT_URI中间用 ContentResolver 执行 query再把 Cursor 交给 SimpleCursorAdapter。只要其中任何一环断了表现都非常像输入“张”没反应或者只弹一次空列表。排障视角下不要先改业务逻辑应该先确认链路顺序。AndroidManifest.xml 里需要uses-permission android:nameandroid.permission.READ_CONTACTS /但 Android 6.0 之后只有静态声明不够运行时还要申请。典型错误是权限弹窗出现了用户也点了允许但onRequestPermissionsResult里的 requestCode 对不上或者授权成功后没有继续调用initAutoComplete()导致 AutoCompleteTextView 始终没有 setAdapter。另一种情况是权限没问题但initAutoComplete()里的 projection 少了_IDSimpleCursorAdapter 无法把 Cursor 行和下拉项稳定绑定。还有一种更隐蔽Cursor 查询成功但getCount()为 0比如模拟器通讯录为空、URI 写错、查询条件过滤过狠输入“张”自然也不会弹出任何内容。所以本篇的目标不是把 ContentProvider 换掉也不是把联系人查询改成别的通道而是用 Codex 走 TaoToken 的模型通道对照权限声明、动态授权回调、projection、orderBy、query、SimpleCursorAdapter 和 AutoCompleteTextView 的 threshold把“为什么不弹补全”拆成可验证的小步骤。你需要准备的东西只有两样一个能访问联系人数据的 Android 工程以及一个 TaoToken Key。二、TaoToken 前置给 Codex 提供 Key 与模型通道不替代 ContentProvider这里必须说清楚边界TaoToken 在这个排障流程里只负责两件事给 Codex 提供可用的 Key以及提供模型调用通道。它不替代 ContactsContract不替代 ContentResolver不替代 SimpleCursorAdapter也不替代 Android 运行时权限机制。联系人数据仍然来自系统联系人 ContentProvider能否查询仍然取决于 READ_CONTACTS 是否被正确授予。先打开 TaoToken 官网创建 Keyhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content登录后进入控制台在 API Keys 页面创建或复制一个 Key。本文示例统一写成YOUR_API_KEY真正配置时替换成你自己的 Key。不要把这个 Key 提交到 Git也不要写进会公开的 Android 资源文件。Codex 的配置建议放在本机用户目录下的~/.codex/config.toml通过环境变量读取 Key。接下来配置 Codex 时Base URL 必须填写https://taotoken.net/api不要写成https://taotoken.net/api/v1也不要填 TaoToken 官网首页地址。Codex 走的是 API 通道不是网页控制台。如果你在配置时混用了官网地址和 API 地址常见表现是 404、鉴权失败或者请求路径不匹配。把 Key 和 Base URL 分开管理后续排查 Android 代码时才不会把模型通道问题和 READ_CONTACTS 权限问题搅在一起。三、可复制配置Codex config.toml 指向 https://taotoken.net/apiAndroid 侧权限与查询代码先给 Codex 一份可复制配置。新建或编辑# ~/.codex/config.toml model MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY其中MODEL_ID换成你在 TaoToken 控制台可用的模型 ID。然后在终端设置环境变量。macOS / Linuxexport TAOTOKEN_API_KEYYOUR_API_KEYWindows CMDset TAOTOKEN_API_KEYYOUR_API_KEYPowerShell$env:TAOTOKEN_API_KEYYOUR_API_KEY之后在 Android 工程根目录启动 Codex让它读取当前项目文件。Codex 会按 config.toml 里的 provider 和 base_url 走 TaoToken 通道。下面给出 Android 侧需要对照的关键代码骨架。注意变量名和原文可以不同关键是链路不能断。AndroidManifest.xmluses-permission android:nameandroid.permission.READ_CONTACTS /运行时权限与回调private static final int REQ_READ_CONTACTS 2001; private AutoCompleteTextView contactInput; private void ensureContactsPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) PackageManager.PERMISSION_GRANTED) { initAutoComplete(); } else { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.READ_CONTACTS}, REQ_READ_CONTACTS ); } } Override public void onRequestPermissionsResult(int requestCode, NonNull String[] permissions, NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode ! REQ_READ_CONTACTS) { return; } if (grantResults.length 0 grantResults[0] PackageManager.PERMISSION_GRANTED) { initAutoComplete(); } else { Toast.makeText(this, 未授予 READ_CONTACTS联系人补全不可用, Toast.LENGTH_SHORT).show(); } }查询与适配器绑定private void initAutoComplete() { contactInput findViewById(R.id.auto_complete_view); String[] projection new String[]{ ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME }; String orderBy ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME ASC; Cursor cursor getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, orderBy ); if (cursor null) { Log.w(ContactAuto, query returned null); return; } Log.d(ContactAuto, cursor count cursor.getCount()); SimpleCursorAdapter adapter new SimpleCursorAdapter( this, android.R.layout.simple_dropdown_item_1line, cursor, new String[]{ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME}, new int[]{android.R.id.text1}, SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER ); contactInput.setAdapter(adapter); contactInput.setThreshold(1); }布局里 AutoCompleteTextView 至少要设置正确的 id 和阈值AutoCompleteTextView android:idid/auto_complete_view android:layout_widthmatch_parent android:layout_heightwrap_content android:completionThreshold1 android:hint输入联系人姓名例如张 /这段配置不是让你重写业务而是给 Codex 一个明确的对照对象。Codex 的任务是检查这些关键点是否真实存在、顺序是否正确、字段是否缺失。四、验证请求与成功结果让 Codex 对照 onRequestPermissionsResult、projection、SimpleCursorAdapter配置好 TaoToken 和 Codex 后在 Android 工程根目录启动 Codex。可以用下面这段提示词让它按排障视角逐段检查请阅读当前 Android 工程按 READ_CONTACTS 权限链路排查 AutoCompleteTextView 联系人自动补全不生效的问题。 重点检查 1. AndroidManifest.xml 是否声明 READ_CONTACTS 2. onRequestPermissionsResult 的 requestCode 是否与请求一致授权成功后是否调用 initAutoComplete 3. query 的 projection 是否包含 Phone._ID 和 Phone.DISPLAY_NAME 4. Cursor 是否为 nullgetCount 是否为 0 5. SimpleCursorAdapter 的 from 和 to 是否匹配 6. AutoCompleteTextView 是否 setAdapterthreshold 是否为 1。 不要改业务逻辑只输出文件路径、可疑行号、问题原因、最小修复建议。Codex 返回后你重点看它有没有指出这几种情况权限回调提前 returngrantResults 空数组_ID缺失Cursor 查询后未判空SimpleCursorAdapter 的 from 列不在 projection 中android.R.id.text1与布局不匹配AutoCompleteTextView 没有 setAdapterthreshold 仍为默认 2。代码静态检查之后再跑真机或模拟器验证。第一次启动时应该弹出 READ_CONTACTS 授权框。点允许后onRequestPermissionsResult应进入授权成功分支并调用initAutoComplete()。此时看 Logcatadb logcat | grep ContactAuto如果看到cursor count0说明权限大概率已经通了但联系人数据为空或者 URI/查询条件有问题。去系统联系人 App 手动添加几个联系人例如“张三”“张伟”“李四”再回到你的 App 输入“张”。如果看到cursor count3同时输入“张”后下拉出现联系人姓名说明 READ_CONTACTS、ContentResolver、Cursor、SimpleCursorAdapter、AutoCompleteTextView 这条链路已经通了。若 count 大于 0 但还是不弹优先检查 threshold 和 adapter 是否被后续代码覆盖。五、本篇常见错排查AndroidManifest.xml、_ID、Cursor、threshold 与权限回调第一个高频坑是 AndroidManifest.xml 写了 READ_CONTACTS但运行时没有动态申请。Android 6.0 以后危险权限必须走运行时请求只在 Manifest 声明不会自动授权。排查时看ensureContactsPermission()是否在 onCreate 或按钮事件中被调用requestPermissions的 requestCode 是否和回调一致。第二个高频坑是 onRequestPermissionsResult 没走到 initAutoComplete。常见写法是 requestCode 对不上例如申请用 1001回调判断写 1或者 grantResults 长度为 0 时仍然继续或者用户拒绝后没有提示导致后面一直误以为查询坏了。授权成功后必须明确调用initAutoComplete()否则 AutoCompleteTextView 一直没有数据源。第三个高频坑是 projection 缺_ID。SimpleCursorAdapter 依赖 Cursor 中名为_id的列联系人查询里对应ContactsContract.CommonDataKinds.Phone._ID。如果 projection 只写 DISPLAY_NAME可能出现列不存在、绑定失败或者下拉项异常。正确做法是把_ID和DISPLAY_NAME都放进 projection。第四个高频坑是 Cursor 查询没结果。输入“张”不弹不一定是代码错也可能是联系人数据库为空。先看cursor.getCount()。如果为 0检查模拟器是否有联系人检查 URI 是否写成ContactsContract.CommonDataKinds.Phone.CONTENT_URI检查 selection 是否误加了过滤条件。如果 Cursor 为 null检查权限是否真的授予以及 ContentResolver 是否在正确 Context 上调用。第五个高频坑是 SimpleCursorAdapter 的 from/to 不匹配。from里写的是 projection 中的列名to里写的是 item 布局中的 TextView id。使用android.R.layout.simple_dropdown_item_1line时对应android.R.id.text1如果换成自定义布局必须确认布局里存在相同 id。否则下拉项可能空白或直接异常。第六个高频坑是 AutoCompleteTextView 的 threshold。默认阈值通常是 2用户输入“张”只有一个字符不会触发补全。XML 里设置android:completionThreshold1或者在代码里setThreshold(1)。如果阈值没问题再检查 adapter 是否被覆盖、输入框 id 是否找错、输入法是否把中文组合输入当作未提交字符。第七个坑是 Cursor 生命周期。把 Cursor 交给 SimpleCursorAdapter 后不要立刻 close否则 adapter 后续读取会失败。也不要在权限还没授予时提前 query否则第一次启动必然为空。等授权成功回调后再初始化查询才是稳定顺序。六、语义一致 CTA排障接入走 API Keys 与接入文档长期编码看 Coding Plan如果你已经按上面的链路排查到具体错误下一步建议按目的分流。排障和接入优先走 API Keys 与接入文档先确认 Key 是否创建正确再核对 Codexconfig.toml里的base_url是否写成https://taotoken.net/apienv_key是否指向TAOTOKEN_API_KEY。API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。如果你只是想验证模型通道是否已经通可以到模型对话页面发一条最短请求确认 Key、Base URL 和模型 ID 能正常返回。模型对话入口https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。如果你准备长期把 Codex 用在 Android 工程排查、Agent 任务或日常编码辅助上可以继续看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。回到本篇场景最终验收仍然很直接授予 READ_CONTACTS 后在 AutoCompleteTextView 输入“张”下拉出现联系人姓名就说明 ContentProvider 查询、Cursor 和 SimpleCursorAdapter 这条链路已经接上了。
返回列表