10 KiB
10 KiB
ContentProvider详解
目录
- ContentProvider作用
- URI机制
- ContentResolver使用
- 数据访问权限
- ContentObserver
- ContentProvider实现
- ContentProvider最佳实践
- 面试常见问题
ContentProvider作用
主要作用
- 数据共享:应用间共享数据
- 数据封装:封装数据访问逻辑
- 统一接口:提供统一的数据访问接口
- 权限控制:控制数据访问权限
使用场景
// 场景1:应用间共享数据
// 应用A提供联系人数据
// 应用B访问联系人数据
// 场景2:封装数据访问
// 统一管理数据库访问
// 隐藏数据存储细节
// 场景3:跨进程数据访问
// 通过 Binder 机制跨进程访问
URI机制
URI 格式
// URI 格式
content://authority/path/id
// 示例
content://com.example.provider/user/1
content://com.example.provider/user
content://com.example.provider/user/1/name
URI 组成
// authority:ContentProvider 的标识
// path:数据路径
// id:数据ID(可选)
// 解析 URI
Uri uri = Uri.parse("content://com.example.provider/user/1");
String authority = uri.getAuthority(); // com.example.provider
List<String> pathSegments = uri.getPathSegments(); // [user, 1]
String id = uri.getLastPathSegment(); // 1
URI 匹配
// 使用 UriMatcher 匹配 URI
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI("com.example.provider", "user", USER);
uriMatcher.addURI("com.example.provider", "user/#", USER_ID);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (uriMatcher.match(uri)) {
case USER:
// 查询所有用户
break;
case USER_ID:
// 查询指定用户
String id = uri.getLastPathSegment();
break;
}
}
ContentResolver使用
查询数据
// 查询数据
Uri uri = Uri.parse("content://com.example.provider/user");
Cursor cursor = getContentResolver().query(
uri,
new String[]{"id", "name", "email"}, // projection
"age > ?", // selection
new String[]{"18"}, // selectionArgs
"name ASC" // sortOrder
);
if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String email = cursor.getString(cursor.getColumnIndex("email"));
}
cursor.close();
}
插入数据
// 插入数据
Uri uri = Uri.parse("content://com.example.provider/user");
ContentValues values = new ContentValues();
values.put("name", "John");
values.put("email", "john@example.com");
Uri newUri = getContentResolver().insert(uri, values);
更新数据
// 更新数据
Uri uri = Uri.parse("content://com.example.provider/user/1");
ContentValues values = new ContentValues();
values.put("name", "Jane");
int count = getContentResolver().update(uri, values, null, null);
删除数据
// 删除数据
Uri uri = Uri.parse("content://com.example.provider/user/1");
int count = getContentResolver().delete(uri, null, null);
数据访问权限
权限声明
<!-- AndroidManifest.xml -->
<provider
android:name=".MyProvider"
android:authorities="com.example.provider"
android:exported="true"
android:readPermission="com.example.READ_PERMISSION"
android:writePermission="com.example.WRITE_PERMISSION" />
权限检查
public class MyProvider extends ContentProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// 检查读取权限
if (getContext().checkCallingOrSelfPermission(
"com.example.READ_PERMISSION") != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Permission denied");
}
// 查询数据
return null;
}
}
ContentObserver
ContentObserver 使用
// 监听数据变化
public class MyObserver extends ContentObserver {
public MyObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// 数据变化时调用
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
// 指定 URI 的数据变化
}
}
// 注册观察者
Uri uri = Uri.parse("content://com.example.provider/user");
ContentObserver observer = new MyObserver(new Handler());
getContentResolver().registerContentObserver(uri, true, observer);
// 注销观察者
getContentResolver().unregisterContentObserver(observer);
ContentProvider实现
完整实现
public class UserProvider extends ContentProvider {
private static final String AUTHORITY = "com.example.provider";
private static final String TABLE_USER = "user";
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int USER = 1;
private static final int USER_ID = 2;
static {
uriMatcher.addURI(AUTHORITY, TABLE_USER, USER);
uriMatcher.addURI(AUTHORITY, TABLE_USER + "/#", USER_ID);
}
private SQLiteDatabase database;
@Override
public boolean onCreate() {
DatabaseHelper helper = new DatabaseHelper(getContext());
database = helper.getWritableDatabase();
return database != null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(TABLE_USER);
switch (uriMatcher.match(uri)) {
case USER:
break;
case USER_ID:
queryBuilder.appendWhere("id = " + uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
Cursor cursor = queryBuilder.query(database, projection, selection,
selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
long id = database.insert(TABLE_USER, null, values);
if (id > 0) {
Uri newUri = ContentUris.withAppendedId(uri, id);
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int count;
switch (uriMatcher.match(uri)) {
case USER:
count = database.update(TABLE_USER, values, selection, selectionArgs);
break;
case USER_ID:
String id = uri.getLastPathSegment();
count = database.update(TABLE_USER, values, "id = ?", new String[]{id});
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count;
switch (uriMatcher.match(uri)) {
case USER:
count = database.delete(TABLE_USER, selection, selectionArgs);
break;
case USER_ID:
String id = uri.getLastPathSegment();
count = database.delete(TABLE_USER, "id = ?", new String[]{id});
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case USER:
return "vnd.android.cursor.dir/user";
case USER_ID:
return "vnd.android.cursor.item/user";
default:
return null;
}
}
}
ContentProvider最佳实践
1. 通知数据变化
// 数据变化时通知观察者
getContext().getContentResolver().notifyChange(uri, null);
2. 使用事务
database.beginTransaction();
try {
// 批量操作
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
3. 权限控制
// 检查权限
if (getContext().checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Permission denied");
}
面试常见问题
Q1: ContentProvider 的作用?
答案:
- 数据共享:应用间共享数据
- 数据封装:封装数据访问逻辑
- 统一接口:提供统一的数据访问接口
- 权限控制:控制数据访问权限
Q2: URI 的格式?
答案:
content://authority/path/id
- authority:ContentProvider 标识
- path:数据路径
- id:数据ID(可选)
Q3: ContentResolver 和 ContentProvider 的关系?
答案:
- ContentResolver:客户端,用于访问数据
- ContentProvider:服务端,提供数据访问接口
- 通过 URI 和 Binder 机制通信
Q4: ContentObserver 的作用?
答案:
- 监听 ContentProvider 数据变化
- 数据变化时自动通知
- 实现数据更新通知机制
最后更新:2024年