Android四大组件之ContentProvider总结

来源:互联网 发布:身份证录入软件 编辑:程序博客网 时间:2024/06/04 18:29

ContentProvider

ContentProvider,顾名思义,内容提供者,为存储和获取数据提供统一的接口,可以在不同的应用程序之间共享数据

ContentProvider为应用间的数据交互提供了一个安全的环境。它准许你把自己的应用数据根据需求开放给其他应用进行增、删、改、查,而不用担心直接开放数据库权限而带来的安全问题。

ContentProvider提供了对底层数据存储方式的抽象,如下图中:

这里写图片描述

底层使用了SQLite数据库,在用了ContentProvider封装后,即使你把数据库换成MongoDB,也不会对上层数据使用层代码产生影响。

ContentResolver

ContentProvider对数据层进行了封装,那该如何使用ContentProvider进行增,删,改,查的操作呢?

在一个应用中直接调用另一个应用的ContentProvider,显然是不可能的。Android中跨进程,一定需要通过Binder机制

其实系统就提供给了我们一个叫ContentResolver的东西。ContentResolver,顾名思义,内容解析者

我们可以在Context的实现类ContextImpl中获得ApplicationContentResolver的实例,ApplicationContentResolver继承自ContentResolver(ContentResolver是个抽象类):

    private final ApplicationContentResolver mContentResolver;    @Override    public ContentResolver getContentResolver() {        return mContentResolver;    }    private static final class ApplicationContentResolver extends ContentResolver {        ……    }

之后ContentResolver通过层层调用,与系统服务进行交互,最后成功调用ContentProvider。情况可以分为三种:

  1. Provider进程不存在:当Provider进程不存在时,先创建进程并发布对应的Provider。

  2. Provider未发布:请求Provider时,Provider进程存在但Provider的记录对象为空,则要去发布对应的Provider。

  3. Provider已发布:直接可以调用了。

关于源码的解析,请移步大神Gityuan写的理解ContentProvider原理。

ContentResolver帮助我们统一管理与不同ContentProvider间的操作,如图:
这里写图片描述

ContentProvider中的URI

那ContentResolver是如何找到自己需要的ContentProvider呢?我们通过URI(Uniform Resource Identifier),即一个标识、定位任何资源的字符串

ContentProvider中的URI有固定格式,如下图:

这里写图片描述

  • Authority:授权信息,用以区别不同的ContentProvider
  • Path:表名,用以区分ContentProvider中不同的数据表
  • Id:Id号,用以区别表中的不同数据

URI组装代码示例:

public class TestContract {    protected static final String CONTENT_AUTHORITY = "me.pengtao.contentprovidertest";    protected static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);    protected static final String PATH_TEST = "test";    public static final class TestEntry implements BaseColumns {        public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(PATH_TEST).build();        protected static Uri buildUri(long id) {            return ContentUris.withAppendedId(CONTENT_URI, id);        }        protected static final String TABLE_NAME = "test";        public static final String COLUMN_NAME = "name";    }}

从上面代码我们可以看到,我们创建了一个content://me.pengtao.contentprovidertest/test的uri,并且开了一个静态方法,用以在有新数据产生时根据id生成新的uri。

创建ContentProvider

必须方法

抽象类ContentProvider定义了六个抽象方法,您必须将这些方法作为自己具体子类的一部分加以实现。 所有这些方法(onCreate() 除外)都由一个尝试访问您的内容提供程序的客户端应用调用:

query()
从您的提供程序检索数据。使用参数选择要查询的表、要返回的行和列以及结果的排序顺序。 将数据作为 Cursor 对象返回。

insert()
在您的提供程序中插入一个新行。使用参数选择目标表并获取要使用的列值。 返回新插入行的内容 URI。

update()
更新您提供程序中的现有行。使用参数选择要更新的表和行,并获取更新后的列值。 返回已更新的行数。

delete()
从您的提供程序中删除行。使用参数选择要删除的表和行。 返回已删除的行数。

getType()
返回内容 URI 对应的 MIME 类型。实现内容提供程序 MIME 类型部分对此方法做了更详尽的描述。

onCreate()
初始化您的提供程序。Android 系统会在创建您的提供程序后立即调用此方法。 请注意,ContentResolver 对象尝试访问您的提供程序时,系统才会创建它。

注意:

  • 所有这些方法(onCreate() 除外)都可由多个线程同时调用,因此它们必须是线程安全方法。

  • 避免在 onCreate() 中执行长时间操作。将初始化任务推迟到实际需要时进行。

  • 尽管您必须实现这些方法,但您的代码只需返回要求的数据类型,无需执行任何其他操作。 例如,您可能想防止其他应用向某些表插入数据。 要实现此目的,您可以忽略 insert() 调用并返回 0。

UriMatcher

为帮助您选择对传入的内容 URI 执行的操作,提供程序 API 加入了实用类 UriMatcher,它会将内容 URI“模式”映射到整型值。您可以在一个 switch 语句中使用这些整型值,为匹配特定模式的一个或多个内容 URI 选择所需操作。

例1:

content://com.example.app.provider/table1

一个名为 table1 的表

例2:

content://com.example.app.provider/table3/1 

对应由 table3 中 1 标识的行的内容 URI

例3:

以下代码段演示了 UriMatcher 中方法的工作方式。 此代码采用不同方式处理整个表的 URI 与单个行的 URI,它为表使用的内容 URI 模式是 content://< authority >/< path >,为单个行使用的内容 URI 模式则是 content://< authority >/< path >/< id >

方法addURI()会将授权和路径映射到一个整型值,方法match()会返回URI的整型值。switch语句会在查询整个表与查询单个记录之间进行选择:

public class ExampleProvider extends ContentProvider {...    // Creates a UriMatcher object.    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);    static {        /*         * The calls to addURI() go here, for all of the content URI patterns that the provider         * should recognize. For this snippet, only the calls for table 3 are shown.         */        /*         * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used         * in the path         */        sUriMatcher.addURI("com.example.app.provider", "table3", 1);        /*         * Sets the code for a single row to 2. In this case, the "#" wildcard is         * used. "content://com.example.app.provider/table3/3" matches, but         * "content://com.example.app.provider/table3 doesn't.         */        sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);    }...    // Implements ContentProvider.query()    public Cursor query(        Uri uri,        String[] projection,        String selection,        String[] selectionArgs,        String sortOrder) {...        /*         * Choose the table to query and a sort order based on the code returned for the incoming         * URI. Here, too, only the statements for table 3 are shown.         */        switch (sUriMatcher.match(uri)) {            // If the incoming URI was for all of table3            case 1:                if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";                break;            // If the incoming URI was for a single row            case 2:                /*                 * Because this URI was for a single row, the _ID value part is                 * present. Get the last path segment from the URI; this is the _ID value.                 * Then, append the value to the WHERE clause for the query                 */                selection = selection + "_ID = " uri.getLastPathSegment();                break;            default:            ...                // If the URI is not recognized, you should do some error handling here.        }        // call the code to actually do the query    }

程序权限

即使底层数据为私有数据,所有应用仍可从您的提供程序读取数据或向其写入数据,因为在默认情况下,您的提供程序未设置权限。 要想改变这种情况,请使用属性或 < provider > 元素的子元素在您的清单文件中为您的提供程序设置权限。 您可以设置适用于整个提供程序、特定表甚至特定记录的权限,或者设置同时适用于这三者的权限

您可以通过清单文件中的一个或多个 < permission > 元素为您的提供程序定义权限。要使权限对您的提供程序具有唯一性,请为 android:name 属性使用 Java 风格作用域。 例如,将读取权限命名为 com.example.app.provider.permission.READ_PROVIDER

更多权限设置,请见谷歌官方文档——创建内容提供程序。

参考:
1.ContentProvider从入门到精通
2.谷歌官方文档——创建内容提供程序
3.理解ContentProvider原理

阅读全文
1 0
原创粉丝点击