在Android应用开发中,跨进程数据共享是常见需求,比如多个应用需要读取系统的通讯录、日程数据,或者自己的多个应用之间需要共享业务数据。ContentProvider作为Android官方提供的跨进程数据共享组件,能够很好地满足这类需求,既保证了数据访问的安全性,又简化了跨进程通信的复杂度。

什么是ContentProvider
ContentProvider是Android四大组件之一,它的核心作用是向其他应用暴露结构化的数据,同时隐藏数据的存储细节。其他应用不需要知道数据是用SQLite存储、文件存储还是网络存储,只需要通过统一的接口就能访问数据,并且ContentProvider自带权限控制,能够避免数据被随意访问。
ContentProvider核心概念
URI
URI是访问ContentProvider数据的唯一标识,格式通常为content://authority/path,其中authority是ContentProvider的唯一标识,一般是应用的包名加组件名,path表示要访问的数据路径,比如content://com.example.provider/user表示访问用户数据。
ContentResolver
其他应用需要通过ContentResolver来访问ContentProvider暴露的数据,系统会负责找到对应的ContentProvider并转发请求,开发者不需要直接和ContentProvider打交道,只需要调用ContentResolver的增删改查方法即可。
数据类型
ContentProvider支持返回多种类型的数据,常见的有文本、图片、结构化数据等,还可以通过MIME类型告知访问方返回的数据格式,方便访问方做对应的处理。
自定义ContentProvider基础步骤
要实现自定义ContentProvider,通常需要以下步骤:
- 创建类继承ContentProvider,实现onCreate、query、insert、update、delete、getType这几个核心方法
- 在AndroidManifest.xml中注册ContentProvider,声明authority和访问权限
- 定义URI匹配规则,处理不同的访问请求
- 在方法中实现对应的数据操作逻辑,比如操作SQLite数据库
实战:自定义ContentProvider暴露用户数据
下面我们通过一个完整的案例,演示如何自定义ContentProvider暴露本地的用户数据,假设我们使用SQLite存储用户数据。
1. 定义数据库帮助类
首先创建SQLite数据库帮助类,用于管理用户数据的存储:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class UserDbHelper extends SQLiteOpenHelper {
// 数据库名称
private static final String DB_NAME = "user.db";
// 数据库版本
private static final int DB_VERSION = 1;
// 用户表名称
public static final String TABLE_USER = "user";
// 用户表字段
public static final String COLUMN_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_AGE = "age";
// 创建用户表的SQL语句
private static final String CREATE_TABLE_USER = "CREATE TABLE " + TABLE_USER + " (" +
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_NAME + " TEXT, " +
COLUMN_AGE + " INTEGER)";
public UserDbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 创建用户表
db.execSQL(CREATE_TABLE_USER);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 升级时删除旧表重建,实际项目需要做数据迁移
db.execSQL("DROP TABLE IF EXISTS " + TABLE_USER);
onCreate(db);
}
}2. 自定义ContentProvider实现
接下来创建自定义ContentProvider,实现数据的增删改查逻辑:
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class UserContentProvider extends ContentProvider {
// 唯一标识authority,一般是包名加provider
private static final String AUTHORITY = "com.example.provider.user";
// URI匹配码,匹配整个用户表
private static final int USER_DIR = 0;
// URI匹配码,匹配单个用户
private static final int USER_ITEM = 1;
// URI匹配器
private static UriMatcher uriMatcher;
// 数据库帮助类
private UserDbHelper dbHelper;
static {
// 初始化URI匹配器
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加匹配规则,content://com.example.provider.user/user 匹配整个表
uriMatcher.addURI(AUTHORITY, "user", USER_DIR);
// 添加匹配规则,content://com.example.provider.user/user/# 匹配单个用户,#表示数字id
uriMatcher.addURI(AUTHORITY, "user/#", USER_ITEM);
}
@Override
public boolean onCreate() {
// 初始化数据库帮助类
dbHelper = new UserDbHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case USER_DIR:
// 查询整个用户表
cursor = db.query(UserDbHelper.TABLE_USER, projection, selection, selectionArgs, null, null, sortOrder);
break;
case USER_ITEM:
// 查询单个用户,获取uri中的id
String id = uri.getPathSegments().get(1);
cursor = db.query(UserDbHelper.TABLE_USER, projection, UserDbHelper.COLUMN_ID + "=?", new String[]{id}, null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("未知URI:" + uri);
}
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri returnUri = null;
switch (uriMatcher.match(uri)) {
case USER_DIR:
// 插入数据,返回插入行的uri
long newId = db.insert(UserDbHelper.TABLE_USER, null, values);
returnUri = ContentUris.withAppendedId(uri, newId);
break;
default:
throw new IllegalArgumentException("未知URI:" + uri);
}
return returnUri;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int updateRows = 0;
switch (uriMatcher.match(uri)) {
case USER_DIR:
// 更新整个表符合条件的行
updateRows = db.update(UserDbHelper.TABLE_USER, values, selection, selectionArgs);
break;
case USER_ITEM:
// 更新单个用户
String id = uri.getPathSegments().get(1);
updateRows = db.update(UserDbHelper.TABLE_USER, values, UserDbHelper.COLUMN_ID + "=?", new String[]{id});
break;
default:
throw new IllegalArgumentException("未知URI:" + uri);
}
return updateRows;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deleteRows = 0;
switch (uriMatcher.match(uri)) {
case USER_DIR:
// 删除整个表符合条件的行
deleteRows = db.delete(UserDbHelper.TABLE_USER, selection, selectionArgs);
break;
case USER_ITEM:
// 删除单个用户
String id = uri.getPathSegments().get(1);
deleteRows = db.delete(UserDbHelper.TABLE_USER, UserDbHelper.COLUMN_ID + "=?", new String[]{id});
break;
default:
throw new IllegalArgumentException("未知URI:" + uri);
}
return deleteRows;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case USER_DIR:
// 返回整个用户表的MIME类型
return "vnd.android.cursor.dir/vnd.com.example.provider.user";
case USER_ITEM:
// 返回单个用户的MIME类型
return "vnd.android.cursor.item/vnd.com.example.provider.user";
default:
return null;
}
}
}3. 注册ContentProvider
在AndroidManifest.xml的application标签内注册ContentProvider,声明authority和权限:
<provider
android:name=".UserContentProvider"
android:authorities="com.example.provider.user"
android:exported="true" />这里android:exported设置为true表示允许其他应用访问,实际项目中可以根据需要设置权限,比如添加android:permission属性限制只有拥有对应权限的应用才能访问。
其他进程访问ContentProvider数据
在其他应用中,只需要通过ContentResolver就能访问上面暴露的用户数据,以下是访问示例:
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class AccessUserActivity extends AppCompatActivity {
// 对应ContentProvider的URI
private static final String USER_URI = "content://com.example.provider.user/user";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ContentResolver resolver = getContentResolver();
// 插入用户数据
ContentValues values = new ContentValues();
values.put("name", "张三");
values.put("age", 25);
Uri insertUri = resolver.insert(Uri.parse(USER_URI), values);
// 查询用户数据
Cursor cursor = resolver.query(Uri.parse(USER_URI), null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
int age = cursor.getInt(cursor.getColumnIndexOrThrow("age"));
// 处理查询到的数据
}
cursor.close();
}
// 更新用户数据
ContentValues updateValues = new ContentValues();
updateValues.put("age", 26);
resolver.update(Uri.parse(USER_URI), updateValues, "name=?", new String[]{"张三"});
// 删除用户数据
resolver.delete(Uri.parse(USER_URI), "name=?", new String[]{"张三"});
}
}注意事项
- ContentProvider的onCreate方法运行在主线程,不要做耗时操作,数据操作建议放在子线程中处理
- 跨进程访问ContentProvider时,query返回的Cursor需要记得关闭,避免内存泄漏
- 如果不需要暴露数据给其他应用,建议将android:exported设置为false,减少安全风险
- URI匹配规则要写准确,避免错误的请求被处理
通过上面的内容,相信你已经掌握了ContentProvider的基本使用和跨进程数据共享的实现方式,在实际项目中可以根据需求灵活调整,比如添加权限控制、处理更复杂的数据操作逻辑等。
ContentProviderAndroid跨进程通信数据共享URIContentResolver修改时间:2026-05-31 06:00:14