Working with ContentProviders

在典型的 Android 结构中,使用 ContentProvider 来访问数据。 ContentProvider 是一种对“提供内容”的抽象。尽管如此,最常见的应用场景还是使用 ContentProvider 来控制 SQLiteDatabase,然后通过 Uri 提供“增删改查”的功能。

(译者:如果你的 App 不需要对外提供数据访问,其实是没必要使用 ContentProvider 的,所以,大部分应用其实用不到 ContentProvider)

在 ContentProvider 的实现中,你可以像在 working with databases 中描述的那样来使用 Cupboard,直接使用 Cupboard 来存取数据。 本章介绍怎样与 ContentProvider 进行操作。

为了操作 ContentProvider,你需要使用 withContext() 方法。

注册 entity/Registering entities

working with databases 描述的一样,同样需要使用 register() 来注册 entity,通常来说,你可以在 ContentProvider 来进行注册。 当然,我们推荐你在 Application 中使用静态初始化块来进行初始化注册。

保存对象/Storing objects

如果要保存一个对象,使用 withContext(context).put() 并传入 ContentProvider 的基础 URI 即可, Cupboard 假定所有的操作都是 REST 风格。 如果你的 Books Uri 是类似 content://com.example.provider/books 的形式, 那么 Cupboard 就假定 id 等于 12 的 book entity 的 uri 是 content://com.example.provider/books/12。

下面演示了如何 put 一个对象:

    static final Uri BOOK_URI = Uri.parse("content://com.example.provider/books");
    Book book = ...
    Uri uri = cupboard().withContext(getContext()).put(BOOK_URI, book);

如果 book 设置了 _id 属性,那么 ContentResolver.insert() 就认为 content://com.example.provider/books/12 的 _id 是12 否则,ContentResolver.insert() 就在 content://com.example.provider/books 上进行操作。

获取对象/Getting objects

为了通过 id 获取一个 entity,你需要为这个entity构建一个 uri

    // The book id is encoded in the Uri
    Uri bookUri = ContentUris.withAppendedId(BOOKS_URI, 12);
    Book book = cupboard().withContext(getContext()).get(bookUri, Book.class);

这将得到 _id 为 12 的 book 对象。如果没有 _id 为 12 的记录,就返回 null。 这背后的操作机制是, ContentResolver.query() 在 uri 上的调用返回一个 cursor,那么第一个结果会作为结果 entity 返回。 然后 cursor 会被关闭。为了查询 books 使用 query():

    // 获取第一条记录
    Book book = cupboard().withContext(getContext()).query(BOOKS_URI, Book.class).get();
    // Get the cursor for this query
    Cursor books = cupboard().withContext(getContext()).query(BOOKS_URI, Book.class).getCursor();
    try {
      // Iterate books
      QueryResultIterable<Book> itr = cupboard().withContext(getContext()).query(BOOKS_URI, Book.class).query();
      for (Book book : itr) {
        // do something with book
      }
    } finally {
      // close the cursor
      itr.close();
    }
    // Get the first matching book with title Android
    Book book = cupboard().withContext(getContext()).query(BOOKS_URI, Book.class).withSelection("title = ?", "Android").get();

query() 的返回值可以是单个 entity,也可以是 entity 的迭代器,或者是一个 Cursor。

更新对象/Updating objects

对比 withDatabase() ,由于我们使用 ContentResolver 来操作(需要 uri),因此没有纯粹的 update 方法。

    // This is standard Android framework code
    ContentValues values = new ContentValues(1);
    values.put("title", "Android")
    // update all books where the title is 'android'
    getContentResolver().update(BOOKS_URI, values, "title = ?", new String[] { "Android" });

删除对象/Deleting objects

使用 delete 来删除一个对象。

    // by passing in the entity, will append book._id to the Uri
    cupboard().withContext(getContext()).delete(BOOKS_URI, book);

如果你需要通过条件删除,那么方式与 update 相同

Tips and tricks

Entity to ContentValues

如果你需要一个 entity 对应的 ContentValues 对象来操作 ContentResolver,可以使用 withEntity() 来进行这种转换。

    ContentValues values = cupboard().withEntity(Book.class).toContentValues(book);
    // you can also reuse ContentValues
    values = cupboard().withEntity(Book.class).toContentValues(book, values);

Cursor to entity or entities

参考 Working with Cursors

Multiple operations

如果你需要对数据库进行多次操作,不想每次都要从 withDatabase() 调用开始,你可以简单的将 DatabaseCompartment 引用保存到一个变量,然后直接使用即可:

    public void doDataStorageWork(Book book) {
        ProviderCompartment pc = cupboard().withContext(getContext())
        pc.put(BOOKS_URI, book);
        Book other = pc.get(ContentUris.withAppendedId(BOOKS_URI, 15), Book.class);
    }