深入分析 Android ContentProvider (一)
1. Android 中的 ContentProvider 设计阐明
ContentProvider 是 Android 中四大组件之一,重要用于在不同应用之间共享数据。ContentProvider 提供了一个一致的接口,使得应用能够以一种受控和安全的方式访问和修改存储的数据。通过 ContentProvider,数据可以被跨进程共享,而不必将数据直接暴露给其他应用。
1.1. ContentProvider 的设计初衷
- 数据共享:在 Android 中,应用之间的直接数据访问是不允许的。ContentProvider 提供了一种标准化的方式,使得应用可以安全地共享数据。
- 数据封装:通过 ContentProvider,数据存取逻辑可以封装在一个单独的组件中,其他组件只需通过 ContentProvider 提供的接口举行数据操作。
- 同一接口:ContentProvider 提供了一个同一的接口,支持多种数据存取方式(如插入、删除、更新、查询),而且支持对数据举行事务操作。
1.2. ContentProvider 的基本结构
一个典型的 ContentProvider 包括以下几个部分:
- URI:同一资源标识符,用于定位 ContentProvider 中的数据。
- MIME 类型:用于标识返回的数据类型。
- 数据存储:实际存储数据的地方,如数据库、文件等。
- 数据操作方法:用于操作数据的标准方法,如 query、insert、update 和 delete。
1.3. ContentProvider 的实现
要实现一个 ContentProvider,须要继承 ContentProvider 类并实现其抽象方法。以下是一个简单的示例:
示例:实现一个简单的 ContentProvider
首先,须要创建一个 SQLite 数据库来存储数据。
- public class DatabaseHelper extends SQLiteOpenHelper {
- private static final String DATABASE_NAME = "example.db";
- private static final int DATABASE_VERSION = 1;
- public static final String TABLE_NAME = "example";
- public static final String COLUMN_ID = "_id";
- public static final String COLUMN_NAME = "name";
- private static final String TABLE_CREATE =
- "CREATE TABLE " + TABLE_NAME + " (" +
- COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
- COLUMN_NAME + " TEXT);";
- public DatabaseHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- }
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL(TABLE_CREATE);
- }
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
- onCreate(db);
- }
- }
复制代码 实现 ContentProvider 类并覆盖其方法。
- public class ExampleProvider extends ContentProvider {
- private static final String AUTHORITY = "com.example.provider";
- private static final String BASE_PATH = "example";
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
- private SQLiteDatabase database;
- private static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.example.example";
- private static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.example.example";
- private static final int EXAMPLES = 1;
- private static final int EXAMPLE_ID = 2;
- private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- static {
- uriMatcher.addURI(AUTHORITY, BASE_PATH, EXAMPLES);
- uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", EXAMPLE_ID);
- }
- @Override
- public boolean onCreate() {
- DatabaseHelper helper = new DatabaseHelper(getContext());
- database = helper.getWritableDatabase();
- return true;
- }
- @Nullable
- @Override
- public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
- @Nullable String[] selectionArgs, @Nullable String sortOrder) {
- Cursor cursor;
- switch (uriMatcher.match(uri)) {
- case EXAMPLES:
- cursor = database.query(DatabaseHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
- break;
- case EXAMPLE_ID:
- cursor = database.query(DatabaseHelper.TABLE_NAME, projection, DatabaseHelper.COLUMN_ID + "=?",
- new String[]{String.valueOf(ContentUris.parseId(uri))}, null, null, sortOrder);
- break;
- default:
- throw new IllegalArgumentException("Unknown URI: " + uri);
- }
- cursor.setNotificationUri(getContext().getContentResolver(), uri);
- return cursor;
- }
- @Nullable
- @Override
- public String getType(@NonNull Uri uri) {
- switch (uriMatcher.match(uri)) {
- case EXAMPLES:
- return CONTENT_TYPE;
- case EXAMPLE_ID:
- return CONTENT_ITEM_TYPE;
- default:
- throw new IllegalArgumentException("Unknown URI: " + uri);
- }
- }
- @Nullable
- @Override
- public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
- long id = database.insert(DatabaseHelper.TABLE_NAME, null, values);
- if (id > 0) {
- Uri returnUri = ContentUris.withAppendedId(CONTENT_URI, id);
- getContext().getContentResolver().notifyChange(returnUri, null);
- return returnUri;
- }
- throw new SQLException("Failed to insert row into " + uri);
- }
- @Override
- public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
- int rowsDeleted;
- switch (uriMatcher.match(uri)) {
- case EXAMPLES:
- rowsDeleted = database.delete(DatabaseHelper.TABLE_NAME, selection, selectionArgs);
- break;
- case EXAMPLE_ID:
- rowsDeleted = database.delete(DatabaseHelper.TABLE_NAME, DatabaseHelper.COLUMN_ID + "=?",
- new String[]{String.valueOf(ContentUris.parseId(uri))});
- break;
- default:
- throw new IllegalArgumentException("Unknown URI: " + uri);
- }
- if (rowsDeleted > 0) {
- getContext().getContentResolver().notifyChange(uri, null);
- }
- return rowsDeleted;
- }
- @Override
- public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
- int rowsUpdated;
- switch (uriMatcher.match(uri)) {
- case EXAMPLES:
- rowsUpdated = database.update(DatabaseHelper.TABLE_NAME, values, selection, selectionArgs);
- break;
- case EXAMPLE_ID:
- rowsUpdated = database.update(DatabaseHelper.TABLE_NAME, values, DatabaseHelper.COLUMN_ID + "=?",
- new String[]{String.valueOf(ContentUris.parseId(uri))});
- break;
- default:
- throw new IllegalArgumentException("Unknown URI: " + uri);
- }
- if (rowsUpdated > 0) {
- getContext().getContentResolver().notifyChange(uri, null);
- }
- return rowsUpdated;
- }
- }
复制代码
- 在 Manifest 中声明 ContentProvider
- <provider
- android:name=".ExampleProvider"
- android:authorities="com.example.provider"
- android:exported="true" />
复制代码 1.4. ContentProvider 的使用
- Uri uri = Uri.parse("content://com.example.provider/example");
- Cursor cursor = getContentResolver().query(uri, null, null, null, null);
- if (cursor != null) {
- while (cursor.moveToNext()) {
- String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
- // 处理数据
- }
- cursor.close();
- }
复制代码- ContentValues values = new ContentValues();
- values.put("name", "Example Name");
- Uri uri = Uri.parse("content://com.example.provider/example");
- Uri newUri = getContentResolver().insert(uri, values);
复制代码- ContentValues values = new ContentValues();
- values.put("name", "Updated Name");
- Uri uri = Uri.parse("content://com.example.provider/example/1");
- int rowsUpdated = getContentResolver().update(uri, values, null, null);
复制代码- Uri uri = Uri.parse("content://com.example.provider/example/1");
- int rowsDeleted = getContentResolver().delete(uri, null, null);
复制代码 2. ContentProvider 的设计优势
- 数据共享:提供了标准化的数据共享接口,使得不同应用可以安全地共享数据。
- 数据封装:将数据存取逻辑封装在 ContentProvider 中,低落了模块间的耦合度。
- 同一访问:通过同一的 URI 和接口访问数据,简化了数据操作。
- 跨进程通信:支持跨进程访问数据,使得应用之间可以实现复杂的数据交互。
3. ContentProvider 的留意事项
- 性能考虑:由于 ContentProvider 通常用于跨进程数据共享,频繁的跨进程调用会有性能开销,需留意优化查询和数据操作。
- 权限管理:确保数据访问权限的安全性,通过权限声明和查抄,防止数据泄漏和未经授权的访问。
- 数据一致性:确保在高并发场景下的数据一致性,须要时使用事务机制。
4. 总结
ContentProvider 是 Android 中用于数据共享和跨进程通信的重要组件,通过同一的接口和标准化的 URI 访问方式,提供了安全、封装和
欢迎点赞|关注|收藏|批评,您的肯定是我创作的动力 |
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |