前言
虽然推荐使用数据库保存结构化的复杂数据,但是数据共享是一个挑战,因为数据库只允许创建它的应用访问。
在Android中共享数据
在Android中,推荐使用content provider方法在不同包之间共享数据。content provider可以被视为一个数据仓库。它如何存储数据与应用如何使用它无关。然而,应用如何使用一致的编程接口在它的里面的数据非常重要。content provider的行为与数据库非常相似——你可以查询它、编辑它的内容、添加或者删除内容。然而,与数据库不同的是,一个content provider可以使用不同的方式存储它的数据。数据可以被存储在数据库中、在文件中、甚至在网络上。
Android提供了许多非常有用的content provider,包括以下几种:
类型 | 说明 |
---|---|
Browser | 存储浏览器数据,比如浏览器的书签、浏览器访问历史等。 |
CallLog | 存储通话数据,比如未接电话、通话详情等。 |
Contacts | 存储通讯录详情。 |
MediaStore | 存储多媒体文件,比如音频、视频和图片。 |
Settings | 存储设备的设置和偏好设置。 |
除了许多内置的content provider以外,可以创建自定义的content provider。
要查询一个content provider,需要以统一的资源标识符(Uniform Resource Identifier,URI)的形式制定查询字符串,以及一个可选的特定行说明符。以下是查询URI的形式:
<standard_prefix>://<authority>/<data_path>/<id>
URI的各种组成部分如下:
组成 | 说明 |
---|---|
content provider | content provider的标准前缀是content:// 。 |
authority | 指定content provider的名称。例如内置Contacts的content provider的名称为contacts。对于第三方content provider来说,它可以是完整的合法名称,例如com.wrox.provider。 |
data_path | 指定请求的数据种类。例如,如果你要从Contacts的content provider中获取所有的通讯录,那么data_path应该是people,URI将类似与:content://contacts/people 。 |
id | 指定请求特定的记录。例如,如果你要获取Contacts的content provider中第2条通讯录,URI将类似类似于:content://contact/people/2 。 |
查询字符串 | 描述 |
---|---|
content://media/internal/images | 返回设备上保存在内部存储中的图片列表。 |
content://media/external/images | 返回设备上保存在外部存储(例如SD卡)中的图片列表。 |
content://call_log/calls | 返回登记在通话记录中的通话记录。 |
content://browser/bookmarks | 返回存储在浏览器中的书签列表。 |
使用content provider
理解content provider的最佳方法是在实践中使用它。
Main2Activity
public class Main2Activity extends ListActivity {
final private int REQUEST_READ_CONTACTS = 123;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},
REQUEST_READ_CONTACTS);
} else {
listContacts();
}
}
@Override
public void onRequestPermissionsResult(int requestCode
, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_READ_CONTACTS:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
listContacts();
} else {
Toast.makeText(Main2Activity.this
, "Permission Denied", Toast.LENGTH_SHORT).show();
}
break;
default:
super.onRequestPermissionsResult(requestCode
, permissions, grantResults);
}
}
protected void listContacts() {
Uri allContacts = Uri.parse("content://contacts/people");
CursorLoader cursorLoader = new CursorLoader(
this,
allContacts,
null,
null,
null,
null);
Cursor cursor = cursorLoader.loadInBackground();
String[] columns = new String[]{
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts._ID};
int[] views = new int[]{R.id.contactName, R.id.contactID};
SimpleCursorAdapter adapter;
adapter = new SimpleCursorAdapter(
this, R.layout.activity_main2, cursor, columns, views,
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
this.setListAdapter(adapter);
}
}
本示例获取了存储在Contacts应用中的通讯录并且将其显示在ListView中。
首先指定访问Contacts应用的URI:
Uri allContacts = Uri.parse("content://contacts/people");
其次,检查应用是否由访问Contacts的权限:
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},
REQUEST_READ_CONTACTS);
} else {
listContacts();
}
如果没有访问权限,就会发出一条权限请求(使Android弹出一个权限请求对话框)。如果应用由相应的访问权限,ListContacts()方法会被调用。
getContentResolver()方法返回一个ContentResolver对象,它会使用适当content provider解析内容URI。
CursorLoader类(Android API level 11及以上版本可用)在后再线程中执行cursor查询操作,因此不会阻塞应用UI。
CursorLoader cursorLoader = new CursorLoader(
this,
allContacts,
null,
null,
null,
null);
Cursor cursor = cursorLoader.loadInBackground();
SimpleCursorAdapter对象将一个Cursor数据映射到XML文件(activity_main2.xml)中定义的TextView(或者ImageView)。它将数据(对应于代码中的columns变量)映射到视图(对应于代码中的view变量)中:
String[] columns = new String[]{
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts._ID
};
int[] views = new int[]{
R.id.contactName, R.id.contactID
};
SimpleCursorAdapter adapter = new SimpleCursorAdapter(
this, R.layout.activity_main2, cursor, columns, views,
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
this.setListAdapter(adapter);
类似于managedQuery()方法,SimpleCursorAdapter类的一个构造函数已经被弃用。对于运行Honeycomb及后续版本的设备,需要使用新的SimpleCursorAdapter类的构造函数,与旧版本相比,该构造函数多一个参数:
SimpleCursorAdapter adapter = new SimpleCursorAdapter(