3.5 数据源组件Content Provider解析
和其他组件不同,数据源组件并不包含特定的功能逻辑,而是负责为应用提供数据访问的接口。在Android中,除了放在扩展存储设备等共享目录中的数据,各个应用所持有的数据库、文件、资源信息等内容都是私有的,其他应用没有权限直接访问。这样的权限设计,保证了各个应用的独立性和安全性,避免应用所依赖的关键数据暴露或被破坏。
但在很多场景下,应用间的数据交互还是很有必要的。比如联系人应用,就应该允许第三方应用使用其中的数据,对联系人的信息进行查询、选择和修改等。数据源组件正是为了帮助在Android的各个应用间共享数据而设计的。
3.5.1 数据源组件的定位和操作
数据源组件派生自抽象类android.content.ContentProvider,需要实现其中的query、update、insert和delete等抽象接口。数据源组件中数据存储的方式没有任何限制,可以通过数据库、文件等任意方式来实现(甚至是直接从其他数据源获取)。
整个数据源组件的接口设计集合了REST标准[1]和数据库设计的概念,它通过URI(Uniform Resource Identifier)进行定位,像数据库一样,通过SQL语句(Structured Query Language)来描述具体的操作。
URI,就是全局统一定位标志,通过一个结构化的字符串,唯一标识数据源的地址信息。网络地址URL,是其中的一个子集。[2]每个数据源组件,都有一个唯一的URI标识,其构成如图3-13所示。
图 3-13 Android数据源组件URI结构图
组件开发者需要在配置文件中对应的provider条目下声明URI信息的地址描述,比如:
<provider android:name="SimpleContentProvider"
android:authorities="com.duguhome.provider.sample">
</provider>
数据源组件URI的命名通常要求与所在应用的包名相关联,以保证在同一个Android设备上具有唯一性(因为统一设备上的应用包名是唯一的,只要不是应用内部的命名冲突,就可以保证该组件地址在设备上是唯一的)。如果在安装应用时,发现设备中已经存在具有相同URI地址的数据源组件,新的应用会由于数据源组件冲突而安装失败。引入URI,同时也就引入了对REST接口的规范,数据源组件可以用REST的方式来定义操作,比如:
对列表类型操作
content://com.duguhome.provider.sample/items
对id为1的条目进行操作
content://com.duguhome.provider.sample/items/1
但REST是基于HTTP协议而设计的,其设计偏向于保证足够的简单和兼容,牺牲了对数据操作灵活性的支持。基于REST不能对数据做投影(Projection)、排序(Order by)、筛选(Selection)等更为精细的操作。
因此,为了增强对数据的控制力,Android同时为数据源组件引入了SQL语句的支持。如图3-14所示,数据源接口中的一些参数都是按照SQL的方式和语法来提供的。并且,对于查询操作,Android没有像REST标准一样,一次性将数据按照JSON或XML的格式全部读取出来,而是返回数据指针android.database.Cursor对象。在数据指针的封装下,只有需要被使用的数据才会从数据源中逐行读取出来,比之一次性地加载所有数据的方式,提升了灵活性,提高了执行效率,节省了内存开销。
图 3-14 Android数据源组件查询操作的参数与SQL语句的对应关系
[1]关于REST的介绍,参见:http://en.wikipedia.org/wiki/Representational_State_Transfer。
[2]关于URI的更多介绍,参见:http://en.wikipedia.org/wiki/Uniform_Resource_Identifier。