1)谈谈你对ContentProvider的理解
说说ContentProvider、ContentResolver、ContentObserver 之间的关系
2)请介绍下ContentProvider 是如何实现数据共享的?
3)ContentProvider的权限管理(解答:读写分离,权限控制-精确到表级,URL控制)
4)Android系统为什么会设计ContentProvider?
首先,官网的基本链接:
https://developer.android.com/guide/topics/providers/content-provider-basics
https://developer.android.com/guide/topics/providers/content-provider-creating
一. 对于ContentProvider的理解
ContentProvider 内容提供者,用于对外提供数据
ContentResolver.notifyChange(uri)发出消息
ContentResolver 内容解析者, 用于获取内容提供者提供的数据
ContentObserver 内容***, 可以监听数据的改变状态
ContentResolver.registerContentObserver() 监听消息
设计ContentProvider的主要目的是供其他应用使用。不同应用可共享存取数据,这些应用使用提供程序客户端对象来访问提供程序。提供程序与提供程序客户端共同提供一致的标准数据接口,该接口还可处理跨进程通信并保护数据访问的安全性。
应用从具有 ContentResolver 客户端对象的内容提供程序访问数据。此对象具有调用提供程序对象ContentProvider的某个具体子类的实例)中同名方法的方法。 ContentResolver 方法可提供持续存储的基本“CRUD”(创建、检索、更新和删除)功能。供开发者调用的 (如视频,音频,图片,通讯录等)
客户端应用进程中的 ContentResolver 对象和拥有提供程序的应用中的 ContentProvider 对象可自动处理跨进程通信。 ContentProvider 还可充当其数据存储区和表格形式的数据外部显示之间的抽象层。
公共的URI
- 每个ContentProvider都有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。
- Android所提供的ContentProvider都存放在android.provider包当中。
二. 实现步骤
1. 定义一个 CONTENT_URI 常量
// CONTENT_URI 的字符串必须是唯一
public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentprovider");
// 如果有子表,URI为:
public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentProvider/users");
2. 定义一个继承自ContentProvider的类
public class MyContentProvider extends ContentProvider { ... }
3. 实现ContentProvider的所有方法
先写一个常量类
public class MyContentProviderMetaData {
//URI的指定,此处的字符串必须和声明的authorities一致
public static final String AUTHORITIES = "com.zhuanghongji.app.MyContentProvider";
//数据库名称
public static final String DATABASE_NAME = "myContentProvider.db";
//数据库的版本
public static final int DATABASE_VERSION = 1;
//表名
public static final String USERS_TABLE_NAME = "user";
public static final class UserTableMetaData implements BaseColumns{
//表名
public static final String TABLE_NAME = "user";
//访问该ContentProvider的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");
//该ContentProvider所返回的数据类型的定义
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.myprovider.user";
public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.myprovider.user";
//列名
public static final String USER_NAME = "name";
//默认的排序方法
public static final String DEFAULT_SORT_ORDER = "_id desc";
}
}
public class MyContentProvider extends ContentProvider {
//访问表的所有列
public static final int INCOMING_USER_COLLECTION = 1;
//访问单独的列
public static final int INCOMING_USER_SINGLE = 2;
//操作URI的类
public static final UriMatcher uriMatcher;
//为UriMatcher添加自定义的URI
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user",
INCOMING_USER_COLLECTION);
uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user/#",
INCOMING_USER_SINGLE);
}
private DatabaseHelper dh;
//为数据库表字段起别名
public static HashMap userProjectionMap;
static
{
userProjectionMap = new HashMap();
userProjectionMap.put(UserTableMetaDa***serTableMetaData._ID);
userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);
}
/**
* 删除表数据
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
System.out.println("delete");
//得到一个可写的数据库
SQLiteDatabase db = dh.getWritableDatabase();
//执行删除,得到删除的行数
int count = db.delete(UserTableMetaData.TABLE_NAME, selection, selectionArgs);
return count;
}
/**
* 数据库访问类型
*/
@Override
public String getType(Uri uri) {
System.out.println("getType");
//根据用户请求,得到数据类型
switch (uriMatcher.match(uri)) {
case INCOMING_USER_COLLECTION:
return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE;
case INCOMING_USER_SINGLE:
return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;
default:
throw new IllegalArgumentException("UnKnown URI"+uri);
}
}
/**
* 插入数据
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
//得到一个可写的数据库
SQLiteDatabase db = dh.getWritableDatabase();
//向指定的表插入数据,得到返回的Id
long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values);
if(rowId > 0){ // 判断插入是否执行成功
//如果添加成功,利用新添加的Id和
Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);
//通知***,数据已经改变
getContext().getContentResolver().notifyChange(insertedUserUri, null);
return insertedUserUri;
}
return uri;
}
/**
* 创建ContentProvider时调用的回调函数
*/
@Override
public boolean onCreate() {
System.out.println("onCreate");
//得到数据库帮助类
dh = new DatabaseHelper(getContext(),MyContentProviderMetaData.DATABASE_NAME);
return false;
}
/**
* 查询数据库
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//创建一个执行查询的Sqlite
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
//判断用户请求,查询所有还是单个
switch(uriMatcher.match(uri)){
case INCOMING_USER_COLLECTION:
//设置要查询的表名
qb.setTables(UserTableMetaData.TABLE_NAME);
//设置表字段的别名
qb.setProjectionMap(userProjectionMap);
break;
case INCOMING_USER_SINGLE:
qb.setTables(UserTableMetaData.TABLE_NAME);
qb.setProjectionMap(userProjectionMap);
// 追加条件,getPathSegments()得到用户请求的Uri地址截取的数组,
// get(1)得到去掉地址中/以后的第二个元素
qb.appendWhere(UserTableMetaDa***ri.getPathSegments().get(1));
break;
}
//设置排序
String orderBy;
if(TextUtils.isEmpty(sortOrder)){
orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
//得到一个可读的数据库
SQLiteDatabase db = dh.getReadableDatabase();
//执行查询,把输入传入
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
//设置监听
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
/**
* 更新数据库
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
System.out.println("update");
//得到一个可写的数据库
SQLiteDatabase db = dh.getWritableDatabase();
//执行更新语句,得到更新的条数
int count = db.update(UserTableMetaData.TABLE_NAME, values, selection, selectionArgs);
return count;
}
}
4. 在AndroidMinifest.xml中进行声明
<provider
android:name=".MyContentProvider"
android:authorities="com.zhuanghongji.app.MyContentProvider" />