(欢迎大家加入android技术交流:209796692)
实现ContentProviderMIME类型
ContentProvider有两个方法返回MIME类型。
一个对任何provider都要实现的方法。
如果你的provider提供的是文件,此方法是期望被实现的。
表的MIME类型们
getType()方法返回一个MIME格式的String,此String描述了由contentURI参数计算出的数据类型。Uri可以是一个模式而不是一个具体的URI;此时,你应该返回对应于那些符合contentURI模式的的数据的类型。
对于普通的数据类型,比如文本,HTML或JPEG,getType()应该返回标准的MIME类型。关于标准MIME类型的一个完整的列表能在IANAMIMEMediaTypes网站上找到。
对于指向表数据的一行或多行的contentURI们,getType()应返回一个Androidvendor-specificMIME格式:
·类型部分:vnd
·子类型部分:
·如果URI指向单行:android.cursor.item/
·如果URI指向多行:android.cursor.dir/
·Provider-specific部分:vnd.<name>.<type>
你要提供<name>和<type>。<name>的值应是一个全局唯一的,并且<type>的值对于对应的URI模式也需是唯一的。对<name>的一个好的选择是用你公司的名字或你的应用的Android包名的一部分。对于<type>的一个好的选择是使用标志与URI相关连的表的字符串。
例如,如果一个provider的authority是com.example.app.provider,并且它曝露了一个叫做table1的表,那么表示表中多行的MIME类型就是:
vnd.android.cursor.dir/vnd.com.example.provider.table1
表示单行的MIME类型是:
vnd.android.cursor.item/vnd.com.example.provider.table1
表示文件的MIME们
如果你的provider提供文件,就要实现getStreamTypes()。这个方法对传入的contentURI返回一个由指向文件们的MIME类型组成的String数组,你应该跟据MIME类型过虑参数过虑MIME类型们,所以你只返回那些客户端真正相要的MIME类型们。
例如,假设一个provider提供图像文件,有.jpg,.png,和.gif类型。如果一个应用使用过滤字符串image/*(表示某些是"image"的东西)调用ContentResolver.getStreamTypes(),那么ContentProvider.getStreamTypes()方法应返回数组:
{"image/jpeg","image/png","image/gif"}
如果应用只对.jpg文件感兴趣,它应使用过滤字符串*\/jpeg调用ContentResolver.getStreamTypes(),并且ContentProvider.getStreamTypes()应该返回:
{"image/jpeg"}
如果你的provider没有提供过滤字符串中所请求的MIME,getStreamTypes()应返回null.
实现一个Contract(契约)类
Contractclass是一个publicfinal类,它包含有定义URI们的常量,列的名字,MIME类型们,以及其它用于provider的元数据。这个类建立了一个provider和其它应用之间的契约以保证在URI、列名等项的值发生变化时依然能被正确的操作。
一个契约类对开发者有帮助,因为它提供了一个好识别的名字,而不用直接使用数字,于是开发者就不会为列名或URI们使用错误的值。因为它是一个类,它就可以包含Javadoc文档。集成开发环境,比如Eclipse可以跟据契约类自动完成常量名并为常量显示Javadoc。
开发者不能从你的应用操作契约类的类文件,但是他们可以从你提供的.jar文件中静态的把它编译到他们的应用中。
ContactsContract类和它的嵌套类们就是契约类的例子。
实现ContentProvider权限
对android系统的所有方面的权限在主题SecurityandPermissions中有详细的描述。主题DataStorage中也描术了安全和权限对各种存储类型的影响。简略的说,重要的几点是:
·默认下,存储在设备的内部存储器上的数据文件是你的应用和provider私有的。
·你创建的SQLiteDatabase数据库是你的应用和provider私有的。
·默认上,保存到外部存储器上的数据文件是公有的和全局可读的。你不能使用一个contentprovider来限制外部存储上的文件的操作,因为其它应用可以使用其它API来读写它们。
·那些用于在你的设备内部存储器上打开或创建文件或SQLite数据的方法们可能会暗中给予其它应用读写的权限。如果你使用一个内部文件或数据库作为你的provider的数据仓库,并且你给予它们"world-readable"或"world-writeable"权限,你在manifest中对你的provider的权限声明将不能保护你的数据。内部存储上的文件和数据库的的默认权限是"private",并且你也不应该改变你的provider的数据仓库的权限。
如果你想使用contentprovider的权限来控制对你的数据的操作,那么你应该存储你的数据到内部文件,SQLite数据库,或"cloud"(例如,在一个远程服务上),并且你应该保持文件和数据库私属于你的应用。
实现权限
所有的应用都可以从你的provider读或写你的provider写,即使后台的数据是私有的。因为默认下你的provider没有权限设置。要改变这种情况,可以在manifest文件中设置你的provider的权限,使用<provider>元素的属性或其儿子元素。你可以设置应用于整个provider的权限,或只应用于特定表的,或特定记录的,或所有三者的。
你在manifest文件中用一个或多个<permission>元素定义你的provider的权限。要使用于你的provider的权限是唯一的,为使用android:name属性使用Java风格的范围限定。例如,为读权限命名为com.example.app.provider.permission.READ_PROVIDER.
下面所列的描述说明了provider权限的范围,开始是应用于整个provider的然后变成更细颗粒度。更细颗粒度的权限优先级高于大范围的权限:
单一的read-writeprovider-level权限
一个权限,它控制对整个provider的读和写权限,用<provider>元素的android:permission属性指定。
分开的读和写provider-level权限
控制整个provider的一个读权限和一个写权限。你用<provider>元素的android:readPermission和android:writePermission属性指定它们。它们的优先级比android:permission更高。
Path-level权限
对你的provider中的contentURI的读、写,或读/写权限。你使用<provider>元素<path-permission>子元素指定的指定每个URI的权限,你可以指定一个读/写权限,一个读权限,或一个写权限,或所有三者。读和写权限优先级高于读/写权限。并且,path-level权限优先级高于provider-level权限。
临时权限
也是一个权限级别,它代表了临时获取并赋于一个应用的权限,即使这个应用不具有权限。临时权限特性减少了一个应用需要在其mainifest中声明的权限的数量。当你打开了临时权限,只有那些持续操作你的所有数据的应用们才需要对你的provider有“持久的”权限.
考虑一下你实现一个emailprovider和应用,当你想允许一个外部图像查看应用通过你的provider来显示图像附件时所需的权限。要想不用声明权限就给予图像查看应用所需的权限,就需设置图像的contentURI的临时权限。这样设计你的email应用:当用户想要显示一个图像,你的应用发送一个intent给图像查看应用,这个intent包含了图像的contentURI和权限。图像查看应用之后可以请求你的emailprovider来获取图像,即使图像查看应用对你的provider不具有普通的读权限。
要开启临时权限,既可以设置<provider>元素的android:grantUriPermissions属性,也可以添加一个或多个<grant-uri-permission>子元素到你的<provider>元素。如果你使用临时权限,你必须在从你的provider删除对一个contentURI的支持时调用Context.revokeUriPermission(),如果这个contentURI关联了一个临时权限的话。
属性的值决定了你的provider具有什么操作的可操作性。如果属性被设置为true,那么系统将为你的整个provider获取临时权限,覆盖任何其它通过provider-level或path-level获取的权限。
如果这个flag设置为false,那么你必须添加<grant-uri-permission>子元素到你的<provider>元素上。每个子元素指定了哪个contentURI或哪些URI们授予了临时权限。
要把临时权限委托给一个应用,intent必须包含FLAG_GRANT_READ_URI_PERMISSION或FLAG_GRANT_WRITE_URI_PERMISSION标志,或两者都有。它们可用setFlags()方法设置。
如果android:grantUriPermissions属性不存在,就被认为是false。