目录
[1.1 什么是ContentProvider?](#1.1 什么是ContentProvider?)
[1.2 ContentProvider架构与工作原理](#1.2 ContentProvider架构与工作原理)
[1.3 Uri 详解](#1.3 Uri 详解)
[1.4 ContentProvider的创建与实现](#1.4 ContentProvider的创建与实现)
[1.5 ContentResolver](#1.5 ContentResolver)
[1.6 通过ContentObserver监听ContentProvider的数据变化](#1.6 通过ContentObserver监听ContentProvider的数据变化)
一、ContentProvider概述
1.1 什么是ContentProvider?
ContentProvider是Android四大组件之一,它为应用程序间的数据共享提供了标准化接口。通过ContentProvider,应用可以将自己的数据安全地暴露给其他应用,同时也可以通过统一的接口访问其他应用的数据。

1.2 ContentProvider架构与工作原理

核心组件解析
ContentResolver :客户端用于访问ContentProvider的接口类
ContentProvider :数据提供者的实现类
ContentValues :键值对集合,用于存储数据
Cursor :查询结果集,类似于数据库的结果集
Uri:统一资源标识符,标识ContentProvider中的数据
1.3 Uri 详解
Uri(Uniform Resource Identifier,统一资源标识符)是Android中用于标识资源的字符串 。在ContentProvider体系中,Uri是访问数据的唯一标识,类似于Web中的URL。
XML
content://com.example.app.provider/table1/123
└──┬──┘ └────────────┬───────────┘└──┬──┘└─┬─┘
协议 授权者(AUTHORITY) 路径 ID
Uri组成部分解析
协议(Scheme):
content://- 访问ContentProvider
file://- 访问文件系统
http://- 网络资源
tel://- 拨号
授权者(Authority):
ContentProvider的唯一标识
格式:
包名.provider示例:
com.example.app.provider
路径(Path):
标识具体的数据表或资源
可包含多级路径
示例:
/users/profile/image
ID:
标识特定记录
可选部分
示例:
/users/123
1.4 ContentProvider的创建与实现
我们很少会自己来定义ContentProvider,因为我们很多时候都不希望自己应用的数据暴露给 其他应用,虽然这样,学习如何ContentProvider还是有必要的,多一种数据传输的方式,是吧~
这是之前画的一个流程图:

创建ContentProvider的步骤
步骤1:定义ContentProvider子类
java
public class MyContentProvider extends ContentProvider {
private static final String TAG = "MyContentProvider";
// 数据库相关
private SQLiteDatabase mDatabase;
private static final String DATABASE_NAME = "myapp.db";
private static final int DATABASE_VERSION = 1;
// 表名
private static final String TABLE_USERS = "users";
// 授权者标识
public static final String AUTHORITY = "com.example.myapp.provider";
// 定义Content URI
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/" + TABLE_USERS);
// 定义MIME类型
private static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.com.example.users";
private static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.com.example.user";
// URI匹配器
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int USERS = 1;
private static final int USER_ID = 2;
static {
sUriMatcher.addURI(AUTHORITY, TABLE_USERS, USERS);
sUriMatcher.addURI(AUTHORITY, TABLE_USERS + "/#", USER_ID);
}
}
步骤2:实现数据库帮助类
java
private static class DatabaseHelper extends SQLiteOpenHelper {
private static final String CREATE_TABLE_USERS =
"CREATE TABLE " + TABLE_USERS + " (" +
UsersColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
UsersColumns.NAME + " TEXT NOT NULL, " +
UsersColumns.EMAIL + " TEXT NOT NULL, " +
UsersColumns.AGE + " INTEGER, " +
UsersColumns.CREATED_AT + " DATETIME DEFAULT CURRENT_TIMESTAMP" +
")";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_USERS);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_USERS);
onCreate(db);
}
}
// 定义列名接口
public interface UsersColumns extends BaseColumns {
String NAME = "name";
String EMAIL = "email";
String AGE = "age";
String CREATED_AT = "created_at";
}
步骤3:实现ContentProvider核心方法
java
@Override
public boolean onCreate() {
DatabaseHelper dbHelper = new DatabaseHelper(getContext());
mDatabase = dbHelper.getWritableDatabase();
return mDatabase != null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(TABLE_USERS);
int match = sUriMatcher.match(uri);
switch (match) {
case USERS:
// 查询所有用户
break;
case USER_ID:
// 根据ID查询单个用户
queryBuilder.appendWhere(UsersColumns._ID + "=" +
uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
Cursor cursor = queryBuilder.query(mDatabase, projection, selection,
selectionArgs, null, null, sortOrder);
// 设置通知URI,当数据变化时通知观察者
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
long rowId;
int match = sUriMatcher.match(uri);
if (match != USERS) {
throw new IllegalArgumentException("Unknown URI: " + uri);
}
// 添加创建时间
if (!values.containsKey(UsersColumns.CREATED_AT)) {
values.put(UsersColumns.CREATED_AT, System.currentTimeMillis());
}
rowId = mDatabase.insert(TABLE_USERS, null, values);
if (rowId > 0) {
Uri newUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
// 通知数据变化
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int count;
int match = sUriMatcher.match(uri);
switch (match) {
case USERS:
count = mDatabase.update(TABLE_USERS, values, selection, selectionArgs);
break;
case USER_ID:
String id = uri.getLastPathSegment();
String where = UsersColumns._ID + "=" + id;
if (selection != null) {
where += " AND (" + selection + ")";
}
count = mDatabase.update(TABLE_USERS, values, where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count;
int match = sUriMatcher.match(uri);
switch (match) {
case USERS:
count = mDatabase.delete(TABLE_USERS, selection, selectionArgs);
break;
case USER_ID:
String id = uri.getLastPathSegment();
String where = UsersColumns._ID + "=" + id;
if (selection != null) {
where += " AND (" + selection + ")";
}
count = mDatabase.delete(TABLE_USERS, where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public String getType(Uri uri) {
int match = sUriMatcher.match(uri);
switch (match) {
case USERS:
return CONTENT_TYPE;
case USER_ID:
return CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
步骤4:在AndroidManifest.xml中注册
XML
<application>
<!-- 声明ContentProvider -->
<provider
android:name=".provider.MyContentProvider"
android:authorities="com.example.myapp.provider"
android:enabled="true"
android:exported="true"
android:readPermission="com.example.myapp.permission.READ_USERS"
android:writePermission="com.example.myapp.permission.WRITE_USERS"
android:grantUriPermissions="true">
<!-- 定义路径权限 -->
<path-permission
android:pathPrefix="/users"
android:permission="com.example.myapp.permission.READ_USERS"
android:readPermission="true" />
<!-- 定义URI权限 -->
<grant-uri-permission android:path="/users/public/*" />
</provider>
<!-- 声明权限 -->
<permission
android:name="com.example.myapp.permission.READ_USERS"
android:protectionLevel="normal" />
<permission
android:name="com.example.myapp.permission.WRITE_USERS"
android:protectionLevel="dangerous" />
</application>
1.5 ContentResolver
其实很多时候我们用到ContentProvider并不是自己暴露自己的数据,更多的时候通过ContentResolver 来读取其他应用的信息,最常用的莫过于读取系统APP,信息,联系人, 多媒体信息等!如果你想来调用这些ContentProvider就需要自行查阅相关的API资料了!
承接上面的例子,可以执行基本的CRUD操作。
java
public class ContentProviderClient {
// ContentProvider的URI
private static final Uri CONTENT_URI =
Uri.parse("content://com.example.myapp.provider/users");
/**
* 查询所有用户
*/
public List<User> queryAllUsers() {
List<User> users = new ArrayList<>();
Cursor cursor = null;
try {
// 获取ContentResolver
ContentResolver resolver = getContentResolver();
// 执行查询
cursor = resolver.query(
CONTENT_URI, // URI
null, // 查询的列
null, // 查询条件
null, // 条件参数
UsersColumns.CREATED_AT + " DESC" // 排序
);
if (cursor != null && cursor.moveToFirst()) {
do {
User user = new User();
user.setId(cursor.getLong(cursor.getColumnIndex(UsersColumns._ID)));
user.setName(cursor.getString(cursor.getColumnIndex(UsersColumns.NAME)));
user.setEmail(cursor.getString(cursor.getColumnIndex(UsersColumns.EMAIL)));
user.setAge(cursor.getInt(cursor.getColumnIndex(UsersColumns.AGE)));
users.add(user);
} while (cursor.moveToNext());
}
} catch (Exception e) {
Log.e("ContentProviderClient", "查询失败", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return users;
}
/**
* 插入用户
*/
public Uri insertUser(User user) {
ContentValues values = new ContentValues();
values.put(UsersColumns.NAME, user.getName());
values.put(UsersColumns.EMAIL, user.getEmail());
values.put(UsersColumns.AGE, user.getAge());
Uri newUri = getContentResolver().insert(CONTENT_URI, values);
return newUri;
}
/**
* 更新用户
*/
public int updateUser(long userId, User user) {
ContentValues values = new ContentValues();
values.put(UsersColumns.NAME, user.getName());
values.put(UsersColumns.EMAIL, user.getEmail());
values.put(UsersColumns.AGE, user.getAge());
Uri userUri = ContentUris.withAppendedId(CONTENT_URI, userId);
return getContentResolver().update(
userUri,
values,
null,
null
);
}
/**
* 删除用户
*/
public int deleteUser(long userId) {
Uri userUri = ContentUris.withAppendedId(CONTENT_URI, userId);
return getContentResolver().delete(userUri, null, null);
}
}
例子:简单的读取收件箱信息(查询数据)
java
private void getMsgs(){
Uri uri = Uri.parse("content://sms/");
ContentResolver resolver = getContentResolver();
//获取的是哪些列的信息
Cursor cursor = resolver.query(uri, new String[]{"address","date","type","body"}, null, null, null);
while(cursor.moveToNext())
{
String address = cursor.getString(0);
String date = cursor.getString(1);
String type = cursor.getString(2);
String body = cursor.getString(3);
System.out.println("地址:" + address);
System.out.println("时间:" + date);
System.out.println("类型:" + type);
System.out.println("内容:" + body);
System.out.println("======================");
}
cursor.close();
}
例子:添加一个新的联系人(插入数据)
java
private void AddContact() throws RemoteException, OperationApplicationException {
//使用事务添加联系人
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri dataUri = Uri.parse("content://com.android.contacts/data");
ContentResolver resolver = getContentResolver();
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
ContentProviderOperation op1 = ContentProviderOperation.newInsert(uri)
.withValue("account_name", null)
.build();
operations.add(op1);
//依次是姓名,号码,邮编
ContentProviderOperation op2 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/name")
.withValue("data2", "Coder-pig")
.build();
operations.add(op2);
ContentProviderOperation op3 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/phone_v2")
.withValue("data1", "13798988888")
.withValue("data2", "2")
.build();
operations.add(op3);
ContentProviderOperation op4 = ContentProviderOperation.newInsert(dataUri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/email_v2")
.withValue("data1", "779878443@qq.com")
.withValue("data2", "2")
.build();
operations.add(op4);
//将上述内容添加到手机联系人中~
resolver.applyBatch("com.android.contacts", operations);
Toast.makeText(getApplicationContext(), "添加成功", Toast.LENGTH_SHORT).show();
}
1.6 通过ContentObserver监听ContentProvider的数据变化
