ContentProvider (内容提供者)是一种共享型组件,可以为系统内应用于与应用之间提供访问接口。
ContentProvide要想正常工作需要三个关键点:
- ContentProvider:对外提供数据的访问接口。
- Uri:ContentProvider的唯一标识,外界可根据其访问对应的ContentProvider。
- ContentResolver
比如,当应用A想把自己数据暴露出来让别的应用也可以操作的话,就可以在应用A内部创建一个ContentProvider实现相关方法并添加URI,然后在其他应用中(应用B)就可以通过ContentResolver和URI来访问应用A中的ContentProvider了。
很多人都会觉得ContentProvider很难理解,因为很多书上在介绍ContentProvider是后上来就用很复杂的方式来讲解,还结合什么数据库、xml等等之类操作,搞得看起来很复杂,让很多的初学者望而却步,其实我们完全不必害怕。你可以把ContentProvider看做是一个网站,在生活中你想访问一个网站就必须要有一个URL地址,而这里的URI就好比这个URL地址,然后就可以用ContentResolver拿着这个这个URI地址去访问了。
一、创建自己的ContentProvider
- 创建ContentProvider。
创建一个自己的ContentProvider也很简单,同四大组件中其他的组件类似,首先继承系统ContentProvider来创建一个类,并实现相关方法:
public class UserInfoProvider extends ContentProvider {
static final String TAG = UserInfoProvider.class.getSimpleName();
@Override
public boolean onCreate() {
Log.i(TAG, "onCreate: ");
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
Log.i(TAG, "query: ");
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
Log.i(TAG, "getType: ");
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
Log.i(TAG, "insert: ");
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
Log.i(TAG, "delete: ");
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
Log.i(TAG, "update: ");
return 0;
}
}
然后再 AndroidMainfest.xml 中注册并添加 authorities,这个authorities就是传入到URI中用的,注意要将exported设为true这样外界才能访问到。
<provider
android:name=".contentprovides.UserInfoProvider"
android:authorities="cn.codingblock.androidadvancestudy.contentprovides.UserInfoProvider"
android:exported="true" />
一个基本的ContentProvider创建好了,接下来要做的就是使用ContentResolver来访问它了。
- 解析Uri
解析Uri使用Uri.parse()来解析,传入对应的参数,参数格式为:
content://authorities/ 对应于上面的UserInfoProvider来说,一个URI为:
Uri uri = Uri.parse("content://cn.codingblock.androidadvancestudy.contentprovides.UserInfoProvider/");
- 通过Context.getContentResolver()获取ContentResolver对象。
ContentResolver contentResolver = getContentResolver();
我们另外新建一个工程,暂且叫做TestApp吧,在这个新的工程里面添加一个Activity,在Activity中使用ContentResolver,通过URI来访问上面的(不同应用中)ContentProvider。
public class UserInfoResolverActivity extends AppCompatActivity implements View.OnClickListener {
private final static String TAG = UserInfoResolverActivity.class.getSimpleName();
private ContentResolver contentResolver;
private Uri uri = Uri.parse("content://cn.codingblock.androidadvancestudy.contentprovides.UserInfoProvider/");
Button btn_insert;
Button btn_query;
Button btn_update;
Button btn_delete;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_info_resolver);
// 获取ContentResolver对象
contentResolver = getContentResolver();
btn_insert = (Button) findViewById(R.id.btn_insert);
btn_query = (Button) findViewById(R.id.btn_query);
btn_update = (Button) findViewById(R.id.btn_update);
btn_delete = (Button) findViewById(R.id.btn_delete);
btn_insert.setOnClickListener(this);
btn_query.setOnClickListener(this);
btn_update.setOnClickListener(this);
btn_delete.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_insert:
insert();
break;
case R.id.btn_query:
query();
break;
case R.id.btn_update:
update();
break;
case R.id.btn_delete:
delete();
break;
}
}
public void insert() {
Uri tempUri = contentResolver.insert(uri, values);
Log.i(TAG, "insert: tempUri = " + tempUri);
}
public void query() {
Cursor cursor = contentResolver.query(uri, null, "where", null, null);
Log.i(TAG, "query: cursor = " + cursor);
}
public void update() {
int n = contentResolver.update(uri, values, "where", null);
Log.i(TAG, "update: n = " + n);
}
public void delete() {
int n = contentResolver.delete(uri, "where", null);
Log.i(TAG, "delete: n = " + n);
}
}
需要注意一点是,这个UserInfoProvider和UserInfoResolverActivity并没有在同一个应用中,UserInfoProvider在AndroidAdvanceStudy这个应用里面,他的进程名是:cn.codingblock.androidadvancestudy。UserInfoResolverActivity在TestApp应用里面,进程名是:cn.codingblock.testapp。
在testApp点击以上的增、查、改、删按钮,log如下:
12-12 15:06:26.846 28104-28118/cn.codingblock.androidadvancestudy I/UserInfoProvider: insert:
12-12 15:06:28.070 28104-28118/cn.codingblock.androidadvancestudy I/UserInfoProvider: query:
12-12 15:06:29.733 28104-28118/cn.codingblock.androidadvancestudy I/UserInfoProvider: update:
12-12 15:06:30.376 28104-28118/cn.codingblock.androidadvancestudy I/UserInfoProvider: delete:
看,我们是在TestApp应用中做的相关操作,果然可以调用AndroidAdvanceStudy里面的UserInfoProvider。
ContenProvider就是这么简单,当然 ContentProvider 的功能远不止如此,我们也可以结合数据库或者SharePreference等实现更加复杂的对外数据操作。
二、调用系统的ContentProvider
除了我们自己创建ContentProvider,Android系统也给我们提供了丰富的ContentProvider接口,这里就以操作系统的联系人为例来说明一下怎使用系统提供的ContentProvider接口。
- ContactsContract.Contacts.CONTENT_URI:联系人Uri。
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI:联系人电话号码Uri。
1、查询系统联系人
通过系统联系人的Uri获取系统联系人及手机号码:
public void query() {
showContact = "";
// 获取联系人的Cursor集合
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
// 获取联系人id
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
// 获取联系人姓名
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
// 获取当前联系人id下的所有电话号码
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id, null, null);
showContact += "--id="+ id + " | name=" + name + "--\n";
Log.i(TAG, "onCreate: ------id="+ id + " | name=" + name + "------");
while (phones.moveToNext()) {
String phone = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
showContact += "phone = " + phone + "\n";
Log.i(TAG, "onCreate: phone = " + phone);
}
phones.close();
}
cursor.close();
tv_show.setText(showContact);
}
查询后log如下:
12-13 17:19:49.490 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=1 | name=张飞------
12-13 17:19:49.490 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 151 1511 1511
12-13 17:19:49.499 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=2 | name=关羽------
12-13 17:19:49.499 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 153 6969 3869
12-13 17:19:49.499 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 135 6497 8664
12-13 17:19:49.499 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 155 5555 5555
12-13 17:19:49.499 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 133 3333 3333
12-13 17:19:49.508 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=3 | name=刘备------
12-13 17:19:49.508 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 156 9637 5648
12-13 17:19:49.508 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 153 7564 8643
12-13 17:19:49.520 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=14 | name=吕布------
12-13 17:19:49.520 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 15678909087
12-13 17:19:49.529 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=20 | name=孙权------
12-13 17:19:49.529 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 15366669999
12-13 17:19:49.538 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=21 | name=曹操------
12-13 17:19:49.538 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 15699788966
12-13 17:19:49.547 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=22 | name=马超------
12-13 17:19:49.547 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 15366999966
2、添加系统联系人:
public void insert() {
String name = et_name.getText().toString();
String phone = et_phone.getText().toString();
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(phone)) {
Toast.makeText(getApplicationContext(), "不能为空", Toast.LENGTH_LONG).show();
return;
}
ContentValues values = new ContentValues();
// 往插入一个空值以便获取id
Uri rawContactUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
// ------插入联系人姓名------
// 设置id
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
// 设置内容类型
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
// 设置名字
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name);
// 插入姓名
getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
values.clear();
// ------插入联系人电话------
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
Toast.makeText(getApplicationContext(), "添加成功", Toast.LENGTH_LONG).show();
query();
}
在输入框中输入联系人姓名(诸葛亮)及电话号码(13696969696),然后点击添加按钮,log如下:
12-13 17:23:37.577 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=1 | name=张飞------
12-13 17:23:37.577 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 151 1511 1511
12-13 17:23:37.589 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=2 | name=关羽------
12-13 17:23:37.590 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 153 6969 3869
12-13 17:23:37.590 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 135 6497 8664
12-13 17:23:37.590 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 155 5555 5555
12-13 17:23:37.590 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 133 3333 3333
12-13 17:23:37.603 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=3 | name=刘备------
12-13 17:23:37.603 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 156 9637 5648
12-13 17:23:37.603 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 153 7564 8643
12-13 17:23:37.616 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=14 | name=吕布------
12-13 17:23:37.617 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 15678909087
12-13 17:23:37.630 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=20 | name=孙权------
12-13 17:23:37.630 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 15366669999
12-13 17:23:37.657 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=21 | name=曹操------
12-13 17:23:37.657 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 15699788966
12-13 17:23:37.671 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=22 | name=马超------
12-13 17:23:37.671 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 15366999966
12-13 17:23:37.712 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: ------id=23 | name=诸葛亮------
12-13 17:23:37.712 14978-14978/cn.codingblock.androidadvancestudy I/ContactProviderActivity: onCreate: phone = 13696969696
可以看到,诸葛亮和电话号码已经被成功添加进去了。
当然,除了以上几个Uri,系统的ContentProvider接口还有很多很多,就不一一举例了,用到时可以查询官方API。
最后想说的是,本系列文章为博主对Android知识进行再次梳理,查缺补漏的学习过程,一方面是对自己遗忘的东西加以复习重新掌握,另一方面相信在重新学习的过程中定会有巨大的新收获,如果你也有跟我同样的想法,不妨关注我一起学习,互相探讨,共同进步!
参考文献:
- 《Android开发艺术探索》
- 《Android开发进阶从小工到专家》