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

安卓短信应用开发:如何通过recipient_id获取正确联系人姓名?

问题分析

你遇到的核心问题是对recipient_ids字段的理解有误:从content://mms-sms/conversations返回的recipient_ids,并不是ContactsContract.Contacts中的CONTACT_ID,而是Android短信系统内部维护的Recipient表ID(属于mms-sms私有数据库)。直接用这个ID去关联联系人表的CONTACT_ID,自然会拿到错误的结果。

解决方案

要通过recipient_ids获取正确的联系人姓名,需要先通过短信系统的规范地址表(canonical-addresses)拿到对应的实际号码,再通过号码去匹配联系人姓名;或者直接通过该表关联到联系人的CONTACT_ID。

步骤1:理解canonical-addresses表

这个表是mms-sms库用来映射内部收件人ID和实际联系人号码的核心表,关键字段:

  • _id:对应conversations中的recipient_ids值
  • address:与联系人表匹配的真实电话号码

步骤2:重写联系人姓名获取逻辑

下面提供两种可行的实现方式,你可以根据需求选择:

方法一:先拿真实号码,再查姓名(更直观)

// 通过recipient_id获取真实电话号码
private String getRealNumberFromRecipientId(String recipientId) {
    String realNumber = null;
    Uri canonicalUri = Uri.parse("content://mms-sms/canonical-addresses");
    Cursor cursor = getContentResolver().query(
            canonicalUri,
            new String[]{"address"},
            "_id = ?",
            new String[]{recipientId},
            null
    );
    if (cursor != null && cursor.moveToFirst()) {
        realNumber = cursor.getString(cursor.getColumnIndex("address"));
        cursor.close();
    }
    return realNumber;
}

// 通过电话号码获取联系人姓名,未找到则返回号码本身
private String getContactNameByNumber(String phoneNumber) {
    String contactName = phoneNumber;
    Uri lookupUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
    Cursor cursor = getContentResolver().query(
            lookupUri,
            new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME},
            null,
            null,
            null
    );
    if (cursor != null && cursor.moveToFirst()) {
        contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
        cursor.close();
    }
    return contactName;
}

在你的循环中替换原有逻辑:

// 替换原来的String num = GetContactData(recipient_id);
String realNumber = getRealNumberFromRecipientId(recipient_id);
String contactName = getContactNameByNumber(realNumber);
msg.setSenderName(contactName);

方法二:直接关联查询CONTACT_ID(更高效)

如果想一步到位,直接通过recipient_id关联到联系人姓名,可以用联合查询:

private String getContactNameByRecipientId(String recipientId) {
    String contactName = null;
    Uri canonicalUri = Uri.parse("content://mms-sms/canonical-addresses");
    String[] projection = new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME};
    // 关联规范地址表和联系人号码表
    String selection = "ca._id = ? AND ContactsContract.CommonDataKinds.Phone.NUMBER = ca.address";
    String[] selectionArgs = new String[]{recipientId};

    Cursor cursor = getContentResolver().query(
            canonicalUri,
            projection,
            selection,
            selectionArgs,
            null
    );
    if (cursor != null && cursor.moveToFirst()) {
        contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
        cursor.close();
    }
    // 未找到联系人时返回真实号码
    if (contactName == null) {
        contactName = getRealNumberFromRecipientId(recipientId);
    }
    return contactName;
}

额外优化:修复代码中的小错误

你的代码里有一个明显的逻辑错误:

String address = cur.getString(cur.getColumnIndex("_id"));

这里你把会话ID(_id)赋值给了address变量,应该改为获取会话对应的真实号码字段:

String address = cur.getString(cur.getColumnIndex("address"));
注意事项
  • 确保已经在Manifest中声明READ_SMS和READ_CONTACTS权限,并且在Android 6.0及以上版本完成动态权限申请。
  • 部分定制ROM的mms-sms库可能存在字段差异,建议先通过Log.d打印cursor.getColumnNames()确认可用字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:34:52