diff --git a/new/Data/Contact.java b/new/Data/Contact.java index b2750afffce3e645a1ae147d1163c52083d43af0..db7f3d03b982e28477da4d3b16a1bcc124685572 100644 --- a/new/Data/Contact.java +++ b/new/Data/Contact.java @@ -17,85 +17,102 @@ package net.micode.notes.data; import android.content.Context; -//接口有关应用程序环境的全局信息。 这是一个抽象类,其实现由Android系统提供。 -//它允许访问特定于应用程序的资源和类,以及对诸如启动活动,广播和接收意图等应用程序级操作的上调。 - +// 应用上下文类:提供应用全局环境信息,用于访问系统服务、资源、ContentResolver等核心功能 import android.database.Cursor; -//该接口提供对数据库查询返回的结果集的随机读写访问。 -//游标实现不需要同步,因此使用Cursor时使用来自多个线程的Cursor的代码应该执行自己的同步。 - +// 数据库游标类:用于读取数据库查询结果集,提供随机读写访问能力 import android.provider.ContactsContract.CommonDataKinds.Phone; -//代表电话号码的数据类型。 - +// 联系人电话数据常量类:定义联系人电话号码相关的字段(如号码、显示姓名)和MIME类型 import android.provider.ContactsContract.Data; -//数据表的常量,其中包含绑定到原始联系人的数据点。 -//数据表的每一行通常用于存储单个联系人信息(例如电话号码)及其关联的元数据(例如,它是工作还是家庭号码)。 - +// 联系人数据表常量类:定义联系人基础数据表的URI、字段(如原始联系人ID、MIME类型)等 import android.telephony.PhoneNumberUtils; -//处理电话号码字符串的各种实用程序。 - +// 电话号码工具类:提供电话号码格式化、匹配、校验等通用方法 import android.util.Log; -//用于发送日志输出的API。 - +// 日志工具类:用于输出调试/错误日志,便于问题排查 import java.util.HashMap; -//基于哈希表的Map接口的实现。 此实现提供了所有可选映射操作,并允许null值和null密钥 -//( HashMap类大致相当于Hashtable ,除了它是不同步的并且允许空值。)这个类不能保证地图的顺序; -//特别是,它不能保证订单会随着时间的推移保持不变。 +// 哈希表实现类:用于构建内存缓存,存储"电话号码-联系人姓名"的映射关系,提升查询效率 +/** + * 联系人工具类 + * 核心功能:根据电话号码查询对应的联系人姓名,并通过内存缓存优化重复查询性能 + */ public class Contact { + // 静态缓存集合:全局唯一,存储已查询过的"电话号码-联系人姓名"键值对,避免重复查询数据库 private static HashMap sContactCache; -//构建一个空的hashmap(联系人缓存) + + // 日志标签:用于日志输出时标识来源,方便定位问题 private static final String TAG = "Contact"; -//声明一个TAG常量(联系人) + /** + * 来电显示查询条件(SQL WHERE子句) + * 核心逻辑: + * 1. PHONE_NUMBERS_EQUAL(Phone.NUMBER, ?):匹配电话号码(系统内置的号码匹配函数,兼容格式差异) + * 2. Data.MIMETYPE = Phone.CONTENT_ITEM_TYPE:限定数据类型为电话号码(过滤其他类型的联系人数据) + * 3. Data.RAW_CONTACT_ID IN (子查询):通过phone_lookup表筛选匹配的原始联系人ID,提升查询准确性 + */ private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER - + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" - + " AND " + Data.RAW_CONTACT_ID + " IN " + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + "(SELECT raw_contact_id " + " FROM phone_lookup" + " WHERE min_match = '+')"; -//CALLER_ID_SELECTION 来电显示选择 -//PHONE_NUMBERS_EQUAL 电话号码等于 + /** + * 根据电话号码查询联系人姓名 + * @param context 应用上下文:用于获取ContentResolver访问系统联系人数据库 + * @param phoneNumber 要查询的电话号码(支持任意格式,工具类会自动处理) + * @return 匹配的联系人姓名(无匹配则返回null) + */ public static String getContact(Context context, String phoneNumber) { - if(sContactCache == null) { + // 1. 初始化缓存:首次调用时创建HashMap实例,保证全局唯一 + if (sContactCache == null) { sContactCache = new HashMap(); } - if(sContactCache.containsKey(phoneNumber)) { + // 2. 缓存命中校验:如果缓存中已有该号码的姓名,直接返回,避免查询数据库 + if (sContactCache.containsKey(phoneNumber)) { return sContactCache.get(phoneNumber); } -//获取联系人的电话号码 + // 3. 构建查询条件:替换子查询中的min_match参数为当前号码的最小匹配值 + // PhoneNumberUtils.toCallerIDMinMatch():根据号码长度生成匹配规则,提升号码匹配准确性 String selection = CALLER_ID_SELECTION.replace("+", PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); -//当前映射到指定值时替换指定键的条目。 + // 4. 查询系统联系人数据库 + // ContentResolver.query()参数说明: + // - uri:查询的数据源(联系人数据表的URI) + // - projection:要查询的字段(仅查显示姓名,减少数据传输) + // - selection:查询条件(即上面构建的CALLER_ID_SELECTION) + // - selectionArgs:查询条件的参数(电话号码,防止SQL注入) + // - sortOrder:排序方式(null表示使用默认排序) Cursor cursor = context.getContentResolver().query( Data.CONTENT_URI, - new String [] { Phone.DISPLAY_NAME }, + new String[]{Phone.DISPLAY_NAME}, selection, - new String[] { phoneNumber }, + new String[]{phoneNumber}, null); -//返回此光标数据中的更改通知将传递的URI - if (cursor != null && cursor.moveToFirst()) { -//如果不为空且能将光标移到第一行 + // 5. 处理查询结果 + if (cursor != null && cursor.moveToFirst()) { // 游标非空且存在数据(有匹配的联系人) try { + // 获取查询结果的第一个字段(即Phone.DISPLAY_NAME):联系人姓名 String name = cursor.getString(0); + // 将查询结果存入缓存,供后续查询使用 sContactCache.put(phoneNumber, name); + // 返回匹配的联系人姓名 return name; } catch (IndexOutOfBoundsException e) { - Log.e(TAG, " Cursor get string error " + e.toString()); + // 异常捕获:防止游标索引越界(如字段不存在时) + Log.e(TAG, "Cursor读取联系人姓名出错: " + e.toString()); return null; } finally { + // 最终操作:关闭游标,释放数据库资源(必须执行,防止内存泄漏) cursor.close(); } -//返回与号码匹配联系人的名字,若索引越界异常,则报错返回空,最后释放资源。 } else { - Log.d(TAG, "No contact matched with number:" + phoneNumber); + // 无匹配的联系人:输出调试日志,返回null + Log.d(TAG, "未找到匹配的联系人,电话号码:" + phoneNumber); return null; } -//没有联系人和这个号码匹配返回空 } -} +} \ No newline at end of file