跳转至正文

2027 年 Google Play 通讯录政策:Capacitor 指南

Google Play 的联系人权限政策将从 2027 年 1 月起限制 READ_CONTACTS。了解 Capacitor 应用会受到哪些影响,以及如何使用 Contact Picker。

Capacitor 应用内 Google Play 联系人权限政策的隐私说明图

Google Play 的联系人权限政策意味着,从 2027 年 1 月 27 日起,目标为 Android 17(API 级别 37)或更高版本的应用只能请求 READ_CONTACTS 前提是 Android Contact Picker 无法支持核心功能,并且必须通过 Play Console 声明说明理由。对于大多数通过读取联系人来邀请好友、分享文件或填写手机号的 Capacitor 应用,解决办法是改用 Contact Picker 并移除该权限。本指南会解释该政策,并演示如何借助 @capgo/capacitor-contacts,并说明如果确实需要完整通讯录时该怎么做。

政策内容

Google 于 2026 年 4 月 15 日发布了这项政策,作为一系列隐私更新的一部分。以下是 Play Console 帮助页面的核心要点:

  • 适用范围: 针对 Android 17 (API 37) 或更高版本并请求 READ_CONTACTS.
  • 规则: 你只能在以下情况下请求权限: READ_CONTACTS 如果 Android Contact Picker 无法满足应用的核心功能需求。
  • 声明: 必须保留该权限的应用需在 Play Console 中填写声明,说明哪些功能需要它,以及为什么 Picker 不够用。
  • 时间线: Play Console 已开始提示声明该权限的应用 READ_CONTACTS 2026 年 9 月生效。2027 年 1 月 27 日起强制合规。Google 在 Play Console 中提及可申请 30 天的自助延期。
  • 豁免情况: 私有应用和企业设备管理应用不在范围内。
  • 数据: 根据用户数据政策,联系人数据属于个人敏感信息。未经数据主体授权,您不得发布非公开的联系人数据。

可保留 READ_CONTACTS 权限的使用场景

Google 列出的核心功能包括:联系人管理应用、拨号器和短信应用、通话记录与来电筛选、CRM、辅助功能、个人助理、好友匹配、备份与恢复,以及键盘或自动补全。

应切换至选择器(Picker)的使用场景

邀请或推荐他人、分享文件、协作,或选择联系人以发送款项。如果您的应用读取联系人仅出于这些目的,声明将不会被接受。请使用选择器。

Android 17 的变化

Android 17 引入了新的系统联系人选择器。应用将获得一个临时的、只读的会话 URI,仅暴露用户选定的联系人和字段,且无需任何权限。对于目标 API 为 Android 17 的应用,系统会自动将传统的 Intent.ACTION_PICK 联系人 Intent 升级为此新选择器。应用还可以通过新的 Intent Action 请求特定数据字段(如电话、邮箱、邮政地址)并支持多选。

在旧版 Android 上,通过联系人选择器选取特定数据行(例如电话号码)时,返回的 URI 一直允许应用无需 READ_CONTACTS即可读取。这使得在所有 Android 版本上都能实现无权限流程。

为什么“选择联系人”过去需要 READ_CONTACTS 权限

许多 Capacitor 插件通过打开联系人选择器来选取整个联系人,然后查询通讯录以获取该联系人的电话和邮箱。第二次查询需要 READ_CONTACTS。因此,即使应用只想获取一个电话号码,也最终声明了该权限,而这正是该政策所针对的问题。

修复方法是让选择器直接请求所需的特定字段,这样返回的 URI 就已经携带了数据。

无需 READ_CONTACTS 即可选择电话、邮箱或地址

@capgo/capacitor-contacts 在 8.1.0 版本中添加了 property 选项。设置后,Android 会针对电话、邮箱或邮政地址表打开选择器,并从授权的 URI 中读取选中的行。无需 READ_CONTACTS 在任何 Android 版本上都需要。在 iOS 上,系统选择器始终不需要联系人权限。

bun add @capgo/capacitor-contacts
bunx cap sync
import { CapacitorContacts, ContactProperty } from '@capgo/capacitor-contacts';

export async function pickPhoneNumber(): Promise<string | undefined> {
  const { contacts } = await CapacitorContacts.pickContacts({
    property: ContactProperty.PhoneNumber,
  });
  const picked = contacts[0];
  return picked?.phoneNumbers?.[0]?.value;
}

export async function pickEmail(): Promise<string | undefined> {
  const { contacts } = await CapacitorContacts.pickContacts({
    property: ContactProperty.EmailAddress,
  });
  return contacts[0]?.emailAddresses?.[0]?.value;
}

使用 property,结果包含该联系人 id, displayName 以及所选属性。多选会被忽略,属性选择始终只返回一个值。如果用户取消,请检查是否为空数组。

该插件不会在 READ_CONTACTS 或 WRITE_CONTACTS 自身的 manifest 中声明,因此如果你只使用属性选择,合并后的 manifest 会保持干净。

当你仍需要 READ_CONTACTS 时

这些插件方法会读取通讯录,因此仍然需要该权限:

  • pickContacts() 或 pickContact() 无需 property,当你需要完整的联系人记录时
  • getContacts(), getContactById(), countContacts()
  • getGroups(), getGroupById(), getAccounts()

如果您的应用是 CRM、备份工具或好友匹配功能,这属于合理的核心用途。请自行添加权限并提交声明:

<!-- android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
import { CapacitorContacts } from '@capgo/capacitor-contacts';

const perm = await CapacitorContacts.requestPermissions({ permissions: ['readContacts'] });
if (perm.readContacts === 'granted' || perm.readContacts === 'limited') {
  const { contacts } = await CapacitorContacts.getContacts({
    fields: ['displayName', 'phoneNumbers'],
    limit: 200,
    offset: 0,
  });
  console.log(contacts.length);
}

本政策针对 READ_CONTACTS. 使用 写入联系人 WRITE_CONTACTS (针对 createContact, updateContactById, deleteContactById)不属于本政策的范围,但只有用到这些方法时才添加。

Capacitor 应用迁移方案

  1. 检查合并后的 Manifest。 构建 Release 版本并打开 android/app/build/intermediates/merged_manifest/ (或使用 Android Studio 的 Merged Manifest 标签页)。某些插件会添加 READ_CONTACTS 而您却毫无察觉。如果某个插件添加了该权限且您并不需要,请使用以下命令将其移除: tools:node="remove":

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
        <uses-permission android:name="android.permission.READ_CONTACTS" tools:node="remove" />
    </manifest>
  2. 列出所有涉及联系人的位置。 针对每一处,决定是使用带属性的选择器,还是访问完整通讯录。

  3. 替换邀请和分享流程 配合 pickContacts({ property }).

  4. 仅为核心功能保留该权限,并准备好声明文案:说明具体功能、为何选择器不够用,若功能较难找到,请附上演示视频。

  5. 发布原生构建。 Manifest 和插件变更需要新的 Play 商店版本。如果您在 CI 中构建, Capgo 构建 可以生成已签名的 Android 构建,并且 Android keystore 生成器 如果你第一次配置签名,这会有所帮助。

  6. 通过空中推送(OTA)发布 UI 更改。 新的原生插件上线后,选择器周围的文案和流程调整可以通过 Capgo 实时更新.

从其他联系人插件迁移

如果你使用 @capacitor-community/contacts (撰写本文时为 8.0.0 版本),其 Android 端 pickContact 会在打开选择器前请求 contacts 权限组,然后从通讯录读取完整联系人。它没有属性模式,方法名也不同(getContact 对比 getContactById使用 projection 对象而非 fields 数组)。我们 对 community contacts 插件与 Capgo 插件进行了对比 并映射了每次调用。

如果你仍在 Capacitor 中使用 Cordova contacts 插件,现在是替换它的好时机。请参阅 从 Cordova 迁移到 Capacitor.

故障排除

选择器返回了一个没有电话号码的联系人。 您调用了 pickContacts() 无需 property 且应用没有 READ_CONTACTS. 仅 id 以及 displayName (以及 Android 17+ 中通过选择器授予的字段)会恢复。添加 property.

Play Console 仍显示应用声明了 READ_CONTACTS。 某个库添加了它。检查合并后的 manifest 并使用以下命令移除: tools:node="remove"。同时检查其他轨道中的旧版本发布,每个活跃构建产物都会计入。

声明被拒绝。 邀请、推荐和分享被列为选择器的适用场景。重构该功能,或明确说明为何完整联系人列表是核心功能(例如针对整个通讯录运行的好友匹配)。

iOS 请求联系人权限。 你调用了读取通讯录的方法。仅选择联系人无需此权限。移除 NSContactsUsageDescription 如果你只进行联系人选择。

关键日期

日期 会发生什么
2026年4月15日 政策发布
2026年9月 Play Console 将提示声明了 READ_CONTACTS 的应用提交声明
2027年1月27日 面向 Android 17 (API 37) 或更高版本的应用必须合规

提交前请查阅 Play Console 帮助页面了解该政策,Google 可能会更新截止日期和措辞。

为 Capacitor 应用提供 Live Updates

当 Web 层出现线上 Bug 时,通过 Capgo 发布修复,而不用等 App Store 审核好几天。用户会在后台收到更新,原生变更则仍走正常审核流程。

博客最新动态

Capgo 为你提供打造真正专业的移动应用所需的最有价值的洞察。