资讯专栏INFORMATION COLUMN

Content Provider 和 Content Resolver

Euphoria / 1362人阅读

摘要:在文件中对进行注册。使用这些方法来访问底层数据,无需知道底层的数据结构和实现。对象应用于完善事务处理和查询请求,而是执行基于行查询的便利辅助类。监听中的数据变化如果共享的数据发生变化,可以在发生数据变化时调用来通知注册在此上的访问者

Content Provider

提供了统一对外的数据访问接口,可以把自己应用的数据提供出去给的应用程序使用,可以对你的数据进行增删改查,例如 通讯录就是其中的一种。

要想创建一个 Content Provider 需要进行下面几部操作:

继承 ContentProvider 类,重写 onCreate、getType、insert、update、query、delete 方法。

在 AndroidManifest.xml 文件中对 Provider 进行注册。

发布 Content Provider 的 URI 地址,每个Content Provider 都应该使用一个公有的静态 CONTENT_URI 属性来公开它的授权
Public static final Uri CONTENT_URI = Uri.parse("content://com.exmaple.provider.applicationname/elements")

通过 Android 中的 Uri 访问 Content Provider,Uri主要包含下面几部分:
content://com.example.earthquake/contentprovider/quakes/1

scheme:Content Provider 的 scheme 是 content://

content:是 Content Provider 的唯一标示,外部程序可以根据这个标示找到对应的 Content Provider

path:标示我们要操作的数据


我们一般提供全部查询和指定条数两种支持查询模式:其中一个非常有用的类 UriMatcher,它是一个非常有用的类,可以分析 URI 并确定它的形势。

// 创建两个常量来区分不同的 URI 请求
private static final int ALLROWS = 1;
private static final int SINGLE_ROW = 2;

private static final UriMatcher uriMatcher;

// 填充 UriMatcher 对象,其中 element 结尾的 URI 对应请求所有的数据
// 以 elements/rowid 结尾的 URI 代表请求单行数据
static {
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    uriMatcher.addURI("com.exmaple.message.provider", "elements", ALLROWS);
    uriMatcher.addURI("com.exmaple.message.provider", "elements/#", SINGLE_ROW);
}

在同一个 Content Provider 中,可以使用同样的技术来公开其他的 URI,这些 URI 代表了不同的数据子集或数据库中不同的表。

区分了全表和单行查询后,就可以很容易的使用 SQLiteQueryBuilder 类对一个查询应用额外的选择条件,如下面的代码所示:

SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();

// 如果是行查询,用传入的行限制结果集
switch (uriMatcher.match(uri)) {
case SINGLE_ROW:
    String rowID = uri.getPathSegments().get(1);
    sqb.appendWhere(KEY_ID + "=" + rowID);
default:
    break;
}

常用帮助方法:

UriMatcher:用于匹配对应的 Uri 路径
ContentUris:用于操作 Uri 路径后面的 id 部分

withAppendId 通过该方法把 主 Uri 和 对应的 id 传入进去,组装返回对应的 uri 对象

  

当应用程序启动时,每个 Content Provider 的 onCreate 处理程序会再应用程序的主线程中被调用。
和之前的数据库操作一样,最好使用 SQLiteOpenHelper 来延迟打开(必要的地方创建)底层的数据库,直到 Content Provider 的查询或事务方法需要时再打开或创建它。

@Override
public boolean onCreate() {
    // 构建一个底层数据库
    // 延迟打开数据库,直到需要执行
    // 一个查询或者事务时再打开
    sqLiteHelper = new MessageSQLiteHelper(getContext(),
        MessageSQLiteHelper.DATABASE_NAME, null,
        MessageSQLiteHelper.DATABASE_VERSION);

    return true;
}


实现 Content Provider 查询

要想使用 Content Provider 就必须实现 query 和 getType 方法。Content Provider 使用这些方法来访问底层数据,无需知道底层的数据结构和实现。
Content Provider 的最常见场景就是访问一个 SQLite 数据库,但在这些方法中,你可以访问任何的数据库(包括文件或应用程序实例变量)。
UriMatcher 对象应用于完善事务处理和查询请求,而 SQLiteQueryBuilder 是执行基于行查询的便利辅助类。

public class StudentContentProvider extends ContentProvider {

    private SimpleSQLiteHelper sqLiteHelper;

    // 创建两个常量来区分不同的 URI 请求
    public static final int ALLROWS = 1;
    public static final int SINGLE_ROW = 2;

    public static final Uri CONTENT_URI = Uri.parse("content://com.example.contentprovider/students");

    private static final UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI("com.example.contentprovider", "students", ALLROWS);
        uriMatcher.addURI("com.example.contentprovider", "students/#",
                SINGLE_ROW);
    }

    @Override
    public boolean onCreate() {
        sqLiteHelper = new SimpleSQLiteHelper(getContext(),
                SimpleSQLiteHelper.DATABASE_NAME, null,
                SimpleSQLiteHelper.DATABASE_VERSION);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {

        SQLiteDatabase db;
        try {
            db = sqLiteHelper.getWritableDatabase();
        } catch (SQLiteException e) {
            db = sqLiteHelper.getReadableDatabase();
        }

        // 设定分组和聚合条件
        String groupBy = null;
        String having = null;

        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

        switch (uriMatcher.match(uri)) {
        case SINGLE_ROW:
            String rowID = uri.getPathSegments().get(1);
            queryBuilder.appendWhere(SimpleSQLiteHelper.KEY_ID + " = " + rowID);
            break;

        default:
            break;
        }

        // 指定要执行查询的表,根据需要,这可以是一个特定的表或者一个连接
        queryBuilder.setTables(SimpleSQLiteHelper.DATABASE_TABLE);

        // 执行查询操作
        Cursor cursor = queryBuilder.query(db, projection, selection,
                selectionArgs, groupBy, having, sortOrder);

        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        // 为一个 Content Provider URI 返回一个字符串,它标识了 MIME 类型
        switch (uriMatcher.match(uri)) {
        case ALLROWS:
            return "vnd.android.cursor.dir/vnd.example.contentprovider.elemental";

        case SINGLE_ROW:
            return "vnd.android.cursor.item/vnd.example.contentprovider.elemental";

        default:
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {

        // 打开一个可写的数据库连接
        SQLiteDatabase db = sqLiteHelper.getWritableDatabase();

        String nullColumnHack = null;

        // 向表中插入值
        long id = db.insert(SimpleSQLiteHelper.DATABASE_TABLE, nullColumnHack,
                values);

        if (id > -1) {

            // 构造并返回插入行的 URI
            Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);

            // 通知所有的观察者,数据集已经改变
            getContext().getContentResolver().notifyChange(insertedId, null);

            return insertedId;
        }

        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {

        // 打开一个可写的数据库连接
        SQLiteDatabase db = sqLiteHelper.getWritableDatabase();

        // 如果是行 URI,限定删除行为指定的行
        switch (uriMatcher.match(uri)) {
        case SINGLE_ROW:
            String rowID = uri.getPathSegments().get(1);
            selection = SimpleSQLiteHelper.KEY_ID + " = " + rowID
                    + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : "");
            break;

        default:
            break;
        }

        // 要想返回删除的项的数量,必须指定一条 where 子句,要删除所有的行并返回一个值,则传入 1
        if (selection == null) {
            selection = "1";
        }

        // 执行删除操作
        int deleteCount = db.delete(SimpleSQLiteHelper.DATABASE_TABLE,
                selection, selectionArgs);

        // 通知所有的观察者,数据集已经改变
        getContext().getContentResolver().notifyChange(uri, null);


        return deleteCount;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {

        // 打开一个可写的数据库连接
        SQLiteDatabase db = sqLiteHelper.getWritableDatabase();

        // 如果是行 URI,限定删除行为指定的行
        switch (uriMatcher.match(uri)) {
        case SINGLE_ROW:
            String rowID = uri.getPathSegments().get(1);
            selection = SimpleSQLiteHelper.KEY_ID
                    + " = "
                    + rowID
                    + (!TextUtils.isEmpty(selection) ? " AND (" + selection
                            + ")" : "");
            break;

        default:
            break;
        }

        // 执行更新
        int updateCount = db.update(SimpleSQLiteHelper.DATABASE_TABLE, values,
                selection, selectionArgs);

        // 通知所有的观察者,数据集已经改变
        getContext().getContentResolver().notifyChange(uri, null);

        return updateCount;
    }

}


Content Resolver

可以通过 Content Resolver 来访问 Content Provider 提供的数据。可以通过 getContext().getContentResolver() 方法来获取一个 Content Resolver 对象。


监听 Content Provider 中的数据变化

如果 共享的数据发生变化,可以在 Content Provider 发生数据变化时调用 getCotentResolver().notifyChange(uri, null) 来通知注册在此 URI 上的访问者

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/12082.html

相关文章

  • Android四大组件之ContentProvider全解析

    摘要:前言属于的四大组件之一本文全面解析了,包括原理使用方法实例讲解,希望你们会喜欢。 前言 ContentProvider属于 Android的四大组件之一 本文全面解析了 ContentProvider ,包括ContentProvider 原理、使用方法 & 实例讲解,希望你们会喜欢。 目录 showImg(https://segmentfault.com/img/remote/...

    XBaron 评论0 收藏0
  • Android四大组件之ContentProvider全解析

    摘要:前言属于的四大组件之一本文全面解析了,包括原理使用方法实例讲解,希望你们会喜欢。 前言 ContentProvider属于 Android的四大组件之一 本文全面解析了 ContentProvider ,包括ContentProvider 原理、使用方法 & 实例讲解,希望你们会喜欢。 目录 showImg(https://segmentfault.com/img/remote/...

    Jacendfeng 评论0 收藏0
  • Android ContenObserver 监听联系人数据变化

    摘要:如果请求的为则为数据改变都将会检测到。利用中的方法,当数据发生变化是,自动执行回调方法。二项目实践步骤定义监听器获取到联系人注册。一、知识介绍    1、ContentProvider是内容提供者          ContentResolver是内容解决者(对内容提供的数据进行操作)          ContentObserver是内容观察者(观察内容提供者提供的数据变化)   2、Co...

    JasonZhang 评论0 收藏0
  • Spring项目本地环境搭建

    摘要:依赖环境安装,并配置环境变量安装,并配置环境变量下载源码到本地,导入这里建议到自己仓库,便于翻看源码添加注释构建编译顺序直接导入选择本地的目录环境,点击完成注释掉行注释掉行将阿里云的库添加到行等待项目包下载完进入 0x01 依赖环境 安装JDK 1.8,并配置环境变量 安装Gradle 4.4.1,并配置环境变量 Git IntellJ IDEA 0x02 下载源码到本地,导入ID...

    NickZhou 评论0 收藏0
  • [译] 别再对 Angular Modules 感到迷惑

    摘要:大多数初学者会认为也有封装规则,但实际上没有。第二个规则是最后导入模块的,会覆盖前面导入模块的。 原文链接:Avoiding common confusions with modules in Angular showImg(https://segmentfault.com/img/remote/1460000015298243?w=270&h=360); Angular Modul...

    LMou 评论0 收藏0

发表评论

0条评论

Euphoria

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<