Android四大组件之ContentProvider

要解决的问题:

有两个工程A和工程B,工程A中通过SQLiteOpenHelper的子类创建了一个自产自销的数据库,工程B中没有数据库,但是工程B想要与工程A共用同一个数据库

为实现以上目标,需要2个系统类:

  1. ContentProvider 内容提供者 Andord四大组件之一
  2. ContentResolver 内容观察者
    两者结合起来可以进程间(多个工程之间)数据的共享

使用方式:

工程A(要向其他工程开放数据的):

  1. 先准备一个SQLiteOpenHelper的子类,用于管理当前工程自产自销的数据库
  2. 为了把自产自销的数据库开放出去,需要通过ContentProvider进行以下操作:
    a. 创建一个ContentProvider的子类
    b. 到清单文件中注册ContentProvider的子类
 <!-- 在application标签中注册ContentProvider子类的信息 
        android:name  用于指定子类的具体路径
        android:authorities  设置当前子类对应的作者名,属性值写任意字符串即可
        用于稍后再其他工程中通过ContentResolver连接此类时,设置的Uri路径中的authorities
        android:exported="true" 允许进行数据开放操作
        -->
        <provider 
            android:name="com.example.day14_5_contentprovider.MyProvider"
            android:authorities="com.ay.sql"
            android:exported="true"
            ></provider>

c. 在子类的增删改查方法中填写相应代码

MyProvider子类:

public class MyProvider ext ends ContentProvider {
    //初始化用于比较Uri路径类型的UriMatcher对象
    private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int VIP = 1;
    private static final int VIP_ID = 3;
    private static final int VIP_LIKE = 4;
    private static final int BLACK = 2;
    static {
        //添加比对参照物
        /**
         * 代表向matcher对象中存入参照物: content://com.ay.oye/vip  ,
         * 后期当通过matcher调用match方法进行比对时,一旦与当前参照物相同,match方法的返回值就是VIP对应的数据1
         */
        matcher.addURI("com.ay.oye", "vip", VIP);
        matcher.addURI("com.ay.oye", "black", BLACK);
        matcher.addURI("com.ay.oye", "vip/#", VIP_ID);  //#代表任意数字
        matcher.addURI("com.ay.oye", "vip/*", VIP_LIKE);  //* 代表任意字符

    }

    private SQLiteDatabase db;

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        db = new MyHelper(getContext()).getReadableDatabase();
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
        Cursor cursor = null;
        switch (matcher.match(uri)) {
        case VIP:
            //查询的是vip表
            //通过db对象调用系统封装好数据库中用于实现查询的query方法
            cursor = db.query("vip", projection, selection, selectionArgs, null, null, sortOrder);
            break;

        case BLACK:
            //查询的是black表
            cursor = db.query("black", projection, selection, selectionArgs, null, null, sortOrder);
            break;
        case VIP_ID:
            if (selection == null) {
                cursor = db.query("vip", projection, "_id = "+uri.getLastPathSegment(), null, null, null, sortOrder);
            } else {
                cursor = db.query("vip", projection, selection+"and _id = "+uri.getLastPathSegment(), selectionArgs, null, null, sortOrder);
            }

            break;
        case VIP_LIKE:
            if (selection == null) {
                cursor = db.query("vip", projection, "name like ?", new String[] {"%"+uri.getLastPathSegment()+"%"}, null, null, sortOrder);
            } else {
                cursor = db.query("vip", projection, selection + " and name like "+"'"+"%"+uri.getLastPathSegment()+"%"+"'", selectionArgs, null, null, sortOrder);
            }

            break;
        }
        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return matcher.match(uri)+"";
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        long num = 0;
        switch (matcher.match(uri)) {
        case VIP:
            num = db.insert("vip", "_id", values);
            break;

        case BLACK:
            num = db.insert("black", "_id", values);
            break;
        }
        return Uri.withAppendedPath(uri, num+"");
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        switch (matcher.match(uri)) {
        case VIP:
            db.delete("vip", selection, selectionArgs);
            break;

        case BLACK:
            db.delete("black", selection, selectionArgs);
            break;
        case VIP_ID:
            if (selection == null) {
                db.delete("vip", "_id = "+uri.getLastPathSegment(), null);
            } else {
                db.delete("vip", selection+" and _id = "+uri.getLastPathSegment(), selectionArgs);
            }
            break;

        case VIP_LIKE:

            break;
        }
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

}

工程B中(从工程A获取数据的):

1. 初始化ContentResolver对象

  //初始化ContentResolver对象
        cr = getContentResolver();

2. 通过ContentResolver对象调用增删改查方法实现数据库操作
如:

 public void click (View v) {
        switch (v.getId()) {
        case R.id.but_add:
            /**
             * 先以增加为例
             * 参数: 
             * 1. Uri  用于描述路径
             * 拼接特点:
             * scheme:// authorities / data
             * scheme  主题 ,代表当前路径的具体类型,即是一个文件路径还是一个电话等
             * 
             * 2.ContentValues ,用于存储要添加的数据
             */
            ContentValues values = new ContentValues();
            values.put("name", "花花");
            values.put("age", 28);

            /**
             *  执行流程: 根据Uri路径中的作者名在整个设备的所有应用中寻找,哪个ContentProvider的子类注册了 相同的作者名
             *  找到后运行该子类中的insert方法
             */
            Uri uri = cr.insert(Uri.parse("content://com.ay.sql/pr"), values);

           //想要从insert方法的返回的uri对象中获取到新添加的数据对应的id值的话
           String id =  uri.getLastPathSegment();
           Log.i("oye", "工程6 新添加数据对应的id为: :  "+id);
            break;

        case R.id.but_query:
            /**
             * 参数:
             * 1. Uri路径 ,
             * 2. 要查询表中的哪些列,填null代表获取所有列
             * 3, 查询的条件
             * 3, 用于替换条件中?的值
             * 5. 排序
             */
            Cursor cursor = cr.query(Uri.parse("content://com.ay.sql/pr"), null, null, null, null);

             while (cursor.moveToNext()) {
                 String name = cursor.getString(cursor.getColumnIndex("name"));

                 Log.i("oye", "工程6中获取到的数据位:  "+name);
             }
            break;

        case R.id.but_delete:

            int num = cr.delete(Uri.parse("content://com.ay.sql/pr"), "_id=?", new String[]{"3"});
            Log.i("oye", "工程6中删除的条数为:"+num);
            break;

        case R.id.but_update:

            ContentValues cv = new ContentValues();
            cv.put("name", "王老六");
            int num1 = cr.update(Uri.parse("content://com.ay.sql/pr"), cv, "_id = ?", new String[]{"5"});
            Log.i("oye", "工程6中删除的条数为:"+num1);
            break;
        }
    }

相对来说常见的使用如 :

系统的通话记录工程通过ContentProvider将通话记录开放了出来,我们想要获取通过记录信息,只需通过ContentResolver调用相应的增删改查方法即可

通过ContentResolver读取常见的系统数据:

1.  通话记录信息
2.  短信记录信息
3.  联系人通讯录信息

一、通过ContentResolver读取通话记录:

读取前的准备: 确保用于测试的设备上有通话记录,否则读取不出内容

如果是模拟器处理通话记录的方式:

1.拨出电话: 直接使用模拟器上的拨号软件即可
2.接听电话 (只有原生模拟器可以模拟接听电话):选择打开Window—ShowVIew --- Other—Android中的Emulator Control标签,在此标签中可以实现向模拟器中拨出电话,发出短信的功能

系统通话记录表的存储位置:

 /*
         * 通话记录的数据库的位置:data/data/com.android.providers.contacts/databases/contacts2.db
         * 表名为:calls
         * 对应的作者名:call_log
         * 通过query方法读取内容
         */

通话记录表中的主要列:

number   存储电话号
type     存储通话类型,1 代表来电(接听的电话) 2 代表去电(拨出的电话)
date     通话的日期
duration  通话的持续时长

连接通过记录使用的Uri:

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

具体的读取方式:

Cursor cursor = cr.query(Uri.parse("content://call_log/calls"), null, null, null, null);

        while(cursor.moveToNext()) {
            //电话号码
            String number = cursor.getString(cursor.getColumnIndex("number"));
            //是来电还是去电
            String s = cursor.getString(cursor.getColumnIndex("type"));

            String type = s.equals("1") ? "来电(接听的电话)":"去电(拨出的电话)";
            //时间
            String date = cursor.getString(cursor.getColumnIndex("date"));

            //获取通话时长
            String duration = cursor.getString(cursor.getColumnIndex("duration"));

            Log.i("oye", "通过话记录中的数据位:  "+number +"  "+date+"  "+type+"   "+duration);

        }

需要添加的权限:

 <!-- 添加读取通话记录的权限 -->
    <uses-permission android:name="android.permission.READ_CALL_LOG"/>

     <!-- 添加修改写入通话记录的权限 -->
    <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>

二、通过ContentResolver读取短信记录:

系统短信记录表的存储位置:

data/data/com.android.provider.telephony/databases/mmssms.db中的sms表

连接使用的Uri:

content://sms

表中比较重要的列:

address   电话号码
date   接收短信的日期
date_sent  发送短信的日期
read  用于标识短信是否已读, 0代表未读,1代表已读
type 用于标识短信类型  1 接收的短信,2发送的短信 3 发送失败,在草稿箱的短信
body  短信内容

读取方式:

protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        ContentResolver cr = getContentResolver();

        Cursor cursor = cr.query(Uri.parse("content://sms"), null, null, null, null);

        while (cursor.moveToNext()) {
            String number = cursor.getString(cursor.getColumnIndex("address"));
            String mess = cursor.getString(cursor.getColumnIndex("body"));
            String read = cursor.getString(cursor.getColumnIndex("read")).equals("0") ? "未读":"已读";
            String type = cursor.getString(cursor.getColumnIndex("type")).equals("1")?"接收的短信":"发送的短信";

            Log.i("oye", "获取到的短信内容为: "+number+"  "+mess+"  "+read+"  "+type);
        }
    }

需要的权限:

<!-- 读取短信记录的权限 -->
    <uses-permission android:name="android.permission.READ_SMS"/>

    <!-- 修改短信记录的权限 -->
     <uses-permission android:name="android.permission.WRITE_SMS"/>

三、通过ContentResolver读取系统通讯录联系人

系统通讯录联系人表的存储位置:

data/data/com.android.providers.contacts/databases/Contacts2.db的数据库文件中

读取联系人信息的主要表:

1.raw_contacts
用于存储联系人的名字以及该联系人对应的唯一id值
2.data
用于存储所有联系人的所有数据,但是注意:
(1)一行中并没有存储一个联系人的所有信息
而是将一个联系人的所有信息拆分成了多行存储
判断哪几行数据是属于同一个联系人的方式为:
根据每一行的raw_contact_id的值判断,值相同的则为同一联系人的所有数据
(2)基本上大部分的数据都是存储在data1列中,为了判断本行data1中的数据属于哪种类型(是电话号码还是姓名),需要根据mimetype_id这一列中的值进行判断
3.Mimetypes
用于存储所有的数据类名,如:电话号,姓名,邮箱地址等

读取方式:

读取方式一:

private ArrayList<MyC> queryAll() {
        ArrayList<MyC> list = new ArrayList<MyC>();

        // 1. 读取raw_contacts这张表,目的:获取每一个人联系人的唯一id标识,本次得到的idCursor中存储了所有联系人id值
        Cursor idCursor = cr.query(
                Uri.parse("content://com.android.contacts/raw_contacts"), null,
                "deleted = 0", null, null);

        while (idCursor.moveToNext()) {
            // 获取联系人的id
            int id = idCursor.getInt(idCursor.getColumnIndex("_id"));

            MyC myc = new MyC();
            myc.setId(id);
            // 2. 根据或得到的id值到data表中读取数据,本次得到的cursor对象中存储了一个联系人的多方面数据
            Cursor cursor = cr.query(
                    Uri.parse("content://com.android.contacts/data"), null,
                    "raw_contact_id = " + id, null, null);

            // 3. 循环cursor对象,读取联系人数据
            while (cursor.moveToNext()) {
                // 获取data1这一列的值
                String data1 = cursor.getString(cursor.getColumnIndex("data1"));

                Log.i("oye", "当前行中data1 的数据为:  " + data1);

                String mime = cursor.getString(cursor
                        .getColumnIndex("mimetype"));

                if (mime.equals("vnd.android.cursor.item/name")) {
                    // 代表当前行的data1列中存储的是姓名
                    myc.setName(data1);
                } else if (mime.equals("vnd.android.cursor.item/email_v2")) {
                    // 代表本行的data1中存储的是邮箱地址
                    myc.setEmail(data1);
                } else if (mime
                        .equals("vnd.android.cursor.item/postal-address_v2")) {
                    // 家庭住址
                    myc.setAddress(data1);
                } else if (mime.equals("vnd.android.cursor.item/phone_v2")) {
                    // 存储的是电话号
                    String data2 = cursor.getString(cursor
                            .getColumnIndex("data2"));
                    if (data2.equals("1")) {
                        // 家庭座机号
                        myc.setPhone_home(data1);
                    } else if (data2.equals("2")) {
                        // 手机号
                        myc.setPhone_mobile(data1);
                    }

                }

            }
            list.add(myc);
            Log.i("oye", "*************************************");
        }

        return list;
    }

读取方式二:

public void queryAllMethod2() {
        // 管理所有联系人的Uri
        Cursor c1 = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null,
                null, null);
        //读取所有联系人的名字
        while (c1.moveToNext()) {

            String name = c1.getString(c1
                    .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
            Log.i("oye", "c1   ddd  " + c1.toString() + "  " + name);
        }

        Cursor c2 = cr.query(
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null,
                null, null);
        //读取所有的联系人的电话号
        while (c2.moveToNext()) {

            String number = c2.getString(c2
                    .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
            Log.i("oye", "c1   ddd  " + c2.toString() + "  " + number);
        }

    }

需要添加的权限:

  <!-- 读取通讯录联系人的权限 -->
    <uses-permission android:name="android.permission.READ_CONTACTS"/>

    <!-- 修改,添加通讯录联系人的权限 -->
     <uses-permission android:name="android.permission.WRITE_CONTACTS"/>

删除数据的方式:

case R.id.but_delete:
            //删除通讯录中联系人
            //1. 从raw_contacts表中删除数据
            cr.delete(Uri.parse("content://com.android.contacts/raw_contacts"), "_id=2", null);
            //2. (可不写)再到data表中删除联系人
            cr.delete(Uri.parse("content://com.android.contacts/data"), "raw_contact_id = 2", null);
            break;

新增数据的方式:

case R.id.but_insert:
            //新增联系人数据
            //1.先向raw_contacts表中存入数据,并获取新添加的数据对应的id值
            ContentValues values = new ContentValues();
            values.put("display_name", "小赵");
            Uri uri = cr.insert(Uri.parse("content://com.android.contacts/raw_contacts"), values);
            //获取新添加的数据对应的id值
            String id = uri.getLastPathSegment();

            //2. 根据id向data表中分别存入,姓名,邮箱,电话号的信息
            //存入姓名信息
            values.clear();
            values.put("data1", "小赵");
            values.put("raw_contact_id", id);
            values.put("mimetype", "vnd.android.cursor.item/name");
            cr.insert(Uri.parse("content://com.android.contacts/data"), values);
            //存入邮箱信息
            values.clear();
            values.put("data1", "250@250.com");
            values.put("raw_contact_id", id);
            values.put("mimetype", "vnd.android.cursor.item/email_v2");
            values.put("data2", "1");
            cr.insert(Uri.parse("content://com.android.contacts/data"), values);
            //存入电话号码信息
            values.clear();
            values.put("data1", "250250250");
            values.put("raw_contact_id", id);
            values.put("mimetype", "vnd.android.cursor.item/phone_v2");
            values.put("data2", "2");
            cr.insert(Uri.parse("content://com.android.contacts/data"), values);

            break;

修改数据的方式:

case R.id.but_update:
            //修改联系人

            //修改数据库中的原有内容
            ContentValues values2 = new ContentValues();
            values2.put("data1", "252252252");
            cr.update(Uri.parse("content://com.android.contacts/data"), values2, "raw_contact_id = ? and mimetype = ?", new String[]{"3","vnd.android.cursor.item/phone_v2"});


            //向数据库中添加新内容
            ContentValues values1 = new ContentValues();
            values1.put("data1", "家庭住址:");
            values1.put("raw_contact_id", 3);
            values1.put("mimetype", "vnd.android.cursor.item/postal-address_v2");
            cr.insert(Uri.parse("content://com.android.contacts/data"), values1);

            break;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值