咱们学了 Android 数据持久化的技术,包括文件存储、SharedPreferences 存 储、以及数据库存储。不知道你有没有发现,使用这些持久化技术所保存的数据都只能在当 前应用程序中访问。虽然文件和 SharedPreferences 存储中提供了 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 这两种操做模式,用于供给其余的应用程序访问当前应用 的数据,但这两种模式在 Android 4.2 版本中都已被废弃了。为何呢?由于 Android 官方已 经再也不推荐使用这种方式来实现跨程序数据共享的功能,而是应该使用更加安全可靠的内容 提供器技术。数据库
可能你会有些疑惑,为何要将咱们程序中的数据共享给其余程序呢?固然,这个要视 状况而定的,好比说帐号和密码这样的隐私数据显然是不能共享给其余程序的,不过一些可 以让其余程序进行二次开发的基础性数据,咱们仍是能够选择将其共享的。例如系统的电话 簿程序,它的数据库中保存了不少的联系人信息,若是这些数据都不容许第三方的程序进行 访问的话,恐怕不少应用的功能都要大打折扣了。除了电话簿以外,还有短信、媒体库等程 序都实现了跨程序数据共享的功能,而使用的技术固然就是内容提供器了,下面咱们就来对 这一技术进行深刻的探讨。安全
内容提供器简介app
内容提供器(Content Provider)主要用于在不一样的应用程序之间实现数据共享的功能, 它提供了一套完整的机制,容许一个程序访问另外一个程序中的数据,同时还能保证被访数据 的安全性。目前,使用内容提供器是 Android 实现跨程序共享数据的标准方式。ide
不一样于文件存储和 SharedPreferences 存储中的两种全局可读写操做模式,内容提供器可 以选择只对哪一部分数据进行共享,从而保证咱们程序中的隐私数据不会有泄漏的风险。学习
内容提供器的用法通常有两种,一种是使用现有的内容提供器来读取和操做相应程序中 的数据,另外一种是建立本身的内容提供器给咱们程序的数据提供外部访问接口。那么接下来 咱们就一个一个开始学习吧,首先从使用现有的内容提供器开始。spa
7.2 访问其余程序中的数据对象
当一个应用程序经过内容提供器对其数据提供了外部访问接口,任何其余的应用程序就 均可以对这部分数据进行访问。Android 系统中自带的电话簿、短信、媒体库等程序都提供 了相似的访问接口,这就使得第三方应用程序能够充分地利用这部分数据来实现更好的功 能。下面咱们就来看一看,内容提供器究竟是如何使用的。排序
7.2.1 ContentResolver 的基本用法接口
对于每个应用程序来讲,若是想要访问内容提供器中共享的数据,就必定要借助 ContentResolve 类,能够经过 Context 中的 getContentResolver() 方法获取到该类的实例。 ContentResolver 中提供了一系列的方法用于对数据进行 CRUD 操做,其中 insert()方法用于 添加数据,update()方法用于更新数据,delete()方法用于删除数据,query()方法用于查询数 据。有没有似曾相识的感受?没错,SQLiteDatabase 中也是使用的这几个方法来进行 CRUD 操做的,只不过它们在方法参数上稍微有一些区别。ci
不一样于 SQLiteDatabase,ContentResolver 中的增删改查方法都是不接收表名参数的,而 是使用一个 Uri 参数代替,这个参数被称为内容 URI。内容 URI 给内容提供器中的数据创建 了惟一标识符,它主要由两部分组成,权限(authority)和路径(path)。权限是用于对不一样 的应用程序作区分的,通常为了不冲突,都会采用程序包名的方式来进行命名。好比某个 程序的包名是 com.example.app ,那么该程序对应的权限就能够命名为 com.example.app. provider。路径则是用于对同一应用程序中不一样的表作区分的,一般都会添加到权限的后面。 好比某个程序的数据库里存在两张表,table1 和 table2,这时就能够将路径分别命名为/table1 和/table2,而后把权限和路径进行组合,内容 URI 就变成了 com.example.app.provider/table1 和 com.example.app.provider/table2。不过,目前还很难辨认出这两个字符串就是两个内容 URI,咱们还须要在字符串的头部加上协议声明。所以,内容 URI 最标准的格式写法以下:
content://com.example.app.provider/table1 content://com.example.app.provider/table2
有没有发现,内容 URI 能够很是清楚地表达出咱们想要访问哪一个程序中哪张表里的数 据。也正是所以,ContentResolver 中的增删改查方法才都接收 Uri 对象做为参数,由于使用 表名的话系统将没法得知咱们指望访问的是哪一个应用程序里的表。
在获得了内容 URI 字符串以后,咱们还须要将它解析成 Uri 对象才能够做为参数传入。 解析的方法也至关简单,代码以下所示:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
只须要调用 Uri.parse()方法,就能够将内容 URI 字符串解析成 Uri 对象了。
如今咱们就可使用这个 Uri 对象来查询 table1 表中的数据了,代码以下所示:
Cursor cursor = getContentResolver().query(
uri,
projection, selection, selectionArgs, sortOrder);
这些参数和 SQLiteDatabase 中 query()方法里的参数很像,但整体来讲要简单一些,毕 竟这是在访问其余程序中的数据,不必构建过于复杂的查询语句。下表对使用到的这部分 参数进行了详细的解释。
query()方法参数 |
对应 SQL 部分 |
描述 |
uri |
from table_name |
指定查询某个应用程序下的某一张表 |
projection |
select column1, column2 |
指定查询的列名 |
selection |
where column = value |
指定 where 的约束条件 |
selectionArgs |
- |
为 where 中的占位符提供具体的值 |
orderBy |
order by column1, column2 |
指定查询结果的排序方式 |
查询完成后返回的仍然是一个 Cursor 对象,这时咱们就能够将数据从 Cursor 对象中逐
个读取出来了。读取的思路仍然是经过移动游标的位置来遍历 Cursor 的全部行,而后再取出 每一行中相应列的数据,代码以下所示:
if (cursor != null) {
while (cursor.moveToNext()) {
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
掌握了最难的查询操做,剩下的增长、修改、删除操做就更不在话下了。咱们先来看看 如何向 table1 表中添加一条数据,代码以下所示:
ContentValues values = new ContentValues(); values.put("column1", "text"); values.put("column2", 1); getContentResolver().insert(uri, values);
能够看到,仍然是将待添加的数据组装到 ContentValues 中,而后调用 ContentResolver的 insert()方法,将 Uri 和 ContentValues 做为参数传入便可。
现 在 如 果 我 们 想 要 更 新 这 条 新 添 加 的 数 据 , 把 column1 的 值 清 空 , 可 以 借 助
ContentResolver 的 update()方法实现,代码以下所示:
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new
String[] {"text", "1"});
注意上述代码使用了 selection 和 selectionArgs 参数来对想要更新的数据进行约束,以防 止全部的行都会受影响。
最后,能够调用 ContentResolver 的 delete()方法将这条数据删除掉,代码以下所示:
getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });
到这里为止,咱们就把 ContentResolver 中的增删改查方法所有学完了。是否是感受很是 简单?由于这些知识早在上一章中学习 SQLiteDatabase 的时候你就已经掌握了,所需特别注 意的就只有 uri 这个参数而已。