1、ContentProvider概述
1.1、ContentProvider简介
ContentProvider即内容提供者,是Android的四大组件之一。内容提供器(ContentProvider)主要用于在不同的应用程序之间实现数据共享的功能,它允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。
使用ContentProvider实现跨程序访问数据的原理是:使用该程序的内容解析者的crud操作来访问另一个程序的数据,另一个程序只需要使用了ContentProvider暴露了被访问的数据接口就行,通常该接口就是内容Uri对象。
1.2、ContentProvider和其它方式共享数据区别
1)与AIDL的区别:
aidl是提供内存中的数据给其他应用访问。
内容提供者是提供磁盘中的数据给其他应用访问。
2)与另外三种数据持久化存储的区别:
使用文件存储、SharedPreferences存储、以及数据库存储这些持久化技术所保存的数据都只能在当前应用程序中访问,而且数据访问方式会因数据存储的方式而不同。 ContentProvider提供应用程序之间数据共享的功能,而且还统一了数据访问方式,应用程序只需通过ContentProvider对其数据提供了外部访问接口,任何其他的应用程序就可以对这部分数据进行访问了。
2、使用ContentResolver访问其他程序中的数据
2.1、ContentResolver简介
ContentResolver内容解析者,用于获取内容提供者提供出来的数据,只有通过这个类中的方法才可以获取到内容提供者要暴露出来的数据。在内容解析者中使用的Uri必须和内容提供者中设置的Uri对应。
2.2、ContentResolver基本用法
外部应用可以使用Context的getContentResolver()方法获取ContentResolver 对象,就可以使用ContentResolver的和ContentProvider相同的crud方法对ContentProvider中的数据进行添加、删除、修改和查询操作。
和Sqlite不同的是,ContentResolver中的增删改查方法都不是接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI,它主要由两部分组成,权限(authority)和路径(path)。
权限是用于对不同的应用程序做区分的,一般采用程序包名进行命名。比如某个程序的包名是 com.example.app,那么该程序对应的权限就可以命名为 com.example.app. provider。
路径则是用于对同一应用程序中不同的表做区分的,通常都会添加到权限的后面。 比如某个程序的数据库里存在一张table1表,这时就可以将路径命名为/table1 ,然后把权限和路径进行组合,内容 URI就变成了 com.example.app.provider/table1 。
内容URI标准的格式写法:content://com.example.app.provider/table1
得到了内容 URI字符串之后,接着调用Uri.parse()方法解析,代码如下所示: Uri uri = Uri.parse("content://com.example.app.provider/table1") ,就可以将内容URI字符串解析成Uri对象了。
1)读取系统联系人
数据库文件:com.android.provider.contacts
两张重要表:
Raw_conmtracts联系人ID表:列contract_id
data联系人数据表:
data1:存放联系人的数据(姓名,电话,邮箱)
raw_contract_id:联系人的ID
mimetype_id:联系人的数据类型
查询联系人的步骤:
1)查询raw_contract表。得到contract_id
2)把contract_id拿到data表里面去,查询data1, mimetype类型
如查询系统联系人代码如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void query(View v) {
/*
* android:authorities="contacts;com.android.contacts"
*/
// 1.获取到内容解析者对象
ContentResolver resolver = getContentResolver();
// 3.获取raw_contacts表的uri
Uri contactUri = Uri
.parse("content://com.android.contacts/raw_contacts");
// 获取data表的uri
Uri dataUri = Uri.parse("content://com.android.contacts/data");
// 2.使用解析者调用内容提供者的查询方法,获取contact_id
Cursor cursor = resolver.query(contactUri,
new String[] { "contact_id" }, null, null, null);
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex("contact_id"));
System.out.println("id=" + id);
// 4.根据获取的contact_id即联系人的ID,去data表查询数据, 根据列data1,
// mimetype,把这个联系人的数据查询出来。
Cursor cursor2 = resolver.query(dataUri, new String[] { "data1",
"mimetype" }, "raw_contact_id=?", new String[] { id + "" },
null);
// 解析数据
while (cursor2.moveToNext()) {
/**
* 在查询联系人的数据类型的时候,系统并不是真的去查询data表,而是去查询了view_data表,所以,
* 指定的miemtype,不能写成mimetype_id .并且返回的类型应该是String即3查询出具体的mime类型。
*/
String data1 = cursor2.getString(cursor2
.getColumnIndex("data1"));
System.out.println("data1=" + data1);
String mimetype = cursor2.getString(cursor2
.getColumnIndex("mimetype"));
System.out.println("mimetype=" + mimetype);
}
// 依次关闭资源
cursor2.close();
}
cursor.close();
}
}
如上所示,先是根据raw_contacts表的uri获取该表的contact_id即联系人的id,接着根据data表的Uri获取该表的id和刚得到的contact_id去查询该表,获取联系人姓名和号码信息。当然,不要忘记添加读取系统联系人的权限:android.permission.READ_CONTACTS。
注:android联系人信息Uri封装子ContactsContract类中。因此,也可以使用ContactsContract中封装好的Uri方式来操作联系人信息。
3、使用ContentProvider提供数据给其他程序访问
当我们要提供自己应用程序的数据让其他应用程序访问时,就需要自定义内容提供者了。但是在开发中很少会将自己的程序数据给其他程序访问,所以自定义内容提供者本人觉得只需大致了解就行。
3.1、ContentProvider的六个方法
1)public boolean onCreate()
初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作, 返回 true 表示内容提供器初始化成功,返回 false 则表示失败。注意,只有当存在ContentResolver尝试访问我们程序中的数据时,内容提供器才会被初始化。
2)public Uri insert(Uri uri, ContentValues values)
该方法用于供外部应用往ContentProvider添加数据。
3)public int delete(Uri uri, String selection, String[] selectionArgs)
该方法用于供外部应用从ContentProvider删除数据。
4)public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
该方法用于供外部应用更新ContentProvider中的数据。
5)public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
该方法用于供外部应用从ContentProvider中获取数据。
6)public String getType(Uri uri)
该方法用于返回当前Uri所代表数据的MIME类型。
一个内容 URI所对应的MIME 字符串主要由三部分组分,Android对这三个部分做了如下格式规定。
1. 必须以 vnd开头。
2. 如果内容 URI以路径结尾,则后接 android.cursor.dir/,如果内容 URI以 id结尾, 则后接 android.cursor.item/。
3. 最后接上 vnd.<authority>.<path>。
如:
对于 content://com.example.app.provider/table1这个内容 URI,它所对应的 MIME 类型就可以写成: vnd.android.cursor.dir/vnd.com.example.app.provider.table1
对于 content://com.example.app.provider/table1/1这个内容 URI,它所对应的 MIME类型 就可以写: vnd.android.cursor.item/vnd. com.example.app.provider.table1
注:使用通配符的方式来分别匹配内容 URI,规则如下。
1. *:表示匹配任意长度的任意字符
2. #:表示匹配任意长度的数字
3.2、ContentProvider的基本用法
ContentProvider的作用就是提供接口供其它程序访问, 它的用法就是如何在程序中创建一个 ContentProvider,以下下是创建ContentProvider的步骤:
1)创建MyProvider继承自ContentProvider,实现其六个方法。
2)借助UriMatcher这个类匹配内容URI的功能。
UriMatcher 中提供了一个addURI()方法,这个方法接收三个参数,可以分别把权限、路径和一个自定义代码传进去。这样,当调用UriMatcher的match()方法时,就可以将一个Uri对象传入,返回值是某个能够匹配这个Uri对象所对应的自定义代码,利用这个代码,我们就可以判断出调用方期望访问的是哪张表中的数据了。
public class MyProvider extends ContentProvider {
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
public static final int TABLE2_DIR = 2;
public static final int TABLE2_ITEM = 3;
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider ", "table1/#", TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2", TABLE2_DIRSS);
uriMatcher.addURI("com.example.app.provider ", "table2/#", TABLE2_ITEM);
}
.....
}
3)编写Crud代码
4)重写getType()
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider. table1";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider. table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider. table2";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider. table2";
default:
break;
}
return null;
}
好了,ContentProvider简单使用已经介绍完了,接着说下在定义ContentProvider出现的两个类,一个是Uri,另一个是UriMatcher。
4、与ContentProvider有关的其他类介绍
4.1、Uri
Uri代表了要操作的数据,通过Uri的parser解析出来,主要包含了两部分信息:
1)需要操作的ContentProvider ,
2)对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:
以content://znouy.com.provider/student/10为例
①ContentProvider的scheme已经由Android所规定, scheme为:content://
②主机名(或叫authority)定义的是哪个ContentProvider提供这些数据。用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。如主机名:znouy.com.provider
③路径(path)用来表示我们要操作的数据,一般写表名。路径的构建应根据业务而定:
要操作person表中id为10的记录,可以构建这样的路径:/person/10
要操作person表中id为10的记录的name字段, person/10/name
要操作person表中的所有记录,可以构建这样的路径:/person
4.2、UriMatcher
Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris ,这里只介绍UriMatcher 类,该类主要用作注册Uri和匹配Uri。
这里介绍几个常用的方法和常量:
UriMatcher.NO_MATCH:表示不匹配任何路径的返回码,值为-1。
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
addUri():添加需要匹配uri,如果匹配就会返回匹配码
sMatcher.addURI(“authority”, “person”, code);
match():如果匹配了指定的uri路径,则返回匹配码code