我们知道,一个软件系统的架构通常是这样的:
为了降低上层业务对底层数据的依赖,需要增加一个数据访问层来解耦,我们今天要说的ContentProvider充当的就是数据访问层的角色。ContentProvider提供了一些通用的接口来实现对底层数据(其实是数据库中的表结构数据)进行操作。
ContentProvider是Android提供给上层的一个组件,主要用于实现数据访问的统一管理和数据共享。这里的数据管理是通过定义统一的访问接口来完成,如增删改查。同时,它采用了类似Internet的URL机制,将数据以URI的形式来标识,这样其他App就可以采用一套标准的URI规范来访问同一处数据,而不用关心具体的实现细节。我们知道在Android系统中可能会涉及到一个App的数据被其他App使用的情况,比如通讯录,日历,短信等,这时就需要一套能实现数据共享的机制,这里的ContentProvider就可以提供该功能,其底层使用了binder来完成App进程之间的通信,同时使用匿名共享内存来作为共享数据的载体。当然为了保证数据访问的安全性,ContentProvider还对每处的数据URI增加了权限管理机制,以控制该数据的访问者及访问方式。
ContentProvider提供了一套通用的数据访问接口,示例代码如下:
public class MyContentProvider extends ContentProvider {
public MyContentProvider() {
}
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
}
咋一看,这些接口貌似跟数据库的访问接口类似。没错,ContentProvider就是专门来管理数据库中的数据的。那么既然作为通用的数据访问接口,ContentProvider为什么没有提供对普通文件的访问接口呢?我猜测是因为普通文件的数据是非结构化的,无法抽象出一套统一的访问接口,而数据库中的数据都是结构化数据,就相对比较容易了。
此外一般我们不会直接使用ContentProvider来访问,而是会通过ContentResolver来访问ContentProvider,为什么?因为ContentProvider作为一个数据源处理中心,不止给App内部使用,还会被其他App访问。所以这里的ContentProvider就好比一个server,其他的App在访问这个ContentProvider的时候都必须先获得一个Server的Client才行,这里的ContentResolver就是Android提供给开发者使用来得到一个ContentProvider Client的管理工具类。示例代码如下:
//根据号码获取联系人的姓名
public void getContactNameByNumber() throws Exception {
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, new String[]{ContactsContract.Data.DISPLAY_NAME}, null, null, null);
if(cursor.moveToFirst()){
String name = cursor.getString(0);
Log.i(TAG, name);
}
cursor.close();
}
此外我们在创建一个ContentProvider组件的时候,必须要声明一个authorities,并且必须保证系统内该字段不重名,它就好比URL中的主机地址。示例如下:
<provider
android:name=".MyContentProvider"
android:authorities="com.xiaofei"
android:enabled="true"
android:exported="true">
最后再说下为何ContentProvider在我们平时开发App中使用的不多?其实,虽然ContentProvider具有数据管理和数据共享的功能,但是多半的优势还是集中在数据共享上,在数据共享的前提下,这个数据管理的优势也才能更加明显。像一些系统内置应用,联系人,日历和短信,就比较适合使用ContentProvider,因为他们经常需要给其他App提供数据。而对于像普通App,一般出于安全原因,不会把数据提供给第三方App使用,而在App内部访问数据库的话,完全可以自己实现一套数据库访问框架,因为ContentProvider为了屏蔽数据库的访问细节,实质上是在其基础上再封装了一层接口而已,而对于App内部的数据库访问来说,没有这个必要,而且ContentProvider作为一个组件与系统依赖性较强,相比自己实现一套数据库管理框架,扩展性和灵活性肯定没那么好。当然也有很多第三方开源的数据库框架可用,比如greenDao,ormlite,realm等。