You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
git/java/net/micode/notes/data/Contact.java

125 lines
5.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.data;
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Data;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import java.util.HashMap;
/**
* 联系人工具类
* 核心功能:根据电话号码从安卓系统的联系人数据库中查询对应的联系人名称,
* 并通过静态HashMap实现缓存机制避免重复查询数据库提升查询性能
* 该类为工具类,所有成员和方法均为静态,无需实例化即可调用
*/
public class Contact {
/**
* 静态缓存集合,存储电话号码与联系人名称的映射关系
* Key电话号码String类型Value对应的联系人名称String类型
* 采用静态HashMap实现全局缓存跨多次方法调用共享数据
*/
private static HashMap<String, String> sContactCache;
/**
* 日志标签用于Logcat输出时标识当前类方便调试时定位问题
*/
private static final String TAG = "Contact";
/**
* 联系人查询的筛选条件模板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 "
+ "(SELECT raw_contact_id "
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
/**
* 根据电话号码获取对应的联系人名称
* 处理流程:
* 1. 初始化缓存集合(首次调用时)
* 2. 检查缓存:若电话号码已在缓存中,直接返回联系人名称(缓存命中)
* 3. 处理筛选条件:替换模板中的占位符为电话号码的最小匹配值
* 4. 查询联系人数据库通过ContentResolver获取联系人名称
* 5. 将查询结果存入缓存返回名称若查询失败则返回null
*
* @param context 上下文对象用于获取ContentResolver以访问安卓联系人数据库
* @param phoneNumber 要查询的电话号码String类型支持不同格式的号码
* @return 对应的联系人名称查询不到则返回null
*/
public static String getContact(Context context, String phoneNumber) {
// 首次调用方法时,初始化静态缓存集合
if(sContactCache == null) {
sContactCache = new HashMap<String, String>();
}
// 缓存命中:直接返回缓存中的联系人名称,避免数据库查询
if(sContactCache.containsKey(phoneNumber)) {
return sContactCache.get(phoneNumber);
}
// 替换筛选条件模板中的"+"占位符为电话号码的最小匹配值(系统工具类处理,提升号码匹配准确性)
String selection = CALLER_ID_SELECTION.replace("+",
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
// 调用ContentResolver查询联系人数据库
// 参数说明:
// 1. Data.CONTENT_URI安卓联系人数据的总URI
// 2. 投影数组仅查询Phone.DISPLAY_NAME列联系人名称减少数据传输
// 3. selection处理后的筛选条件
// 4. selectionArgs传入电话号码作为查询参数防止SQL注入
// 5. sortOrder排序规则null表示无需排序
Cursor cursor = context.getContentResolver().query(
Data.CONTENT_URI,
new String [] { Phone.DISPLAY_NAME },
selection,
new String[] { phoneNumber },
null);
// 游标非空且包含数据时,处理查询结果
if (cursor != null && cursor.moveToFirst()) {
try {
// 获取联系人名称索引0对应投影数组中的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());
return null;
} finally {
// 最终关闭游标,释放数据库资源(必须执行,避免资源泄漏)
cursor.close();
}
} else {
// 游标为空或无数据时输出调试日志返回null
Log.d(TAG, "No contact matched with number:" + phoneNumber);
return null;
}
}
}