• Android探索之ContentProvider熟悉而又陌生的组件


    前言:

        总结这篇文章之前我们先来回顾一下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.)访问权限控制

    声明读写自定义权限

        <permission android:name="com.whoislcj.testsqlite.personprovider.read" />
        <permission android:name="com.whoislcj.testsqlite.personprovider.write" />
    
        <uses-permission android:name="com.whoislcj.testsqlite.personprovider.read" />
        <uses-permission android:name="com.whoislcj.testsqlite.personprovider.write" />

    ContentProvider注册声明:

           <provider
                android:name=".PersonProvider"
                android:authorities="com.whoislcj.testsqlite.personprovider"
                android:enabled="true"
                android:exported="true"
                android:readPermission="com.whoislcj.testsqlite.personprovider.read"
                android:writePermission="com.whoislcj.testsqlite.personprovider.write">
            </provider>

     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的注册声明:

    <activity android:name=".TestActivity" android:icon="@mipmap/ic_launcher">
                <intent-filter>
                    <action android:name="com.whoislcj.testsqlite.personprovider" />
                    <category android:name="android.intent.category.DEFAULT"/>
                    <data android:mimeType="vnd.android.cursor.dir/person" />
                </intent-filter>
    
            </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来实现跳转。

  • 相关阅读:
    在C#代码中应用Log4Net(二)典型的使用方式
    在C#代码中应用Log4Net(一)简单使用Log4Net
    Windows Azure Active Directory (2) Windows Azure AD基础
    Windows Azure Virtual Network (6) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (1)
    Windows Azure Active Directory (1) 前言
    Azure China (6) SAP 应用在华登陆 Windows Azure 公有云
    Microsoft Azure News(3) Azure新的基本实例上线 (Basic Virtual Machine)
    Microsoft Azure News(2) 在Microsoft Azure上运行SAP应用程序
    Microsoft Azure News(1) 新的数据中心Japan East, Japan West and Brazil South
    Windows Azure HandBook (2) Azure China提供的服务
  • 原文地址:https://www.cnblogs.com/whoislcj/p/5507928.html
Copyright © 2020-2023  润新知