博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android探索之ContentProvider熟悉而又陌生的组件
阅读量:6376 次
发布时间:2019-06-23

本文共 11451 字,大约阅读时间需要 38 分钟。

前言:

    总结这篇文章之前我们先来回顾一下Android Sqlite数据库,参考文章:http://www.cnblogs.com/whoislcj/p/5506294.html,Android程序内部数据存储如果使用Sqlite数据库,那么Android 如何实现程序间数据共享?Android 提供了一种机制可以实现程序间的数据共享,它就是Android 四大组件之一ContentProvider,Android为存储和获取数据提供统一的接口,用于实现程序间数据共享,不要将其理解为数据库。

     为什么说是熟悉又陌生呢?因为我们经常使用到,Android内置的许多数据都是采用ContentProvider,比如图片,视频,音频,手机联系人等,至于陌生那是因为我很少自己去实现一个ContentProvider,今天我们重点是来实现一个自定义ContentProvider。

ContentProvider类简介:

     1.) 我们一般要继承ContentProvider,那么要实现那些函数呢?
  • ContentProvider()   构造函数
  • onCreate()    创建数据时调用的回调函数
  • insert()      插入数据
  • delete()     删除数据
  • update()    更新数据
  • query()      查询数据
  • getType()  得到数据类型
   2.)URI简介:

     ContentProvider通过URI来访问数据执行增删改查的操作,一个完整的URI有 content://自定义ContentProvider/xxx数据库名称 

     我们先声明一个作用域:

//访问URI作用域    public static final String CONTENT_URI="com.whoislcj.testsqlite.personprovider";

    对应URI举例说明一下:

  • content://com.whoislcj.testsqlite.personprovider/person   返回person所以数据
  • content://com.whoislcj.testsqlite.personprovider/person/10 返回id为10的person数据
   3.)UriMatcher简介

       主要用于匹配Uri,为什么要匹配Uri呢?通过上面的Uri举例可以看出操作域不一样,对于执行一个delete、update、query来说我们要获取参数参数执行不能对应操作。

   使用:

//定义一个UriMatcher类对象,用来匹配Uri的。    private static final UriMatcher uriMatcher;    //集合操作    public static final int INCOMING_COLLECTION = 1;    //单个ID操作    public static final int INCOMING_SIGNAL = 2;    static {        //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);        //如果match()方法匹配com.whoislcj.testsqlite.personprovider/person路径,返回匹配码为1        uriMatcher.addURI(CONTENT_URI, "person", INCOMING_COLLECTION);//添加需要匹配uri,如果匹配就会返回匹配码        //如果match()方法匹配content://com.ljq.provider.personprovider/person/230路径,返回匹配码为2        uriMatcher.addURI(CONTENT_URI, "person/#", INCOMING_SIGNAL);//#号为通配符    }
4.)ContentUris简介

      ContentUris是对URI的操作类,比如获取URI路径里的参数,或者给URI拼接一个参数

    举例说明:

  • long id = ContentUris.parseId(uri);//从uri中获取id
  • Uri rowUri = ContentUris.withAppendedId(uri, rowId);//uri追加id 生成该条数据完整的URI地址
  5.)ContentResolver简介   

       ContentResolver主要用于为外部程序提供增删改查的操作函数,也可以注册观察者来监听数据的变化。

 6.)自定义ContentProvider具体实现:
public class PersonProvider extends ContentProvider {    // DatabaseHelper操作句柄    private DBHelper dbHelper;    //访问URI    public static final String CONTENT_URI="com.whoislcj.testsqlite.personprovider";    // 数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头    public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person";    // 单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头    public static final String CONTENT_TYPE_ITME = "vnd.android.cursor.item/person";    //定义一个UriMatcher类对象,用来匹配Uri的。    private static final UriMatcher uriMatcher;    //集合操作    public static final int INCOMING_COLLECTION = 1;    //单个ID操作    public static final int INCOMING_SIGNAL = 2;    static {        //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);        //如果match()方法匹配com.whoislcj.testsqlite.personprovider/person路径,返回匹配码为1        uriMatcher.addURI(CONTENT_URI, "person", INCOMING_COLLECTION);//添加需要匹配uri,如果匹配就会返回匹配码        //如果match()方法匹配content://com.ljq.provider.personprovider/person/230路径,返回匹配码为2        uriMatcher.addURI(CONTENT_URI, "person/#", INCOMING_SIGNAL);//#号为通配符    }    public PersonProvider() {    }    /**     * 回调函数,在ContentProvider创建的时候,就会运行     * 作用获取操作用户的句柄     */    @Override    public boolean onCreate() {        //这里会调用 DBHelper的构造函数创建一个数据库;        dbHelper = new DBHelper(getContext());        return true;    }    /**     * 执行插入数据函数     *     * @param uri     * @param values     * @return     */    @Override    public Uri insert(Uri uri, ContentValues values) {        //获取一个可写的数据库        SQLiteDatabase db = dbHelper.getWritableDatabase();        //调用数据库的插入操作 也可以自己构造sql语句 执行  db.execSQL();相对比较麻烦        long rowId = db.insert(DBHelper.TABLE_NAME, "", values);        //判断是否插入成功        if (rowId > 0) {            Uri rowUri = ContentUris.withAppendedId(uri, rowId);//uri追加id 生成该条数据完整的URI地址            getContext().getContentResolver().notifyChange(uri, null);            return rowUri;        }        throw new SQLException("Failed to insert row" + uri);    }    /**     * 删除数据操作     *     * @param uri     * @param selection     * @param selectionArgs     * @return     */    @Override    public int delete(Uri uri, String selection, String[] selectionArgs) {        //获取一个可写的数据库        SQLiteDatabase db = dbHelper.getWritableDatabase();        int count = 0;        switch (uriMatcher.match(uri)) {            case INCOMING_COLLECTION:                //执行删除操作                count = db.delete(DBHelper.TABLE_NAME, selection, selectionArgs);                getContext().getContentResolver().notifyChange(uri, null);                break;            case INCOMING_SIGNAL:                long id = ContentUris.parseId(uri);//从uri中获取id                String where = "id=" + id; // 删除指定id的记录                where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : ""; // 把其它条件附加上                count = db.delete(DBHelper.TABLE_NAME, where, selectionArgs);                getContext().getContentResolver().notifyChange(uri, null);                break;            default:                throw new SQLException("Failed to delete row " + uri);        }        //关闭数据库        db.close();        return count;    }    /**     * 更新数据操作     *     * @param uri     * @param values     * @param selection     * @param selectionArgs     * @return     */    @Override    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {        //获取一个可写的数据库        SQLiteDatabase db = dbHelper.getWritableDatabase();        int count = 0;        switch (uriMatcher.match(uri)) {            case INCOMING_COLLECTION:                //执行更新数据                count = db.update(DBHelper.TABLE_NAME, values, selection, selectionArgs);                getContext().getContentResolver().notifyChange(uri, null);                break;            case INCOMING_SIGNAL:                long id = ContentUris.parseId(uri);//从uri中获取id                String where = "id=" + id;    // 删除指定id的记录                where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它条件附加上                //执行更新数据                count = db.update(DBHelper.TABLE_NAME, values, where, selectionArgs);                getContext().getContentResolver().notifyChange(uri, null);                break;            default:                throw new SQLException("Failed to update row " + uri);        }        //关闭数据库        db.close();        return count;    }    /**     * 查询操作     *     * @param uri     * @param projection     * @param selection     * @param selectionArgs     * @param sortOrder     * @return     */    @Override    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {        //获取一个可读的数据库        SQLiteDatabase db = dbHelper.getReadableDatabase();        Cursor cursor = null;        switch (uriMatcher.match(uri)) {            case INCOMING_COLLECTION:                //执行查询                cursor = db.query(DBHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);                break;            case INCOMING_SIGNAL:                long id = ContentUris.parseId(uri);//从uri中获取id                String where = "id=" + id;    // 删除指定id的记录                where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它条件附加上                cursor = db.query(DBHelper.TABLE_NAME, projection, where, selectionArgs, null, null, sortOrder);                break;            default:                throw new SQLException("Failed to query " + uri);        }        return cursor;    }    /**     * 该方法用于返回当前Url所代表数据的MIME类型。     *     * @param uri     * @return     */    @Override    public String getType(Uri uri) {        switch (uriMatcher.match(uri)) {            case INCOMING_COLLECTION:                return CONTENT_TYPE;            case INCOMING_SIGNAL:                return CONTENT_TYPE_ITME;            default:                throw new IllegalArgumentException("Unknown URI " + uri);        }    }}
View Code
7.)外部如何访问
ContentResolver resolver = getContentResolver();        Uri uri = Uri.parse("content://com.whoislcj.testsqlite.personprovider/person");        //添加一条记录        ContentValues values = new ContentValues();        values.put("name", "whoislcj");        resolver.insert(uri, values);        //更新一条数据        ContentValues updateValues = new ContentValues();        updateValues.put("name", "lcj");        //组合        resolver.update(uri, updateValues, "id=?", new String[]{"2"});        //单个        Uri updateIdUri = ContentUris.withAppendedId(uri, 5);        resolver.update(updateIdUri, updateValues, null, null);        //删除person表指定数据        Uri deleteIdUri = ContentUris.withAppendedId(uri, 5);        resolver.delete(deleteIdUri, null, null);        //获取person表指定数据        Uri tempUri = ContentUris.withAppendedId(uri, 5);        Cursor cursor = resolver.query(tempUri, null, null, null, "id asc");        while (cursor.moveToNext()) {            Log.e("testContentProvider", "signal id=" + cursor.getInt(0) + ",name=" + cursor.getString(1));        }        cursor.close();        //获取person表中所有记录        cursor = resolver.query(uri, null, null, null, "id asc");        while (cursor.moveToNext()) {            Log.e("testContentProvider", "id=" + cursor.getInt(0) + ",name=" + cursor.getString(1));        }        cursor.close();
View Code
8.)如何监听数据变化

需要注册一个自定义的观察者,当时如下

// 为uri的数据改变注册监听器        getContentResolver().registerContentObserver(                Uri.parse("content://com.whoislcj.testsqlite.personprovider/person"), true,                new Observer(new Handler()));    // 提供方自定义的ContentOberver监听器    private final class Observer extends ContentObserver {        public Observer(Handler handler) {            super(handler);        }        @Override        public void onChange(boolean selfChange, Uri uri) {            // 查询发送邮箱中的短息(处于正在发送状态的短信放在发送箱)            Log.e("MainActivity", "onChange--->uri :" + uri.toString());        }    }

同样数据操作位置也需要执行如下代码

getContext().getContentResolver().notifyChange(uri, null);
9.)访问权限控制

声明读写自定义权限

ContentProvider注册声明:

 10.)关于getTpye

        ContentProvider里面一个getType ()函数很多人不知道 这个干嘛的,接下来介绍一下,

// 数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头    public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person";    // 单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头    public static final String CONTENT_TYPE_ITME = "vnd.android.cursor.item/person";    /**     * 该方法用于返回当前Url所代表数据的MIME类型。     *     * @param uri     * @return     */    @Override    public String getType(Uri uri) {        switch (uriMatcher.match(uri)) {            case INCOMING_COLLECTION:                return CONTENT_TYPE;            case INCOMING_SIGNAL:                return CONTENT_TYPE_ITME;            default:                throw new IllegalArgumentException("Unknown URI " + uri);        }    }

假设我们在项目搞了一个联系人列表Activity,我们需要外面来访问这个Activity,首先看下这个Activity的注册声明:

看到上面的mimeType:vnd.android.cursor.dir/person

外部如何启动呢:

Uri uri = Uri.parse("content://com.whoislcj.testsqlite.personprovider/person");                Intent intent = new Intent();                intent.setAction("com.whoislcj.testsqlite.personprovider");                intent.setData(uri);                startActivity(intent);

这样以来系统会去调用你定义的ContentProvider中的getType,去匹配出相应的Activity来实现跳转。

你可能感兴趣的文章
containerd发布了CRI修复程序和CVE-2019-5736更新的runc
查看>>
77. Combinations
查看>>
WEB前端开发的思考与感悟
查看>>
实现了所有主流APP的分类切换效果,可快速接入,灵活扩展(swift)
查看>>
微信自动跳转浏览器打开APP(APK)下载链接
查看>>
==与===的区别
查看>>
机器学习实验笔记
查看>>
不同工具查看代码分支diff的差异
查看>>
一文 | 跨域及其解决方案
查看>>
[LeetCode] 671. Second Minimum Node In a Binary Tree
查看>>
深度解析国内首个云原生数据库POLARDB的“王者荣耀”
查看>>
详解vue全局组件与局部组件使用方法
查看>>
你还没有撸一个包扔到npm上?
查看>>
白话Java I/O模型
查看>>
python继承与多重继承
查看>>
数据挖掘(一):引论
查看>>
小程序开发实践总结
查看>>
在 web 上使用 JavaScript 模块
查看>>
IP正则表达式
查看>>
CMS垃圾回收和线上Full GC排查
查看>>