当前位置: 代码迷 >> 综合 >> Android - 跨应用访问数据实战之 ContentProvider
  详细解决方案

Android - 跨应用访问数据实战之 ContentProvider

热度:79   发布时间:2023-12-29 09:47:12.0

        四大组件之一,但我却没有一次尝试过,只是曾经在文档中看到过这么个东西,为了弥补自己的遗憾,特此记录下本次尝试。 虽然只有查看和添加,但删除和更新的方法也都实现了,并且内容全部写死了,可以通过 Button 的点击事件来查看。CP-demo 可实现增删改查,cp-2只写了查看。demo 地址写在最后。

内容提供程序以一个或多个表的形式将数据呈现给外部应用,这些表与关系型数据库中的表类似。行表示提供程序收集的某种类型数据的实例,行中的每一列表示为一个实例所收集的单个数据。

        因此,看过了介绍之后感觉这就是共享数据库(不知道我理解的对不对)。而大部分我们需要的都是读取其他应用,如通讯录等。本文自定义的 MyProvider 也就是实现对数据库的增删改查。

public class MyProvider extends ContentProvider {private static final String TAG = "MyProvider";private static final int TITTLE = 1;private static final int TITTLE_ID = 2;private static final UriMatcher uriMatcher = getUriMatcher();private static final String DATABASE_TABLE = "test";private static UriMatcher getUriMatcher() {UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(Constants.PROVIDER_NAME, "test", TITTLE);uriMatcher.addURI(Constants.PROVIDER_NAME, "test/#", TITTLE_ID);return uriMatcher;}private DBHelper dbHelper = null;private SQLiteDatabase database = null;@Overridepublic boolean onCreate() {Context context = getContext();dbHelper = new DBHelper(context);database = dbHelper.getWritableDatabase();return true;}@Nullable@Overridepublic Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,@Nullable String[] selectionArgs, @Nullable String sortOrder) {Cursor cursor = null;switch (uriMatcher.match(uri)) {case TITTLE_ID:cursor = database.query(DATABASE_TABLE, projection, "_id = ",new String[]{uri.getPathSegments().get(1)}, null,null, sortOrder);break;case TITTLE:cursor = database.query(DATABASE_TABLE, projection, selection, selectionArgs, null,null, sortOrder);break;default:throw new IllegalArgumentException("Unknown URI " + uri);}return cursor;}/*** 关于MIME类型,android 是这么规定的。* 1.必须以vnd开头* 2.如果是多条记录,后面接android.cursor.dir/,如果是单条记录,后面接android.cursor.item/* 3.最后 加上"vnd.<authority>.<path>"** @param uri* @return*/@Nullable@Overridepublic String getType(@NonNull Uri uri) {switch (uriMatcher.match(uri)) {case TITTLE:return "vnd.android.cursor.dir/vnd.com.flyscale.cp.data";case TITTLE_ID:return "vnd.android.cursor.item/vnd.com.flyscale.cp.data";default:throw new IllegalArgumentException("Unsupported URI: " + uri);}}@Nullable@Overridepublic Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {long rowId = database.insert(DATABASE_TABLE, null, contentValues);if (rowId > 0) {Uri mUri = ContentUris.withAppendedId(uri, rowId);return mUri;}throw new SQLException("Failed to insert row into " + uri);}@Overridepublic int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {int rowIDs;switch (uriMatcher.match(uri)) {case TITTLE_ID:String tittleID = uri.getPathSegments().get(1);rowIDs = database.delete(DATABASE_TABLE, "_id = ", new String[]{tittleID});break;case TITTLE:rowIDs = database.delete(DATABASE_TABLE, selection, selectionArgs);break;default:throw new IllegalArgumentException("Unknown URI " + uri);}return rowIDs;}@Overridepublic int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String selection, @Nullable String[] selectionArgs) {int rowIDs;Log.e(TAG, "query: " + uriMatcher.match(uri));switch (uriMatcher.match(uri)) {case TITTLE_ID:String tittleID = uri.getPathSegments().get(1);rowIDs = database.update(DATABASE_TABLE, contentValues, "_id = ", new String[]{tittleID});break;case TITTLE:rowIDs = database.update(DATABASE_TABLE, contentValues, selection, selectionArgs);break;default:throw new IllegalArgumentException("Unknown URI " + uri);}return rowIDs;}
}

        调用方法的过程涉及到两个类。

ContentValues类和Bundle类很类似,都是使用HashMap的泛型形式来存储的,只能存储基本类型的数据。
ContentResolver,具体的实现过程通过该类。

        

contentValues.put("tittle", "Hello world");
Uri uri = contentResolver.insert(Constants.CONTENT_URI, contentValues);

int cursor = contentResolver.delete(Constants.CONTENT_URI,null, null);

contentValues.clear();
contentValues.put("tittle", "Hello world - 改");
int cursor2 = contentResolver.update(Constants.CONTENT_URI, contentValues, null, null);

Cursor cursor3 = contentResolver.query(Constants.CONTENT_URI,null, null, null, null);
while(cursor3.moveToNext()) {Log.d(TAG, "initView: " + cursor3.getString(cursor3.getColumnIndex("tittle")));
}
cursor3.close();

在官网看到的ContentResolver.query() 方法:

// Queries the user dictionary and returns results
cursor = getContentResolver().query(UserDictionary.Words.CONTENT_URI,   // The content URI of the words tableprojection,                        // The columns to return for each rowselectionClause,                   // Selection criteriaselectionArgs,                     // Selection criteriasortOrder);                        // The sort order for the returned rows

Query() 与 SQL 查询的比较。

query() 参数 SELECT 关键字/参数 备注
Uri FROM table_name Uri 映射至提供程序中名为 table_name 的表。
projection col,col,col,... projection 是检索到的每个行所应包含的列的数组。
selection WHERE col = value selection 指定选择行的条件。
selectionArgs (没有完全等效项,选择参数会替换选择子句中的 ? 占位符。)
sortOrder ORDER BY col,col,... sortOrder 指定在返回的 Cursor 中各行的显示顺序。

        上述的方法通过 contentResolver 调用内容提供器的功能,实质上则是通过 Content Provide 来实现的。在 MyProvider 已实现具体功能。为了方便,表名为 test , 只有一个 tittle。下面的是导出来的 db 文件。

public class DBHelper extends SQLiteOpenHelper {private static final String DATABASE_NAME = "data.db";private static final String TABLE_NAME = "test";private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME +" (_id INTEGER PRIMARY KEY, tittle TEXT )";private static final String SQL_DROP = "DROP TABLE IS EXISTS " + TABLE_NAME ;public DBHelper(Context context) {super(context, DATABASE_NAME, null, 1);}@Overridepublic void onCreate(SQLiteDatabase sqLiteDatabase) {sqLiteDatabase.execSQL(SQL_CREATE);}@Overridepublic void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {sqLiteDatabase.execSQL(SQL_DROP);onCreate(sqLiteDatabase);}public Cursor getData(String id, String[] projection, String selection, String[] selectionArgs, String sortOrder) {SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();sqliteQueryBuilder.setTables(TABLE_NAME);if(id != null) {sqliteQueryBuilder.appendWhere("_id" + " = " + id);}if(TextUtils.isEmpty(sortOrder)) {sortOrder = "tittle";}Cursor cursor = sqliteQueryBuilder.query(getReadableDatabase(),projection,selection,selectionArgs,null,null,sortOrder);return cursor;}
}

 最后需要在清单文件中注册,authorities 是 provider所在的包的名字 + provider 本身定义的名称。

<providerandroid:authorities="com.flyscale.cp.data"android:name=".MyProvider"android:exported="true" />

对了不要忘记权限声明

<uses-permission android:name="android.permission.READ_USER_DICTIONARY"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

传送门:ContentProvider demo

  相关解决方案