首页/新闻资讯/正文详情

Android 内容提供器入门:用 ContentResolver 查询数据与权限配置实战

发布时间:2026/9/26 18:05:40 来源:云帆数科 栏目:资讯中心
Android 内容提供器入门:用 ContentResolver 查询数据与权限配置实战
1. 为什么你查联系人总是返回空 Cursor很多人第一次写 Android 内容提供器相关代码时都会遇到一个很迷惑的现象代码没报错ContentResolver.query()也正常返回了一个 Cursor 对象但getCount()就是 0界面上什么都显示不出来。我试过在模拟器上反复检查 URI、列名、排序参数最后发现真正的问题出在权限上——清单里少了一行uses-permission或者运行时权限没申请系统直接把查询结果过滤成了空集。内容提供器ContentProvider是 Android 四大组件之一它把数据封装成类似数据库表的接口让不同应用之间可以安全地共享数据。而 ContentResolver 就是你作为客户端去访问这些数据的入口。它适合谁适合所有需要在应用之间读写共享数据联系人、媒体库、日历、用户字典等的 Android 开发者尤其是刚接触跨进程数据访问、对 URI 和 Cursor 还不熟的同学。这篇内容我会带你跑通一个最小闭环从清单权限声明到 URI 构造到 ContentResolver 查询再到 Cursor 读取和运行时权限申请最后用一个读取联系人的完整例子验证结果。每一步都给可复制的代码骨架你照着改包名就能用。2. 前置准备权限、URI 和查询四要素在动手写查询之前先把三件事理清楚否则后面报错你会不知道从哪查。第一是权限。内容提供器的读权限分两种一种是普通权限在清单里声明后安装时系统自动授予另一种是危险权限比如读取联系人READ_CONTACTS除了清单声明还必须在运行时动态申请。你查的是哪个提供器就要去它的官方文档里找确切的权限名不能猜。第二是 URI。每个提供器都暴露一个或多个 Content URI格式通常是content://authority/path。比如联系人提供器的ContactsContract.Contacts.CONTENT_URI用户字典的UserDictionary.Words.CONTENT_URI。URI 决定了你访问哪张表。第三是查询四要素对应query()的五个参数URI 之外projection要返回哪些列、selection筛选条件、selectionArgs条件里的占位参数、sortOrder排序。这四样和 SQL 的 SELECT 子句一一对应理解了 SQL 就理解了它。注意查询一定要放在子线程或异步任务里执行主线程查询大数据量会触发 ANR。本文为了演示清晰用同步写法实际项目请用 CursorLoader 或协程。3. 可复制配置清单权限与查询代码骨架3.1 清单文件声明权限以读取联系人为例在AndroidManifest.xml的manifest下、application外添加uses-permission android:nameandroid.permission.READ_CONTACTS /如果你还要写数据再加WRITE_CONTACTS。声明之后普通权限安装即授予危险权限还需要下一步的运行时申请。3.2 构造查询并读取 Cursor下面是一个查询联系人姓名和 ID 的骨架注意 projection 里必须包含_ID列否则后面配合 SimpleCursorAdapter 会直接崩// 1. 定义要返回的列 String[] projection { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }; // 2. 筛选条件只取有电话号码的联系人 String selection ContactsContract.Contacts.HAS_PHONE_NUMBER ?; String[] selectionArgs { 1 }; // 3. 排序 String sortOrder ContactsContract.Contacts.DISPLAY_NAME ASC; // 4. 执行查询 Cursor cursor getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, projection, selection, selectionArgs, sortOrder ); // 5. 读取结果 if (cursor ! null) { int idIndex cursor.getColumnIndex(ContactsContract.Contacts._ID); int nameIndex cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); while (cursor.moveToNext()) { String id cursor.getString(idIndex); String name cursor.getString(nameIndex); Log.d(ContactQuery, id id , name name); } cursor.close(); // 必须关闭否则泄漏 } else { Log.e(ContactQuery, cursor is null, 查询失败); }这里 selection 用了?占位符加 selectionArgs而不是把用户输入拼进字符串。这是防 SQL 注入的关键用户输入被当作参数绑定不会被解释成 SQL 语句。如果你写成name userInput恶意输入就可能拼出破坏性语句。3.3 运行时权限申请Android 6.0 起危险权限必须动态申请。在查询前检查并请求if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) ! PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, new String[]{ Manifest.permission.READ_CONTACTS }, REQUEST_CODE_READ_CONTACTS); } else { queryContacts(); // 已授权直接查 }然后在onRequestPermissionsResult里根据授权结果决定是否继续查询。授权通过再调用查询方法否则提示用户。4. 验证请求跑通一次完整的联系人读取把上面的代码串起来完整流程是这样的先在 Activity 的onCreate里调用权限检查逻辑。用户点允许后onRequestPermissionsResult收到PERMISSION_GRANTED触发queryContacts()。查询返回 Cursor 后遍历打印日志。实测下来在模拟器里预置几个联系人运行后 Logcat 会输出类似D/ContactQuery: id1, nameAlice D/ContactQuery: id2, nameBob D/ContactQuery: id3, nameCarol如果你想把结果直接显示到列表可以用 SimpleCursorAdapter 绑定 ListView但记得 Cursor 必须含_ID列String[] fromColumns { ContactsContract.Contacts.DISPLAY_NAME }; int[] toViews { android.R.id.text1 }; SimpleCursorAdapter adapter new SimpleCursorAdapter( this, android.R.layout.simple_list_item_1, cursor, fromColumns, toViews, 0); listView.setAdapter(adapter);看到列表里出现联系人名字就说明从提供器获取数据的最小闭环跑通了。整个过程的核心就是权限到位、URI 正确、projection 含_ID、Cursor 及时关闭。5. 本篇常见错排查查询返回 null部分提供器内部出错时返回 null另一些直接抛异常。先判空再使用别直接cursor.getCount()。getCount() 为 0 但数据明明存在九成是权限问题。检查清单权限名是否拼错、运行时权限是否真的授予。可以在设置里手动看应用权限状态。列名找不到getColumnIndex 返回 -1projection 里没写这一列或者列名拼错。用getColumnIndexOrThrow()能在开发期直接暴露问题。配合 ListView 崩溃Cursor 缺少_ID列。SimpleCursorAdapter 强制要求结果集里有_ID查询时务必带上。主线程查询卡顿或 ANR把查询放到子线程、CursorLoader 或协程里别在主线程做大数据量遍历。SQL 注入风险永远用?加 selectionArgs不要字符串拼接用户输入。6. 接入与调试把查询能力接到实际工程如果你在本地调试时想快速验证某个提供器的返回结构或者需要对比不同模型给出的查询代码是否正确可以借助在线工具做交叉验证。我平时会把查询逻辑和报错信息整理好用模型对话快速定位是 URI 写错还是权限漏了省去反复翻文档的时间。对于需要长期写 Android 代码、频繁调试 ContentResolver 和权限逻辑的场景可以考虑用 Coding Plan 把这类重复性的代码骨架生成和排错固化下来减少手动试错。具体入口想直接对话验证查询代码和报错模型对话 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite长期编码与 Agent 工作流Coding Plan https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite生成和管理接入密钥API Keys https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite接入文档与参数说明接入文档 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content最后留一个实用习惯每次写完查询先在 Logcat 里打印cursor.getColumnNames()确认返回的列和你 projection 里写的一致。这一步能帮你提前发现大部分列名不匹配的问题比等到界面空白再回头查要快得多。

相关推荐

阿里真武V900芯片算力3倍?拆解TOPS、精度与稀疏算力真相
阿里真武V900芯片算力3倍?拆解TOPS、精度与稀疏算力真相

1. 从一颗芯片的发布聊起:为什么"算力倍数"值得关注阿里平头哥发布真武 V900 芯片,官方口径是算力达到 M890 的 3 倍。这条消息在圈子里传开之后,我第一反应不是去看那个"3 倍"的数字,而是去翻它的定位——因… · 2026/9/26 18:05:34

AI 编程学习网站分享:vibe-coding-tutorial 配 TaoToken 的 settings.json 骨架
AI 编程学习网站分享:vibe-coding-tutorial 配 TaoToken 的 settings.json 骨架

/* 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 18:05:27

华为芯片训练DeepSeek模型:工程难点与国产算力实践
华为芯片训练DeepSeek模型:工程难点与国产算力实践

1. 从一条行业消息说起:为什么“用华为芯片训练模型”值得关注前几天在技术圈里刷到一条消息,说 DeepSeek 的梁文锋提到,DeepSeek 计划用华为芯片来训练模型。这条消息本身很短,但信息量不小。作为一个长期关注模型训练和推理部署… · 2026/9/26 18:05:21

Jupyter Notebook 中 matplotlib inline 开关配置:TaoToken 统一 Key 接入与验证
Jupyter Notebook 中 matplotlib inline 开关配置:TaoToken 统一 Key 接入与验证

/* 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 19:39:20

Python直链解析实战:突破网盘限速的下载方案
Python直链解析实战:突破网盘限速的下载方案

1. 直链解析到底在解决什么问题很多人第一次接触"直链解析"这个词,是因为被网盘的下载速度折磨得没脾气。明明家里是千兆宽带,下载一个几百兆的文件,进度条却像蜗牛爬树,几十KB每秒的速度能磨掉一整个下午。这时候就会有… · 2026/9/26 19:39:01

从MyBatis缓存到Redis二级缓存:数据库性能优化实践
从MyBatis缓存到Redis二级缓存:数据库性能优化实践

1. 从一次线上故障说起:缓存优化到底解的是什么问题半年前我们团队接手了一个订单查询系统的性能治理,现象很典型:数据库CPU持续高位,高峰期查询接口的平均响应时间在800ms以上,部分复杂报表查询直接能把连接池打满。当… · 2026/9/26 19:38:55

蒙特卡洛积分:光线追踪降噪与采样策略的核心数学
蒙特卡洛积分:光线追踪降噪与采样策略的核心数学

1. 从一个全是噪点的渲染图说起我最早接触光线追踪时,第一反应是:这东西怎么这么慢?关掉一个看似平平无奇的场景,在1080p分辨率下跑一帧,动辄就是几分钟甚至几十分钟。更让人抓狂的是,好不容易算完&#xf… · 2026/9/26 19:38:55

生产LLM全链路管控:TaoToken统一Key下Token、成本、延迟三位一体优化落地
生产LLM全链路管控:TaoToken统一Key下Token、成本、延迟三位一体优化落地

/* 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 19:38:48

pnpm 忽略构建脚本报错解析与解决方案
pnpm 忽略构建脚本报错解析与解决方案

1. 这个报错到底在说什么第一次看到[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: parcel/watcher2.5.6, canvas2.11.2这行红字,很多人第一反应是“我是不是装崩了”,然后开始疯狂重装、删node_modules、删 lock 文件,折腾半天发现报错还… · 2026/9/26 19:38:35

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* 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 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码