Android 查询通话记录,查询联系人,查询短信

ContentProvider和ContentResolver介绍

ContentProvider 是Android的四大组件之一,提供数据的跨应用程序访问。
从ContentProvider源码看,ContentProvider是抽象类,包含6个抽象方法

public abstract class ContentProvider {
	public abstract boolean onCreate();
    public abstract @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
           @Nullable String selection, @Nullable String[] selectionArgs,
           @Nullable String sortOrder);
    public abstract @Nullable String getType(@NonNull Uri uri);
    public abstract @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values);
    public abstract int delete(@NonNull Uri uri, @Nullable String selection,
            @Nullable String[] selectionArgs);
    public abstract int update(@NonNull Uri uri, @Nullable ContentValues values,
            @Nullable String selection, @Nullable String[] selectionArgs);
}

所有子类ContentProvider都要实现其中的抽象方法,并利用SQLiteDatabase,在抽象方法中实现对数据库的创建与增删改查。

我们在app里想要访问系统其他进程的ContentProvider就需要用到ContentResolver

Context的实现类ContextImpl的构造方法里创建了ContentResolver子类ApplicationContentResolver,并且提供了方法“getContentResolver()”来获取这个ContentResolver

mContentResolver = new ApplicationContentResolver(this, mainThread, user);
@Override
public ContentResolver getContentResolver() {
    return mContentResolver;
}

所以我们在使用中只需要拥有context,就可以获取到当前应用中的ContentResolver
而ContentResolver则提供了增删改查的接口用来访问各种ContactsProvider里的数据库

public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
         @Nullable ContentValues values)
public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
        @Nullable String[] selectionArgs)
public final int update(@RequiresPermission.Write @NonNull Uri uri,
        @Nullable ContentValues values, @Nullable String where,
        @Nullable String[] selectionArgs)
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal)             

即只需要有context,就可以获取到当前应用的ContentResolver


查询通话记录

通话记录的数据库由CallLogProvider提供
文件数据库存储路径/data/data/com.android.providers.contacts/databases/calllog.db

  1. 通话记录的Uri
    由于CallLogProvider注册时的authorities是"call_log",
    所以对应的Uri就是
  public static final String AUTHORITY = "call_log";
  public static final Uri CONTENT_URI =
      Uri.parse("content://" + AUTHORITY);

也即

Uri.parse("content://call_log");

通常Uri后面还要跟上表的名字,所以具体通话记录的Uri路径就是

Uri.parse("content://call_log/calls")

这是个常量,定义在CallLog.java的内部类Calls里

CallLog.Calls.CONTENT_URI = Uri.parse("content://call_log/calls");
  1. 查询通话记录的语法格式
mContext.getContentResolver().query(
				CallLog.Calls.CONTENT_URI,  //Uri
				null,//projection
				null,//selection
				null,//selectionArgs
				null,//sortOrder
)
  1. Calllog数据库的列的序号
  public static final int ID = 0;
  public static final int NUMBER = 1;
  public static final int DATE = 2;
  public static final int DURATION = 3;
  public static final int CALL_TYPE = 4;
  public static final int COUNTRY_ISO = 5;
  public static final int VOICEMAIL_URI = 6;
  public static final int GEOCODED_LOCATION = 7;
  public static final int CACHED_NAME = 8;
  public static final int CACHED_NUMBER_TYPE = 9;
  public static final int CACHED_NUMBER_LABEL = 10;
  public static final int CACHED_LOOKUP_URI = 11;
  public static final int CACHED_MATCHED_NUMBER = 12;
  public static final int CACHED_NORMALIZED_NUMBER = 13;
  public static final int CACHED_PHOTO_ID = 14;
  public static final int CACHED_FORMATTED_NUMBER = 15;
  public static final int IS_READ = 16;
  public static final int NUMBER_PRESENTATION = 17;
  public static final int ACCOUNT_COMPONENT_NAME = 18;
  public static final int ACCOUNT_ID = 19;
  public static final int FEATURES = 20;
  public static final int DATA_USAGE = 21;
  public static final int TRANSCRIPTION = 22;
  public static final int CACHED_PHOTO_URI = 23;
  1. Dialer 中封装的CallLogQueryHandler

在Dialer 这个系统app里,封装了一个CallLogQueryHandler,用于异步查询通话记录。

  public CallLogQueryHandler(
      Context context, ContentResolver contentResolver, Listener listener, int limit)

构造方法需要传入四个参数 context,contentResolver,CallLogQueryHandler的Listener,以及limit查询的最大数目

用法:

callLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL,-1);

其中两个参数代表的是通话类型,和最新时间,这个时间意思是得到比这个时间值更新的数据。

返回的结果就是一个Cursor,会在Listener的回调方法中返回一个不为空的游标。

@Override
public boolean onCallsFetched(Cursor callLogCursor){
}

查询联系人

联系人的数据库由ContactsProvider2提供
文件数据库存储路径/data/data/com.android.providers.contacts/databases/contacts2.db

  1. 联系人的Uri
    由于联系人注册的authoritiescontacts;com.android.contacts
    所以对应的Uri就是
public static final String AUTHORITY = "com.android.contacts";
/** A content:// style uri to the authority for the contacts provider */
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);

Uri.parse("content://com.android.contacts");

通常Uri后面还要加上表格的名字,
然而contact的数据库是分多个表格组成的,一个表格只含有一部分信息,电话号码与显示名称不在同一个表格里。

public interface Tables {
        public static final String CONTACTS = "contacts";
        public static final String DELETED_CONTACTS = "deleted_contacts";
        public static final String RAW_CONTACTS = "raw_contacts";
        public static final String STREAM_ITEMS = "stream_items";
        public static final String STREAM_ITEM_PHOTOS = "stream_item_photos";
        public static final String PHOTO_FILES = "photo_files";
        public static final String PACKAGES = "packages";
        public static final String MIMETYPES = "mimetypes";
        public static final String PHONE_LOOKUP = "phone_lookup";
        public static final String NAME_LOOKUP = "name_lookup";
        public static final String AGGREGATION_EXCEPTIONS = "agg_exceptions";
        public static final String SETTINGS = "settings";
        public static final String DATA = "data";
        public static final String GROUPS = "groups";
        public static final String PRESENCE = "presence";
        public static final String AGGREGATED_PRESENCE = "agg_presence";
        public static final String NICKNAME_LOOKUP = "nickname_lookup";
        public static final String STATUS_UPDATES = "status_updates";
        public static final String ACCOUNTS = "accounts";
        public static final String VISIBLE_CONTACTS = "visible_contacts";
        public static final String DIRECTORIES = "directories";
        public static final String DEFAULT_DIRECTORY = "default_directory";
        public static final String SEARCH_INDEX = "search_index";
        public static final String METADATA_SYNC = "metadata_sync";
        public static final String METADATA_SYNC_STATE = "metadata_sync_state";
        public static final String PRE_AUTHORIZED_URIS = "pre_authorized_uris";
        ....
 }

这些表格中包含显示名称的表格是RAW_CONTACTS = "raw_contacts"
包含电话号码的表格是DATA = "data"
联系人的数据序号信息所在表格是CONTACTS = "contacts"
所以对应的Uri分别是

Uri.parse("content://com.android.contacts/raw_contacts");
Uri.parse("content://com.android.contacts/data");
Uri.parse("content://com.android.contacts/contacts");

而这些Uri也是常量,定义在ContactsContract.java
分别是

ContactsContract.RawContacts.CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts");
ContactsContract.Data.CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");
ContactsContract.Contacts.CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "contacts");
  1. 查询联系人的语法格式
mContext.getContentResolver().query(
				uri,  //Uri
				null,//projection
				null,//selection
				null,//selectionArgs
				null,//sortOrder
)

其中Uri需要指明具体的Uri,由于每个表的数据结构比较多,所以每个表的列的索引信息需要在projection,selection,selectionArgs,sortOrder这些参数里进行精确匹配

  1. 举例根据号码查询联系人名称
   String[] projection = { Phone.DISPLAY_NAME, Phone.NUMBER};
   Cursor cursor = resolver.query(Phone.CONTENT_URI, projection, Phone.NUMBER + "=?",
       new String[]{number}, null);

由于我们需要根据号码来查询,所以我们要查询的Uri是

ContactsContract.Data.CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");

然后,由于ContactsProvider2在data 后面还新建了一些Uri,比如data/phones,

matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);

并且指向了Data.DATA1DATA1 = "data1"这一列,就是data1这一列存储的是电话号码
所以我们查询的时候,Uri就可以指向

Phone.CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,"phones")

查询的结果就是data表中的data1那一列中的号码,返回的游标就是匹配了电话号码的游标,
而这个游标同时可以得到联系人显示名称,

String name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));

于是name就是对应的DISPLAY_NAME,有个要注意的地方,这里保存的号码是经过格式化的,11位号码中间有空格。

联系人的数据库构造较复杂,所以由很多Uri,不过只要找对了Uri,就可以查询到我们想要的信息。


查询短信与彩信

短信和彩信数据库路径
/data/data/com.android.providers.telephony/databases/mmssms.db

MmsProvider
SmsProvider
MmsSmsProvider
他们三个ContentProvider都是操作的同一个数据库mmssms.db

  1. Uri
短信:CONTENT_URI = Uri.parse("content://sms")
彩信:CONTENT_URI = Uri.parse("content://mms")
短信和彩信:CONTENT_URI = Uri.parse("content://mms-sms/")

他们都定义在Telephony.java

Telephony.Sms.CONTENT_URI
Telephony.Mms.CONTENT_URI
Telephony.MmsSms.CONTENT_URI
  1. 查询未读短信的语法格式
Cursor smscursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, null,
     "read = 0", null, null);

其中 "read = 0"表示未读状态
同样,彩信的也是如此,换个Uri就可以了。

查询到的cursor,可以得到发件人地址,信息内容,以及时间,唯独没有号码对应的联系人名称。

String number = cursor.getString(cursor.getColumnIndex("address"));
String content= cursor.getString(cursor.getColumnIndex("body"));
long time = cursor.getLong(cursor.getColumnIndex("date"));

同样,彩信也包含这些内容,不过更为复杂,具体的要看mms的数据库结构。


总结

经过以上分析,基本上根据contentresolver 查询 通话记录,联系人信息,短信,就都可以很快搞定了,根据Uri,再结合projection,selection,就可以查询到想要的具体数据,再根据表格的列来定位即可得到想要的内容。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Android Studio中查询联系人,您需要使用ContentResolver类和ContactsContract类。以下是一些步骤: 1. 确保您的应用程序具有读取联系人和读取电话状态的权限。您可以在AndroidManifest.xml文件中添加以下行: ``` <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> ``` 2. 在您的活动中,使用ContentResolver类查询联系人。以下是一个示例代码: ``` Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); if (cursor.moveToFirst()) { do { String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); Cursor phoneCursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]{id}, null); if (phoneCursor.moveToFirst()) { do { String phoneNumber = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); Log.d(TAG, "Name: " + name + ", Phone Number: " + phoneNumber); } while (phoneCursor.moveToNext()); } phoneCursor.close(); } while (cursor.moveToNext()); } cursor.close(); ``` 这将查询所有联系人及其电话号码,并将它们记录在日志中。 3. 如果您想将联系人信息显示在屏幕上,您可以使用ListView或RecyclerView等视图组件。您需要创建一个适配器来将联系人数据绑定到视图上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值