Android 系统提供了三种方式来实现数据持久化(数据存储),即文件存储、SharedPreference 存储、数据库存储,而这三种方式只是程序内部共享与存储数据,要实现跨程序共享数据,就要使用 ContentProvider(内容提供者),将自己的数据共享给其它应用程序,但 ContentProvider 存储数据的方式还是基于前面提到的三种数据持久化技术,本文就使用数据库存储数据。
关于如何使用 SQLite 存储数据,请阅读另一篇文章:Android 数据持久化(SQLite数据存储)
本文源代码下载:http://download.csdn.net/download/xwdoor/9435978
public class BookInfoProvider extends ContentProvider {
public BookInfoProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
}
一个标准的 Uri 的写法是这样的:
content://com.example.app.provider/table1
以路径结尾,表示期望访问 com.example.app 这个应用中的 table1 表中的所有数据。当然,我们也可以访问其中某一条数据:
content://com.example.app.provider/table1/1
以 id 结尾,表示期望访问 com.example.app 这个应用中的 table1 表中的 id 为1的数据。
当然,也可以使用通配符:
UriMatcher 这个类就可以轻松地实现匹配内容 URI 的功能,UriMatcher
中提供了一个 addURI() 方法,这个方法接收三个参数,可以分别把权限、路径和一个自定义代码传进去。当 UriMatcher 匹配成功时,会返回相应的自定义代码,利用这个代码,我们就可以判断出调用方期望访问的是哪张表中的数据了。
在 BookInfoProvider 类中添加以下代码:
public static final String AUTHORITY = "net.xwdoor.provider.bookinfo";
//表示访问 Book 表中的所有数据
public static final int TABLE_BOOK_DIR = 0;
//表示访问 Book 表中的单条数据
public static final int TABLE_BOOK_ITEM = 1;
private static UriMatcher mUriMatcher;
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, BookDbHelper.TABLE_BOOK,TABLE_BOOK_DIR);
mUriMatcher.addURI(AUTHORITY, BookDbHelper.TABLE_BOOK+"/#",TABLE_BOOK_ITEM);
}
ContentProvider 中还有一个 getType() 方法,用于获取 Uri 对象所对应的 MIME 类型,一个内容 URI 所对应的 MIME 字符串主要由三部分组成,Android 对这三个部分有如下规定:
所以,对于 content://com.example.app.provider/table1 这个 Uri,它所对应的 MIME 类型就是:
vnd.android.cursor.dir/vnd.com.example.app.provider.table1
对于 content://com.example.app.provider/table1/1 这个 Uri,对应的 MIME 类型就是:
vnd.android.cursor.dir/vnd.com.example.app.provider.table1
实现 getType() 方法的逻辑后,代码如下:
@Override
public String getType(Uri uri) {
switch (mUriMatcher.match(uri)) {
case TABLE_BOOK_ITEM:
return "vnd.android.cursor.item/" + AUTHORITY + "." + BookDbHelper.TABLE_BOOK;
case TABLE_BOOK_DIR:
return "vnd.android.cursor.dir/" + AUTHORITY + "." + BookDbHelper.TABLE_BOOK;
}
return null;
}
实现跨程序数据共享,完成 ContentProvider 的 CRUD 操作,代码如下:
public class BookInfoProvider extends ContentProvider {
public static final String AUTHORITY = "net.xwdoor.provider.bookinfo";
//表示访问 Book 表中的所有数据
public static final int TABLE_BOOK_DIR = 0;
//表示访问 Book 表中的单条数据
public static final int TABLE_BOOK_ITEM = 1;
private static UriMatcher mUriMatcher;
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, BookDbHelper.TABLE_BOOK, TABLE_BOOK_DIR);
mUriMatcher.addURI(AUTHORITY, BookDbHelper.TABLE_BOOK + "/#", TABLE_BOOK_ITEM);
}
private BookDbHelper mBookDb;
public BookInfoProvider() {
}
@Override
public boolean onCreate() {
mBookDb = new BookDbHelper(getContext(), "BookStore.db", null, 1);
return true;
}
//删除数据
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase writeDb = mBookDb.getWritableDatabase();
int deleteRows = 0;
switch (mUriMatcher.match(uri)) {
case TABLE_BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deleteRows = writeDb.delete(BookDbHelper.TABLE_BOOK, "id=?", new String[]{bookId});
break;
case TABLE_BOOK_DIR:
deleteRows = writeDb.delete(BookDbHelper.TABLE_BOOK,selection,selectionArgs);
break;
}
return deleteRows;
}
//获取 Uri 类型
@Override
public String getType(Uri uri) {
switch (mUriMatcher.match(uri)) {
case TABLE_BOOK_ITEM:
return "vnd.android.cursor.item/" + AUTHORITY + "." + BookDbHelper.TABLE_BOOK;
case TABLE_BOOK_DIR:
return "vnd.android.cursor.dir/" + AUTHORITY + "." + BookDbHelper.TABLE_BOOK;
}
return null;
}
//插入数据
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase writeDb = mBookDb.getWritableDatabase();
Uri newUri = null;
switch (mUriMatcher.match(uri)) {
case TABLE_BOOK_ITEM:
case TABLE_BOOK_DIR:
long newBookId = writeDb.insert(BookDbHelper.TABLE_BOOK,null,values);
newUri = Uri.parse("content://"+AUTHORITY+"/"+BookDbHelper.TABLE_BOOK+"/"+newBookId);
break;
}
return newUri;
}
//查询数据
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase writeDb = mBookDb.getWritableDatabase();
Cursor cursor = null;
switch (mUriMatcher.match(uri)) {
case TABLE_BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = writeDb.query(BookDbHelper.TABLE_BOOK,null,"id=?",new String[]{bookId},null,null,null);
break;
case TABLE_BOOK_DIR:
cursor = writeDb.query(BookDbHelper.TABLE_BOOK,projection,selection,selectionArgs,null,null,sortOrder);
break;
}
return cursor;
}
//更新数据
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase writeDb = mBookDb.getWritableDatabase();
int updateRows = 0;
switch (mUriMatcher.match(uri)) {
case TABLE_BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updateRows = writeDb.update(BookDbHelper.TABLE_BOOK,values,"id=?",new String[]{bookId});
break;
case TABLE_BOOK_DIR:
updateRows = writeDb.update(BookDbHelper.TABLE_BOOK,values,selection,selectionArgs);
break;
}
return updateRows;
}
}
好了,ContentProvider 中的代码全部编写完了,不过离实现跨程序数据共享的功能还差了一小步,接下来就是新建一个 Module:ProviderTest,我们就将通过这个程序去访问 ContentProviderTest 中的数据。
很简单,布局文件中放置四个按钮,分别对应 CRUD 操作,即用于添加、查询、修改和删除数据的,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="net.xwdoor.contentprovidertest.MainActivity">
<Button
android:id="@+id/btn_add_book"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add Book"
android:textAllCaps="false" />
<Button
android:id="@+id/btn_update_book"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update Book's Price By Name"
android:textAllCaps="false" />
<Button
android:id="@+id/btn_delete_book"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete Book By Name"
android:textAllCaps="false" />
<Button
android:id="@+id/btn_query_book"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query Book"
android:textAllCaps="false" />
<TextView
android:id="@+id/tv_books"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
在 MainActivity 中实现代码逻辑,这样就能够正常访问 ContentProvider 了。
private String newsId;
@Override
public void onClick(View view) {
Uri uriDir = Uri.parse("content://net.xwdoor.provider.bookinfo/Book");
switch (view.getId()) {
case R.id.btn_add_book:
//添加书籍
for (Book book : mBooks) {
ContentValues values = new ContentValues();
values.put(Book.BOOK_AUTHOR, book.author);
values.put(Book.BOOK_PRICE, book.price);
values.put(Book.BOOK_PAGES, book.pages);
values.put(Book.BOOK_NAME, book.name);
Uri r = getContentResolver().insert(uriDir, values);
if (r != null) {
newsId = r.getPathSegments().get(1);
}
values.clear();
}
break;
case R.id.btn_update_book:
//更新书籍价格
ContentValues values = new ContentValues();
values.put(Book.BOOK_PRICE, 33.3);
getContentResolver().update(uriDir,values,"name=?",new String[]{mBooks.get(1).name});
break;
case R.id.btn_delete_book:
//删除书籍
getContentResolver().delete(uriDir,"name=?",new String[]{mBooks.get(1).name});
break;
case R.id.btn_query_book:
Uri uri = Uri.parse("content://net.xwdoor.provider.bookinfo/Book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
do {
Book book = new Book();
book.author = cursor.getString(cursor.getColumnIndex(Book.BOOK_AUTHOR));
book.name = cursor.getString(cursor.getColumnIndex(Book.BOOK_NAME));
book.price = cursor.getFloat(cursor.getColumnIndex(Book.BOOK_PRICE));
book.pages = cursor.getInt(cursor.getColumnIndex(Book.BOOK_PAGES));
tvBooks.append(book + "\n");
} while (cursor.moveToNext());
//关键:一定要关闭cursor
cursor.close();
}
break;
}
}
这次算是把知识复习了一遍,一切还算顺利。
源代码下载:http://download.csdn.net/download/xwdoor/9435978