You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发:如何从其他应用更新通讯录App的用户头像与姓名

Android跨应用更新系统通讯录User Profile头像与姓名实现方案

前置权限要求

  • 应用需要在清单文件声明WRITE_CONTACTS权限,针对Android 6.0(API 23)及以上版本,必须动态向用户申请该危险权限,拿到授权后才可执行修改操作
  • 无需申请已废弃的WRITE_PROFILE权限,该权限从API 23开始仅系统应用可获取,第三方应用申请后也不会被授予

注意:仅设备主用户空间下的应用可修改系统User Profile,工作资料、访客空间、多用户场景下的子用户无操作权限,执行代码前可先判断当前用户是否为主用户避免报错。

具体实现步骤

  • 第一步:查询User Profile对应的RawContact ID
    系统User Profile不是普通联系人条目,必须使用专属URI查询,不能直接用普通联系人的查询地址:
val profileRawContactUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI
val cursor = contentResolver.query(
    profileRawContactUri,
    arrayOf(ContactsContract.RawContacts._ID),
    null,
    null,
    null
)
var targetRawContactId = -1L
cursor?.use {
    if (it.moveToFirst()) {
        targetRawContactId = it.getLong(it.getColumnIndexOrThrow(ContactsContract.RawContacts._ID))
    }
}
  • 第二步:初始化Profile条目(仅查询不到ID时需要)
    部分设备出厂或首次开机时未初始化个人资料条目,直接写入数据会报错,需要先创建基础RawContact:
if (targetRawContactId == -1L) {
    val insertValues = ContentValues().apply {
        // 本地账户类型,不需要关联第三方账户
        put(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
        put(ContactsContract.RawContacts.ACCOUNT_NAME, null)
        put(ContactsContract.RawContacts.IS_USER_PROFILE, 1)
    }
    val insertResult = contentResolver.insert(profileRawContactUri, insertValues)
    targetRawContactId = ContentUris.parseId(insertResult!!)
}
  • 第三步:写入/更新用户姓名
    姓名数据存储在联系人Data表中,对应结构化姓名的数据类型,写入前先判断是否已有存量数据,避免重复插入多条姓名记录:
val nameMimeType = ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
val nameContentValues = ContentValues().apply {
    put(ContactsContract.Data.RAW_CONTACT_ID, targetRawContactId)
    put(ContactsContract.Data.MIMETYPE, nameMimeType)
    put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, "你要设置的用户显示名")
    // 按需可拆分设置名、姓、拼音等字段
    // put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "名")
    // put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, "姓")
}

// 查询已有姓名条目
val nameQueryCursor = contentResolver.query(
    ContactsContract.Data.CONTENT_URI,
    arrayOf(ContactsContract.Data._ID),
    "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?",
    arrayOf(targetRawContactId.toString(), nameMimeType),
    null
)
nameQueryCursor?.use {
    if (it.moveToFirst()) {
        // 已有条目执行更新
        val existEntryId = it.getLong(it.getColumnIndexOrThrow(ContactsContract.Data._ID))
        val updateUri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, existEntryId)
        contentResolver.update(updateUri, nameContentValues, null, null)
    } else {
        // 无条目执行插入
        contentResolver.insert(ContactsContract.Data.CONTENT_URI, nameContentValues)
    }
}
  • 第四步:写入/更新用户头像
    头像同样存储在Data表中,注意传入的图片字节数组不要过大,建议先将图片裁剪为720*720分辨率、压缩到500KB以内,否则系统通讯录可能加载失败:
val photoMimeType = ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
// 将目标头像Bitmap压缩为JPEG格式字节数组
val byteArrayOutputStream = ByteArrayOutputStream()
targetAvatarBitmap.compress(Bitmap.CompressFormat.JPEG, 85, byteArrayOutputStream)
val photoByteArray = byteArrayOutputStream.toByteArray()

val photoContentValues = ContentValues().apply {
    put(ContactsContract.Data.RAW_CONTACT_ID, targetRawContactId)
    put(ContactsContract.Data.MIMETYPE, photoMimeType)
    put(ContactsContract.CommonDataKinds.Photo.PHOTO, photoByteArray)
}

// 查询已有头像条目
val photoQueryCursor = contentResolver.query(
    ContactsContract.Data.CONTENT_URI,
    arrayOf(ContactsContract.Data._ID),
    "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?",
    arrayOf(targetRawContactId.toString(), photoMimeType),
    null
)
photoQueryCursor?.use {
    if (it.moveToFirst()) {
        val existEntryId = it.getLong(it.getColumnIndexOrThrow(ContactsContract.Data._ID))
        val updateUri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, existEntryId)
        contentResolver.update(updateUri, photoContentValues, null, null)
    } else {
        contentResolver.insert(ContactsContract.Data.CONTENT_URI, photoContentValues)
    }
}

常见问题说明

  • 不要直接对ContactsContract.Contacts层级的Profile URI执行写入操作,该地址是系统聚合生成的只读视图,直接写入会抛出权限异常
  • 部分深度定制ROM会对User Profile修改做额外限制,写入完成后可以主动查询一次显示名和头像字段,验证写入结果,若写入失败可提示用户手动在系统通讯录中修改
  • 写入完成后不需要主动发送广播刷新通讯录,系统会自动监听ContactsProvider的数据变化更新UI
  • Android 13及以上版本、Android 14版本的细分联系人权限不影响该操作,即使用户只授予了应用部分联系人访问权限,依然可以正常修改User Profile条目

内容的提问来源于stack exchange,提问作者vinit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 15:51:41