《第一行代码》读书笔记(五)

Author Avatar
wshunli 6月 03, 2018
  • 在其它设备中阅读本文章

《第一行代码》读书笔记 — 应用组件之 ContentProvider

《第一行代码》读书笔记(一)— 平台架构 (第1章)
《第一行代码》读书笔记(二)— 应用组件之 Activity (第2、4章)
《第一行代码》读书笔记(三)— 应用组件之 Service (第10章)
《第一行代码》读书笔记(四)— 应用组件之 BroadcastReceiver (第5章)
《第一行代码》读书笔记(五)— 应用组件之 ContentProvider (第7章)
《第一行代码》读书笔记(六)— 数据存储方案 (第6章)
《第一行代码》读书笔记(七)— 多媒体资源 (第8章)
《第一行代码》读书笔记(八)— 网络编程 (第9章)

第7章 跨程序共享数据

ContentProvider 主要用于不同程序之间实现数据共享的功能,同时保证被访问数据的安全。

目前,使用 ContentProvider 是 Android 实现跨程序共享数据的标准方式。

ContentProvider 有两种用法,一是访问其他程序中的数据,另一种是创建自己的 ContentProvider 让其他程序访问。

访问其他程序中的数据

对于应用程序来说,访问内容提供器中共享的数据,要借助 ContentResolver 类,可以通过 Context 中的 getContentResolver() 方法获取该类的实例。

可以借助以下方法对共享的数据进行 CRUD 操作:

Cursor query(@NonNull Uri uri,
             @Nullable String[] projection,
             @Nullable String selection,
             @Nullable String[] selectionArgs,
             @Nullable String sortOrder)
Uri insert(@NonNull Uri url,
           @Nullable ContentValues values)
int update(@NonNull Uri uri,
           @Nullable ContentValues values,
           @Nullable String where,
           @Nullable String[] selectionArgs)
int delete(@NonNull Uri url,
           @Nullable String where,
           @Nullable String[] selectionArgs)

查询联系人示例:

Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor != null) {
    while (cursor.moveToNext()) {
        String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
        String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
        Log.d(TAG, name + ": " + number);

    }
    cursor.close();
}

创建自己的内容提供器

新建一个类继承 ContentProvider 即可创建自己的内容提供器。

public class MContentProvider extends ContentProvider {

    private static final int TABLE_DIR = 0;
    private static final int TABLE_ITEM = 1;

    private static UriMatcher uriMatcher;
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI("com.wshunli.provider.demo", "table1", TABLE_DIR);
        uriMatcher.addURI("com.wshunli.provider.demo", "table1/#", TABLE_ITEM);
    }

    // 初始化内容提供器时调用。
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        switch (uriMatcher.match(uri)) {
            case TABLE_DIR:
                // 查询 table1 表中的所有数据
                break;
            case TABLE_ITEM:
                // 查询 table1 表中的单挑数据
                break;
        }
        return null;
    }

    // 根据传入的 URI 返回相应的 MIME 类型。
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        switch (uriMatcher.match(uri)) {
            case TABLE_DIR:
                return "vnd.android.cursor.dir/vnd.com.wshunli.provider.demo.table1";
            case TABLE_ITEM:
                return "vnd.android.cursor.item/vnd.com.wshunli.provider.demo.table1";
        }
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

对于 URI 有两种,一种是以路径结尾表示期望访问表中所有数据:

content://com.wshunli.provider.demo/table

另一种以 id 结尾表示期望访问该表中拥有相应 id 的数据:

content://com.wshunli.provider.demo/table/1

1、对于 UriMatcher 匹配 URI

* 表示任意长度的字符,# 表示任意长度的数字。

2、对于返回 MIME 类型,分别使用

表示单行数据:android.cursor.item/

表示多行数据:android.cursor.dir/

其他 insert 、 delete 、 update 方法类似。

实现跨程序数据共享

首先实现自己的 ContentProvider :

public class DatabaseProvider extends ContentProvider {

    public static final int BOOK_DIR = 0;
    public static final int BOOK_ITEM = 1;
    public static final int CATEGORY_DIR = 2;
    public static final int CATEGORY_ITEM = 3;

    public static final String AUTHORITY = "com.wshunli.provider";

    private static UriMatcher uriMatcher;
    private MyDatabaseHelper dbHelper;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }

    @Override
    public boolean onCreate() {
        dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
        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 BOOK_DIR:
                cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("Book", projection, "id = ?", new String[] { bookId }, null, null, sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("Category", projection, "id = ?", new String[] { categoryId }, null, null, sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // 添加数据
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Uri uriReturn = null;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("Book", null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("Category", null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);
                break;
            default:
                break;
        }
        return uriReturn;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // 更新数据
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int updatedRows = 0;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                updatedRows = db.update("Book", values, selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updatedRows = db.update("Book", values, "id = ?", new String[] { bookId });
                break;
            case CATEGORY_DIR:
                updatedRows = db.update("Category", values, selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updatedRows = db.update("Category", values, "id = ?", new String[] { categoryId });
                break;
            default:
                break;
        }
        return updatedRows;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // 删除数据
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int deletedRows = 0;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                deletedRows = db.delete("Book", selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deletedRows = db.delete("Book", "id = ?", new String[] { bookId });
                break;
            case CATEGORY_DIR:
                deletedRows = db.delete("Category", selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deletedRows = db.delete("Category", "id = ?", new String[] { categoryId });
                break;
            default:
                break;
        }
        return deletedRows;
    }

    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.wshunli.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.wshunli.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.wshunli.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.wshunli.provider.category";
        }
        return null;
    }

}

注意 ContentProvider 需要在 Manifest 中声明才能使用。

<provider
    android:name=".MContentProvider"
    android:authorities="com.wshunli.provider.demo"
    android:enabled="true"
    android:exported="true" />

然后就是访问 ContentProvider 了:

private String newId;
// 添加数据
Uri uri = Uri.parse("content://com.wshunli.provider/book");
ContentValues values = new ContentValues();
values.put("name", "A Clash of Kings");
values.put("author", "George Martin");
values.put("pages", 1040);
values.put("price", 55.55);
Uri newUri = getContentResolver().insert(uri, values);
newId = newUri.getPathSegments().get(1);
// 查询数据
Uri uri = Uri.parse("content://com.wshunli.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
    while (cursor.moveToNext()) {
        String name = cursor.getString(cursor. getColumnIndex("name"));
        String author = cursor.getString(cursor. getColumnIndex("author"));
        int pages = cursor.getInt(cursor.getColumnIndex ("pages"));
        double price = cursor.getDouble(cursor. getColumnIndex("price"));
        Log.d("MainActivity", "book name is " + name);
        Log.d("MainActivity", "book author is " + author);
        Log.d("MainActivity", "book pages is " + pages);
        Log.d("MainActivity", "book price is " + price);
    }
    cursor.close();
}
// 更新数据
Uri uri = Uri.parse("content://com.wshunli.provider/book/" + newId);
ContentValues values = new ContentValues();
values.put("name", "A Storm of Swords");
values.put("pages", 1216);
values.put("price", 24.05);
getContentResolver().update(uri, values, null, null);
// 删除数据
Uri uri = Uri.parse("content://com.wshunli.provider/book/" + newId);
getContentResolver().delete(uri, null, null);

也就是将前面的内容结合起来。

参考资料
1、ContentProvider | Android Developers
https://developer.android.com/guide/topics/providers/content-providers
2、Android深入四大组件(五)Content Provider的启动过程 | 刘望舒的博客
http://liuwangshu.cn/framework/component/5-contentprovider-start.html

如果本文对您有所帮助,且您手头还很宽裕,欢迎打赏赞助我,以支付网站服务器和域名费用。 https://paypal.me/wshunli 您的鼓励与支持是我更新的最大动力,我会铭记于心,倾于博客。
本文链接:https://www.wshunli.com/posts/2311b8ab.html