Android四大组件之ContentProvider详解

1. 为什么需要内容提供者contentProvider?

为不同的应用之间数据共享提供统一的访问接口,内容提供者的作用 把私有的数据给暴露出来

2. 内容提供者原理?

原理:可以把ContentProvider当成Android系统内部的网站,这个网站以固定的Uri对外提供服务,而ContentResolver则可以当成android系统内部的HttpClient,它可以向指定的Uri发送请求(实际上是调用ContnetResolver的方法),这种请求委托给ContnentProvider处理,从而实现对“网站”内部数据的进行,访问,每一个ContentProvider都拥有一个公共的URI,这个URL用于表示这个ContentProvider所提供的数据

URL : 统一资源定位符 https : //www.baidu.com/
URI : 统一资源标示符 可以自己定义 表示的范围更大些

I / ActivityThread(2895) : Pub com.itheima.db : com.itheima.db.AccountProvider 如果你发现这样一段日志 证明你写的内容提供者没有问题

3. 内容提供者实现的步骤?

  • 3.1 定义内容提供者
  • 3.2 定义urimatcher
  • private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  • 3.3 写一个静态代码块 添加匹配规则
static {
    sURIMatcher.addURI("com.itheima.db", "query", QUERYSUCESS);
}
  • 3.4 一定要要记得在清单文件里面配置
  • 3.5 暴露你想暴露的方法
 public class AccountProvider extends ContentProvider {
        private static final int QUERYSUCESS = 0; //ctrl + shift + X   表小写加Y
        private static final int INSERTSUCESS = 1;
        private static final int UPDATESUCESS = 2;
        private static final int DELETESUCESS = 3;
        //(1)定义urimatcher 路径的匹配器
        private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        private MyOpenHelper myOpenHelper;
        //(2)写一个静态代码块  添加匹配规则
        static {
        /**
         * authority 要和你在清单文件里面配置一样
         * 比如你访问百度  http://www.baidu.com
         *                   com.itheima.db/query
         *
         */
        sURIMatcher.addURI("com.itheima.db", "query", QUERYSUCESS);
        sURIMatcher.addURI("com.itheima.db", "insert", INSERTSUCESS);
        sURIMatcher.addURI("com.itheima.db", "update", UPDATESUCESS);
        sURIMatcher.addURI("com.itheima.db", "delete", DELETESUCESS);
        }
     @ Override
    public boolean onCreate() {
        //获取类的实例
        myOpenHelper = new MyOpenHelper(getContext());
        return false;
    }
     @ Override
    public Cursor query(Uri uri, String[]projection, String selection,
        String[]selectionArgs, String sortOrder) {
        int code = sURIMatcher.match(uri);
        if (code == QUERYSUCESS) {
            //匹配成功   把curosr返回
            SQLiteDatabase db = myOpenHelper.getReadableDatabase();
            Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
            //我就发条消息  说 数据库被操作了
            getContext().getContentResolver().notifyChange(uri, null);
            return cursor;
        } else {
            //路径匹配失败
            throw new IllegalArgumentException("哥们路径不正确 请检测路径...");
        }
    }
     @ Override
    public String getType(Uri uri) {
        return null;
    }
     @ Override
    public Uri insert(Uri uri, ContentValues values) {
        int code = sURIMatcher.match(uri);
        if (code == INSERTSUCESS) {
            //说明路径匹配成功
            SQLiteDatabase db = myOpenHelper.getReadableDatabase();
            //代表插入到了第几行
            long insert = db.insert("info", null, values);
            //为了业务严谨一些
            if (insert > 0) {
                //发送一条通知  说明数据库被操作了
                getContext().getContentResolver().notifyChange(uri, null);
            }
            Uri uri2 = Uri.parse("com.itheima.com" + insert);
            db.close();
            return uri2;
        } else {
            //路径匹配失败
            //路径匹配失败
            throw new IllegalArgumentException("姐们路径不正确 请检测路径...");
        }
    }
     @ Override
    public int delete (Uri uri, String selection, String[]selectionArgs) {
        int code = sURIMatcher.match(uri);
        if (code == DELETESUCESS) {
            //说明路径匹配成功
            SQLiteDatabase db = myOpenHelper.getReadableDatabase();
            //代表影响的行数
            int delete  = db.delete ("info", selection, selectionArgs);
            //发送一条通知 说明数据库内容发生了改变
            if (delete  > 0) {
                getContext().getContentResolver().notifyChange(uri, null);
            }
            db.close();
            return delete;
        } else {
            //路径匹配失败
            throw new IllegalArgumentException("姐们路径不正确 请检测路径...");
        }
    }
     @ Override
    public int update(Uri uri, ContentValues values, String selection,
        String[]selectionArgs) {
        int code = sURIMatcher.match(uri);
        if (code == UPDATESUCESS) {
            //路径匹配成功
            SQLiteDatabase db = myOpenHelper.getWritableDatabase();
            //代表更新的行数
            int update = db.update("info", values, selection, selectionArgs);
            if (update > 0) {
                //发送一条通知 说明数据库内容发生了改变
                getContext().getContentResolver().notifyChange(uri, null);
            }
            db.close();
            return update;
        } else {
            //路径匹配失败
            throw new IllegalArgumentException("姐们路径不正确 请检测路径...");
        }
    }
}
  • 3.6  通过内容提供者实现增删改查
public class MainActivity extends Activity {
     @ Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    //点击按钮 增加一条记录
    public void click1(View v) {
        Uri uri = Uri.parse("content://com.itheima.db/insert"); //小细节
        ContentValues values = new ContentValues();
        //key :代表列的名字  value:对于的值
        values.put("name", "zhaoliu");
        values.put("money", "2000000");
        Uri uri2 = getContentResolver().insert(uri, values);
        System.out.println("uri2:" + uri2);
    }
    //点击按钮删除一条记录
    public void click2(View v) {
        Uri uri = Uri.parse("content://com.itheima.db/delete");
        int delete  = getContentResolver().delete (uri, "name=?", new String[]{
                "zhaoliu"
            });
        Toast.makeText(getApplicationContext(), "删除了" + delete  + "行", 1).show();
    }
    //点击按钮 进行修改
    public void click3(View v) {
        Uri uri = Uri.parse("content://com.itheima.db/update");
        ContentValues values = new ContentValues();
        //更新钱数
        values.put("money", "2");
        int update = getContentResolver().update(uri, values, "name=?", new String[]{
                "zhaoliu"
            });
        Toast.makeText(getApplicationContext(), "更新了" + update + "行", 1).show();
    }
    //点击按钮 去查询第一个应用里面的数据库的内容
    public void click4(View v) {
        /**
         * path  数据库路径
         * factory 游标工厂
         */
        /*SQLiteDatabase db = SQLiteDatabase.openDatabase("/data/data/com.itheima.db/databases/Account.db", null, SQLiteDatabase.OPEN_READWRITE);
        Cursor cursor = db.query("info", null, null, null, null, null, null);
        if (cursor!=null && cursor.getCount()>0) {
        while(cursor.moveToNext()){
        String name = cursor.getString(1);
        String money = cursor.getString(2);
        System.out.println("第二个应用读取name:"+name+"---"+money);
        }
        }*/
        //(2)第二种查询方法 因为数据库的内容已经通过内容提供者给暴露出来了  所以我们通过内容的解析者去获取
        Uri uri = Uri.parse("content://com.itheima.db/query"); //小细节
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        if (cursor != null && cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                String name = cursor.getString(1);
                String money = cursor.getString(2);
                System.out.println("第二个应用读取name:" + name + "---" + money);
            }
            cursor.close();
        }
    }
}

4. 备份短信案例

5. 插入短信案例

public void click(View v) {
//点击按钮 把一条短信插入到短信数据库
    //(1)由于短信的数据库已经通过内容提供者暴露出来了 所以我们可以直接通过内容解析者去操作这个数据库
    //(2)直接获取内容解析者 直接通过上下文
    Uri uri = Uri.parse("content://sms/");
    ContentValues values = new ContentValues();
    //address date  body一定要和短信数据库表的字段对应
    values.put("address", "173815");
    values.put("date", System.currentTimeMillis());
    values.put("body", "请您马上来一趟 否则后果自负");
    //插入一条记录
    getContentResolver().insert(uri, values);
}

6. 读取联系人案例

QQ 微信 陌陌都经常去读取我们手机的联系人
联系人的数据库有三张重要的表

  • a. data表 data1列存的是所有联系人的信息
  • b. rowcontacts表 contact_id列 用来表示一共有几条联系人 contact_id就是data表的raw_contact_id
  • c. mimetype表的mimetype字段定义了数据的类型

查询联系人的步骤

  • 6.1 先查询rowcontacts表的 contact_id字段 我就可以知道一共有几条联系人
  • 6.2 查询data表 根据raw_contact_id字段去查询 data1列 和 mimetype列
  • 6.3 查询data表 实际上查询的是view_data的视图
public class ContactUtils {
    // 读取联系人的业务方法
    public static List < Contact > readContact(Context context) {
        //0创建一个集合用来封装数据
        List < Contact > contactLists = new ArrayList < Contact > ();
        // 由于联系人的数据库也通过内容提供者把数据暴露出来了 所以我们可以直接通过内容解析者去操作
        // (1)先查询rowcontacts表的 contact_id字段 我就可以知道一共有几条联系人
        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
        Uri dataUri = Uri.parse("content://com.android.contacts/data");
        Cursor cursor = context.getContentResolver().query(uri,
                new String[]{
                "contact_id"
            }, null, null, null);
        while (cursor.moveToNext()) {
            String contact_id = cursor.getString(0); // 拿到contact_id列的内容
            System.out.println("contact_id:" + contact_id);
            if (contact_id != null) {
                //创建Javabean对象
                Contact contact = new Contact();
                contact.setId(contact_id);
                // (2)根据raw_coatct_id 去查询data表 data1 和 mimetypeid
                // 2.1 小细节 实际上我们查收data表的时候 查询的不是data表 查询的是view_data的视图 视图就是将多张表进行组合
                // view_data这个视图是将data表和mimetyp表进行组合
                Cursor dataCursor = context.getContentResolver().query(dataUri,
                        new String[]{
                        "data1",
                        "mimetype"
                    }, "raw_contact_id=?",
                        new String[]{
                        contact_id
                    }, null);
                while (dataCursor.moveToNext()) {
                    String data1 = dataCursor.getString(0);
                    String mimetype = dataCursor.getString(1);
                    System.out.println("data1:" + data1 + "----" + mimetype);
                    // (3)区分一下data1列 具体的数据类型
                    if ("vnd.android.cursor.item/name".equals(mimetype)) {
                        System.out.println("姓名:" + data1);
                        contact.setName(data1);
                    } else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
                        System.out.println("邮箱:" + data1);
                        contact.setEmail(data1);
                    } else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
                        contact.setPhone(data1);
                        System.out.println("电话号码:" + data1);
                    }
                }
                //把Javabean对象加入到集合中
                contactLists.add(contact);
            }
        }
        return contactLists;
    }
}

7. 插入联系人案例

  • 7.1 先往rowcontacts表插入数据
  • 7.2 在往data表里面插入数据
public class MainActivity extends Activity {
    private EditText et_name;
    private EditText et_phone;
    private EditText et_email;
     @ Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取我们关心控件
        et_name = (EditText)findViewById(R.id.et_name);
        et_phone = (EditText)findViewById(R.id.et_phone);
        et_email = (EditText)findViewById(R.id.et_email);
    }
    //点击按钮 把数据插入到联系人数据库
    public void click(View v) {
        //获取我们关心的数据
        String name = et_name.getText().toString().trim();
        String phone = et_phone.getText().toString().trim();
        String email = et_email.getText().toString().trim();
        //TODO 自己判断是是否为空
        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
        Uri dataUri = Uri.parse("content://com.android.contacts/data");
        //[0]插入之前先查询一下 一共有多少条记录  (行)
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        int count = cursor.getCount();
        int contact_id = count + 1;
        //[1]先往rowcontacts 表 插入 一条数据  更新raw_contact_id字段
        ContentValues values = new ContentValues();
        values.put("contact_id", contact_id);
        getContentResolver().insert(uri, values);
        //[2]把name 姓名插入到data表
        ContentValues nameValues = new ContentValues();
        nameValues.put("data1", name); //把数据插入到data1列
        nameValues.put("raw_contact_id", contact_id); //新插入的数据属于第几条联系人的
        nameValues.put("mimetype", "vnd.android.cursor.item/name"); //告诉数据库插入的name是属于什么数据类型
        getContentResolver().insert(dataUri, nameValues);
        //[3]把phone 插入到data表中
        ContentValues phoneValues = new ContentValues();
        phoneValues.put("data1", phone); //把数据插入到data1列
        phoneValues.put("raw_contact_id", contact_id); //新插入的数据属于第几条联系人的
        phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2"); //告诉数据库插入的name是属于什么数据类型
        getContentResolver().insert(dataUri, phoneValues);
        //[4]把phone 插入到data表中
        ContentValues emailValues = new ContentValues();
        emailValues.put("data1", email); //把数据插入到data1列
        emailValues.put("raw_contact_id", contact_id); //新插入的数据属于第几条联系人的
        emailValues.put("mimetype", "vnd.android.cursor.item/email_v2"); //告诉数据库插入的name是属于什么数据类型
        getContentResolver().insert(dataUri, emailValues);
    }
}

8. 内容观察者原理

contentObserver内容观察者 他不是四大组件 不需要在清单文件里面配置
contentProvider内容提供者 是四大组件 需要在清单文件里面配置

9. 内容观察者观察短信的数据库

  • 9.1 在oncreate方法里面注册内容观察者
getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, new MyContentObserver(new Handler()));
  • 9.2 定义内容观察者
//定义一个内容的观察者
private class MyContentObserver extends ContentObserver {
    public MyContentObserver(Handler handler) {
        super(handler);
    }
    //当我们关心的数据发生改变的时候调用
     @ Override
    public void onChange(boolean selfChange) {
        System.out.println("哈哈 数据库的内容发生了改变");
        super.onChange(selfChange);
    }
}

 

你可能感兴趣的:(android基础)