Android基础笔记(十四)- 内容提供者读取联系人

  • 利用内容提供者读取联系人
  • 利用内容提供者插入联系人
  • 内容观察者的原理
  • 利用内容观察者监听系统应用数据库或者自己应用数据库的变化

利用内容提供者读取联系人

读取联系人相对于读取短信来说就复杂很多了,我们一步一步来吧。

先看看一下联系人的数据库,是位于什么地方!

既然很复杂,我们就一步步分析吧,我们把contacts2.db导出到电脑中,并使用SQLite数据库软件打开。你可以看到一大堆的表和视图,当然我们使用到的也只有三张。分别是raw_contactsdatamimetypes分别存储着联系人ID、联系人数据、联系人数据对应的MIMIE类型。

我们逐个看一下,首先是raw_contacts表。

在本表的contact_id列存储的是手机中联系人所对应的ID。通过上面的图可以知道,在手机中有两个联系人。

接下来看一下data表。

在本表中,我们最关心的有三列mimetype_idraw_contact_iddata1。其中mimetype_idmimetype表的主键ID;相应的raw_contact_id就是raw_contactcontact_id列了。那么data1列代表的什么呢?

你猜的不错,就是我们联系人的数据。横向来看,raw_contact_id相同的行中data1列数据组成了一个联系人的数据。

在看一下mimetypes表的情况吧。

也是比较简单,不同的mimetype内容代表的不同数据类型,比如:电话号、姓名、Email等。

看起来很麻烦的样子,貌似还需要讲data表和mimetypes表联合查询才可以。但是实际上不用这么麻烦,Google工程师已经为我们准备了view_data的视图,已经将datamimetypes两张表的数据联结起来了。我们赶紧看看里面的内容吧。

了解视图结构后,我们通过contact_id查询一下,就可以得到数据的内容和内容类型了。

分析完联系人的表,接下来就是如何使用内容解析者去查询了,跟之前一样,依旧要知道主机名路径。跟着我一起看一下源码,这次联系人应用内容提供者的路径为android4.4\packages\providers\ContactsProvider。打开清单文件,可以看到如下信息:

 <provider android:name="ContactsProvider2"
    android:authorities="contacts;com.android.contacts"
    android:label="@string/provider_label"
    android:multiprocess="false"
    android:exported="true"
    android:readPermission="android.permission.READ_CONTACTS"
    android:writePermission="android.permission.WRITE_CONTACTS">
</provider>

主机名是:com.andriod.contacts,记住别忘记添加读联系人和写联系人的权限。

再打开src\com\android\providers\contacts\ContactsProvider2.java文件,看一下路径是什么。

static {
    // Contacts URI matching table
     matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
     matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
    ... ...

     matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
     matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
     ... ...
}

可以得知,查看raw_contacts的路径就是raw_contacts,查看data表的路径就是data

该准备的东西都准备完毕了,那么就进入到代码的步骤吧。
① 在raw_contacts表中查询出来联系人的ID集合
② 以联系人ID为过滤条件,在view_data视图中,查找mimetype和data1

以下是代码:

// 在raw_contacts表中查询出来联系人的ID集合
Uri rawContactsUri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri viewDataUri = Uri.parse("content://com.android.contacts/data");

Cursor rawContactsCursor = getContentResolver().query(rawContactsUri, null, null, null, null);
if (rawContactsCursor != null) {

    while (rawContactsCursor.moveToNext()) {
        int columnIndex = rawContactsCursor.getColumnIndex("contact_id");
        String contact_id = rawContactsCursor.getString(columnIndex);
        // System.out.println("联系人ID:" + contact_id);

        // 以联系人ID为过滤条件,在view_data视图中,查找mimetype和data1

        Cursor viewDataCursor = getContentResolver().query(viewDataUri, new String[] { "mimetype", "data1" }, "contact_id=?", new String[] { contact_id }, null);

        if (viewDataCursor != null) {

            while (viewDataCursor.moveToNext()) {
                String mimetype = viewDataCursor.getString(0);
                String data1 = viewDataCursor.getString(1);

                if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
                    System.out.println("联系人ID:" + contact_id + ",电话:" + data1);
                } else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
                    System.out.println("联系人ID:" + contact_id + ",email:" + data1);
                } else if ("vnd.android.cursor.item/name".equals(mimetype)) {
                    System.out.println("联系人ID:" + contact_id + ",联系人姓名:" + data1);
                }

            }

            viewDataCursor.close();
        }

    }

    rawContactsCursor.close();
}

利用内容提供者插入联系人

插入联系人分为以下几步:
①手动插入联系人ID;ID值为数据库中总条目数加1;
②再依次向data表中插入联系人数据。

整体的代码还是比较清晰地,请看下吧:

String name = et_name.getText().toString().trim();
String phone = et_phone.getText().toString().trim();

int contact_id = 0;

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

// 手动插入联系人ID;ID值为数据库中总条目数加1;
Cursor rawContactsCursor = getContentResolver().query(rawContactsUri, null, null, null, null);
if (rawContactsCursor != null) {
    int count = rawContactsCursor.getCount();
    contact_id = count + 1;
    rawContactsCursor.close();
}
// 插入联系人ID
ContentValues values = new ContentValues();
values.put("contact_id", contact_id);
getContentResolver().insert(rawContactsUri, values);

// 再依次向`data`表中插入联系人数据。
ContentValues nameValues = new ContentValues();
nameValues.put("raw_contact_id", contact_id);
nameValues.put("mimetype", "vnd.android.cursor.item/name");
nameValues.put("data1", name);
getContentResolver().insert(viewDataUri, nameValues);

ContentValues phoneValues = new ContentValues();
phoneValues.put("raw_contact_id", contact_id);
phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2");
phoneValues.put("data1", phone);
getContentResolver().insert(viewDataUri, phoneValues);

以上都是示例代码,如果要实际使用还需要做很多很多工作。

内容观察者的原理

ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。

使用ContentObserver的情况主要有一下两者情况:
①需要频繁检测的数据库或者某个数据是否发生改变,如果使用线程去操作,很不经济而且很耗时 ;
②在用户不知晓的情况下对数据库做一些事件,比如:悄悄发送信息、拒绝接受短信黑名单等;

利用内容观察者监听系统应用数据库或者自己应用数据库的变化

这里就给出一个简单的例子吧。博主的学识还是太浅了,刚才浏览了网上一些人写的内容观察者的博客,觉得自己会的东西还是太少了。小小总结一下。当以后用到的时候再仔细的总结。

使用观察者监听系统应用的变化

监听数据库的变化,主要使用到了ContentResler类中的registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)方法。第一个参数是我们监视的Uri,第二个参数为true时会匹配所有以我们指定的Uri开头的地址,第三个参数就是我们的观察者了。

假设我们要监听系统短信数据库的变化,可以这么做:

Uri uri = Uri.parse("content://sms");
getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        System.out.println("短信内容发生改变了");
    }
});

已经知道了如何监视一个Uri的变化后,监听我们自己应用数据库的变化,只需要将Uri修改一下就可以了。但是,这里还差一步,当我们通过内容提供者去访问已有数据后,还要通知一下,这样才可以让观察者接收到内容改变的消息。

这里就使用到了ContentReslover中的notifyChange(Uri uri, ContentObserver observer)方法。

// 提供给外部应用调用的查询方法
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    int MATCH_CODE = MURI_MATCHER.match(uri);
    if (MATCH_CODE == QUERY_SUCCESS) {
        // 大喊一声,数据发生改变了
        getContext().getContentResolver().notifyChange(uri, null);
        return db.query(ACCOUNT, projection, selection, selectionArgs, null, null, sortOrder);
    }
    return null;
}

这两篇博客写的比较仓促,请多多包含了。

时间: 2024-10-10 16:28:47

Android基础笔记(十四)- 内容提供者读取联系人的相关文章

Android基础之十四数据存储 之 SQLite数据库详解

Android基础之十四数据存储 之 SQLite数据库详解 SQLite 是一款 轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百 K 的内存就足够了,因而特别适合在移动设备上使用. SQLite 不仅支持标准的 SQL 语法,还遵循了数据库的 ACID( 原子性(Atomicity) .一致性(Consistency) . 隔离性(Isolation) . 持久性(Durability))事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手 SQLite.而

【android基础篇】利用内容提供者实现短信备份

I,准备工作 系统存储短信内容的目录为:/dada/dada/com.android.providers.telephony/databases/mmssms.db,我们找到对应的数据库文件. 我们可以发现该文件对于第三方而言,是不可读不可写的,这里则必须要使用内容提供者. 问题就来了:我不知道主机名,也就无法作为中间人去找证监会打探消息.其实,完全没有关系,我们可以翻阅安卓的源代码: \packages\providers\TelephonyProvider的清单文件,因为主机名都配置在清单文

Oracle基础笔记十四

第十四章 高级子查询 1.子查询 子查询 (内查询) 在主查询执行之前执行 主查询(外查询)使用子查询的结果 SELECT select_list FROM table WHERE expr operator (SELECT select_list FROM  table); 问题:查询工资大于149号员工工资的员工的信息 SELECT last_name FROM   employees WHERE  salary > (SELECT salary FROM   employees WHERE

Android学习笔记十四.深入理解fragment(二) 之《图书详情》实战

深入理解fragment(二) 之<图书详情>实战 通过上一篇博文<深入理解fragment一>,我们学习了Android-Fragment的核心知识点.现在在此基础上,利用Fragment技术开发一款适用于大屏幕手机/平板的查找图书详情的应用软件.该项目主要在于两方面,一是Activity.Fragment的源码实现:二是,布局界面资源文件的实现. 1.res/../BookListFragment.java: 自定义类,继承于ListFragment,无需实现OnCreateV

Android基础笔记(四)

Android下ListView控件入门 Android下ListView优化 Android下ListView复杂item的显示 Android下常用的数据适配器ArrayAdapter 把数据库数据显示到ListView上 Android下ListView控件入门 简介 ListView是我们Android中最重要的控件之一,是用于对数据进行列表展示的控件. 特点 ① 屏幕上可以展示几个控件, ListView就初始化几个,节省内存,防止内存溢出. ② 通过使用convertView对创建的

【转】 Pro Android学习笔记(四十):Fragment(5):适应不同屏幕或排版

目录(?)[-] 设置横排和竖排的不同排版风格 改写代码 对于fragment,经常涉及不同屏幕尺寸和不同的排版风格.我们在基础小例子上做一下改动,在横排的时候,仍是现实左右两个fragment,在竖排时,如下图显示: 屏幕上只显示一个fragment,点击列表上的数目,进入到简介的activity.下面介绍实现的方式. 设置横排和竖排的不同排版风格 在 Pro Android学习笔记(四):了解Android资源(下)的“资源和配置的变更”中,我们介绍了如何同资源文件夹名设置不同资源.缺省的l

Android学习笔记(四七):Content Provider初谈和Android联系人信息

Content Provider 在数据处理中,Android通常使用Content Provider的方式.Content Provider使用Uri实例作为句柄的数据封装的,很方便地访问地进行数据的增.删.改.查的操作.Android并不提供所有应用共享的数据存储,采用content Provider,提供简单便捷的接口来保持和获取数据,也可以实现跨应用的数据访问.简单地说,Android通过content Provider从数据的封装中获取信息. Content provider使用Uri

十九、android中判断sim卡状态和读取联系人资料的方法

在写程序中,有时候可能需要获取sim卡中的一些联系人资料.在获取sim卡联系人前,我们一般会先判断sim卡状态,找到sim卡后再获取它的资料,如下代码我们可以读取sim卡中的联系人的一些信息. PhoneTest.java package com.android.test; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.datab

swift 笔记 (十四) —— 构造过程

构造过程 为了生成类.结构体.枚举等的实例,而做的准备过程,叫做构造过程. 为了这个过程,我们通常会定义一个方法来完成,这个方法叫做构造器.当然它的逆过程,叫做析构器,用于在实例被释放前做一些清理工作以及一此自定义化的处理. 为存储型属性设置初始值 类和结构体在生成实例那一刻,必须为所有的属性赋以特定的初始值. 要么在定义存储型属性的时候直接给个初始值,否则就必须在构造器里面指定一个初始值. 上面说的这两种情况,都不会触发存储型属性的监听者行为(property observer). struc