对内容提供者的访问,是通过ContentResolver来完成的。某个应用通过ContentProvider向外提供访问的接口,访问者通过ContentResolver进行访问。
连接ContentProvider与ContentResolver的就是Uri。因此,想要准确地访问某个内容提供者,写对Uri是非常重要的事情。其Uri的拼写规则为: Uri必须 以 “content://”开头。当自己写完一个内容提供者之后,需要在清单文件中进行注册,此时便需要指定authorities属性,假设该属性的值是x。那么通过ContentResolver访问该ContentProvider时的Uri便为:“content://x”。因此,可以通过查看系统的源码,从而拼出系统内容提供者的Uri。只需要用"content://"拼上该内容提供者的authorities值即可。
以上是Uri的基本形式。但有些时候会发现Uri会是“content://x/yyy”的形式,也就是Uri后面指定的path。
对于path的理解:
我们可以把yyy看成一个指令,用于指使ContentProvider执行什么样的操作,或者是操作哪一个数据结构(大部分情况下是数据库)等。
下面以android系统的短信内容提供者为例,首先看\providers\TelephonyProvider中的清单文件,其中有一个SmsProvider,它的配置是:
<provider android:name="SmsProvider" android:authorities="sms" android:exported="true" android:multiprocess="true" android:readPermission="android.permission.READ_SMS" android:writePermission="android.permission.WRITE_SMS" />
因此,我们要想通过ContentResolver访问SmsProvider,那么Uri便可以写成:“content://sms”。
再看SmsProvider中的具体代码:
在静态代码块中,为UriMatcher添加了一系列的Uri。截取前三个如下:
static { sURLMatcher.addURI("sms", null, SMS_ALL); sURLMatcher.addURI("sms", "#", SMS_ALL_ID); sURLMatcher.addURI("sms", "inbox", SMS_INBOX);接着看一个SMS_INBOX的使用:
在query,insert,update三个方法中都有一个case SMS_INBOX:。以query方法为例,在case SMS_INBOX情况下,它最终会执行:
private void constructQueryForBox(SQLiteQueryBuilder qb, int type) { qb.setTables(TABLE_SMS); if (type != Sms.MESSAGE_TYPE_ALL) { qb.appendWhere("type=" + type); } }
从上面的代码可以看出,SMS_INBOX只不过是为sql语句添加一个查询条件而已。
同理当取成case SMS_SENT: case SMS_DRAFT等值时,也是会执行上面的方法,只不过将参数type的值给换了,也就是查询条件给更换了。
因此,从上面可以看出当在content://x/yyy后面path的一个作用就是执行不同的操作。
同理,当我们再看到sURLMatcher.addURI("sms", "raw",SMS_RAW_MESSAGE);,从而跳跃SMS_RAW_MESSAGE时,会发现它是将要操作的表给换了。即当uri为content://sms/raw时,它改变了ContentProvider要操作的表。
综上所述:content://x/yyy后面的path只是影响着要执行sql语句。而具体会执行什么样的内容,自己应该通过UriMatcher. addURI()方法中的哪一个拼到content://x后面,就需要根据源码来看,看哪一个是自己需要的操作。
它是用来操作ContentProvider的,后者提供了CRUD方法,而可以用前者调用CRUD。这些CRUD操作,最终会调用与它Uri相匹配的ContentProvider的。而且在ContentResolver中还可以注册内容观察者。用registerContentObserver()方法即可。也可以通过调用notifyChange()等方法来通知相应的内容观察者。从而可以调用内容观察者中的方法。