Android ContactsContract存储与读取联系人自定义数据的实现方案咨询
自定义联系人收藏歌曲字段实现方案
1. 前置配置
权限声明
在AndroidManifest.xml中添加读写联系人权限:
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />
Android 6.0及以上版本需要在运行时动态申请上述权限,获取用户授权后才可执行后续操作。
常量定义
首先定义全局唯一的自定义MIME类型和本地账户信息,避免和其他应用冲突:
// 自定义MIME类型,格式为vnd.android.cursor.item/[你的应用包名].[自定义字段名] const val MIME_TYPE_FAVORITE_SONG = "vnd.android.cursor.item/com.yourpackage.favorite_song" // 本地账户类型,无需同步可直接使用自定义值 const val LOCAL_ACCOUNT_TYPE = "com.yourpackage.local_account" const val LOCAL_ACCOUNT_NAME = "App Local Account"
2. 给联系人添加收藏歌曲数据
使用ContentProviderOperation批量操作,创建归属当前应用的RawContact并写入自定义数据,自动关联到目标联系人:
import android.content.ContentProviderOperation import android.content.ContentResolver import android.provider.ContactsContract fun addFavoriteSongToContact(resolver: ContentResolver, contactId: String, songName: String): Boolean { return try { val operations = arrayListOf<ContentProviderOperation>() // 第一步:插入属于当前应用的本地RawContact val rawContactOp = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, LOCAL_ACCOUNT_TYPE) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, LOCAL_ACCOUNT_NAME) .withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) .build() operations.add(rawContactOp) // 第二步:关联RawContact到目标聚合联系人 val aggregateOp = ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI) .withValue(ContactsContract.AggregationExceptions.TYPE, ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER) .withValueBackReference(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, 0) .withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, getRawContactIdByContactId(resolver, contactId)) .build() operations.add(aggregateOp) // 第三步:插入自定义收藏歌曲数据 val dataOp = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValue(ContactsContract.Data.MIMETYPE, MIME_TYPE_FAVORITE_SONG) .withValue(ContactsContract.Data.DATA1, songName) .build() operations.add(dataOp) resolver.applyBatch(ContactsContract.AUTHORITY, operations) true } catch (e: Exception) { e.printStackTrace() false } } // 辅助方法:根据contactId获取对应的原始RawContactId用于聚合 private fun getRawContactIdByContactId(resolver: ContentResolver, contactId: String): Long? { val cursor = resolver.query( ContactsContract.RawContacts.CONTENT_URI, arrayOf(ContactsContract.RawContacts._ID), "${ContactsContract.RawContacts.CONTACT_ID} = ?", arrayOf(contactId), null ) return cursor?.use { if (it.moveToFirst()) it.getLong(0) else null } }
3. 查询联系人关联的收藏歌曲
扩展数据类
修改原有ItemContact添加收藏歌曲字段:
data class ItemContact ( val name: String, val number: String, val contact_id: String, val favoriteSong: String? = null )
查询自定义数据方法
fun getFavoriteSongByContactId(resolver: ContentResolver, contactId: String): String? { val projection = arrayOf(ContactsContract.Data.DATA1) val selection = "${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?" val selectionArgs = arrayOf(contactId, MIME_TYPE_FAVORITE_SONG) resolver.query( ContactsContract.Data.CONTENT_URI, projection, selection, selectionArgs, null )?.use { cursor -> if (cursor.moveToFirst()) { return cursor.getString(0) } } return null }
改造原有联系人查询逻辑
在原有fetchContacts方法中,遍历联系人时调用上述查询方法填充favoriteSong字段即可:
fun fetchContacts(resolver: ContentResolver) : MutableList<ItemContact> { val cols = arrayOf( ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.CONTACT_ID ) val contactsList = mutableListOf<ItemContact>() resolver.query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, cols, null, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME )?.use { cursor -> while(cursor.moveToNext()){ val contactId = cursor.getString(2) contactsList.add(ItemContact( name = cursor.getString(0), number = cursor.getString(1), contact_id = contactId, favoriteSong = getFavoriteSongByContactId(resolver, contactId) )) } } return contactsList }
注意事项
- 所有联系人相关操作需要放在子线程执行,避免阻塞主线程引发ANR
- 如果需要修改已保存的收藏歌曲,直接查询到对应Data表的id后执行update操作即可
- 不需要云端同步的场景下无需实现同步适配器,自定义数据仅保存在本地,其他应用无法访问
- 卸载应用后,你创建的RawContact和对应的自定义数据会被系统自动清除
内容的提问来源于stack exchange,提问作者Jonathan Fenno
相关产品推荐
相关产品推荐

