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

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

Android - 跨应用访问数据实战之 ContentProvider_第1张图片

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

        因此,看过了介绍之后感觉这就是共享数据库(不知道我理解的对不对)。而大部分我们需要的都是读取其他应用,如通讯录等。本文自定义的 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;

    @Override
    public boolean onCreate() {
        Context context = getContext();
        dbHelper = new DBHelper(context);
        database = dbHelper.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 = 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.."
     *
     * @param uri
     * @return
     */
    @Nullable
    @Override
    public 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
    @Override
    public 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);
    }

    @Override
    public 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;
    }

    @Override
    public 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 table
    projection,                        // The columns to return for each row
    selectionClause,                   // Selection criteria
    selectionArgs,                     // Selection criteria
    sortOrder);                        // 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 文件。

Android - 跨应用访问数据实战之 ContentProvider_第2张图片

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);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(SQL_CREATE);
    }

    @Override
    public 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 本身定义的名称。

对了不要忘记权限声明



传送门:ContentProvider demo

你可能感兴趣的:(Android,android,java)