本文的合集已經編著成書读规,高級Android開發(fā)強化實戰(zhàn)驳规,歡迎各位讀友的建議和指導硫惕。在京東即可購買:https://item.jd.com/12385680.html
ContentProvider主要應用于進程間數(shù)據(jù)共享. 對于應用而言, 多進程并不會經常使用, 因而較少使用ContentProvider, 是最不常見的四大組件(Activity, Service, BroadcastReceiver, ContentProvider). 但是其優(yōu)異的性能與便捷, 對于多應用共享數(shù)據(jù)而言, 非常重要, 比如共享同一份計步數(shù)據(jù)等. 開發(fā)者只有掌握多種技能, 才能在開發(fā)中游刃有余, 用最優(yōu)的方式完成項目, 提升應用性能, 間接提高用戶體驗. 本文借用Demo, 講解ContentProvider共享數(shù)據(jù)的要點.
本文源碼的GitHub下載地址
SQLite
ContentProvider需要媒介進行數(shù)據(jù)存儲, 最常用的就是SQLite數(shù)據(jù)庫.
SQLite數(shù)據(jù)庫繼承SQLiteOpenHelper
類, 提供數(shù)據(jù)庫名稱, 表名, 版本. 在onCreate方法中, 創(chuàng)建數(shù)據(jù)庫表, 添加字段.
本示例使用兩張表, 書籍和用戶.
public class DbOpenHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "book_provider.db";
public static final String BOOK_TABLE_NAME = "book";
public static final String USER_TABLE_NAME = "user";
private static final int DB_VERSION = 1;
private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "
+ BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY, name TEXT)";
private String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS "
+ USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY, name TEXT, sex INT)";
public DbOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_TABLE);
db.execSQL(CREATE_USER_TABLE);
}
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
直接使用數(shù)據(jù)庫的情況較少, 也比較復雜, 推薦使用一些經典ORM數(shù)據(jù)庫, 如Sugar等, 簡化管理. ORM, 即對象關系映射.
ContentProvider
ContentProvider提供數(shù)據(jù)訪問的接口, CRUD增刪改查. 在onCreate中, 初始化數(shù)據(jù)庫, 并添加數(shù)據(jù).
@Override public boolean onCreate() {
showLogs("onCreate 當前線程: " + Thread.currentThread().getName());
mContext = getContext();
initProviderData(); // 初始化Provider數(shù)據(jù)
return false;
}
private void initProviderData() {
mDb = new DbOpenHelper(mContext).getWritableDatabase();
mDb.execSQL("delete from " + DbOpenHelper.BOOK_TABLE_NAME);
mDb.execSQL("delete from " + DbOpenHelper.USER_TABLE_NAME);
mDb.execSQL("insert into book values(3,'Android');");
mDb.execSQL("insert into book values(4, 'iOS');");
mDb.execSQL("insert into book values(5, 'HTML5');");
mDb.execSQL("insert into user values(1, 'Spike', 1);");
mDb.execSQL("insert into user values(2, 'Wang', 0);");
}
CRUD的參數(shù)是Uri, 數(shù)據(jù)庫需要使用表名, 為了便于從Uri映射到表名, 使用關系轉換.
private String getTableName(Uri uri) {
String tableName = null;
switch (sUriMatcher.match(uri)) {
case BOOK_URI_CODE:
tableName = DbOpenHelper.BOOK_TABLE_NAME;
break;
case USER_URI_CODE:
tableName = DbOpenHelper.USER_TABLE_NAME;
break;
default:
break;
}
return tableName;
}
添加數(shù)據(jù)insert
, 可以注冊內容改變的監(jiān)聽, 插入數(shù)據(jù)時, 廣播更新, 即notifyChange
.
@Nullable @Override public Uri insert(Uri uri, ContentValues values) {
showLogs("insert");
String table = getTableName(uri);
if (TextUtils.isEmpty(table)) {
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
mDb.insert(table, null, values);
// 插入數(shù)據(jù)后通知改變
mContext.getContentResolver().notifyChange(uri, null);
return null;
}
刪除數(shù)據(jù)delete
, 返回刪除數(shù)據(jù)的數(shù)量, 大于0即刪除成功.
@Override public int delete(Uri uri, String selection, String[] selectionArgs) {
showLogs("delete");
String table = getTableName(uri);
if (TextUtils.isEmpty(table)) {
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int count = mDb.delete(table, selection, selectionArgs);
if (count > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return count; // 返回刪除的函數(shù)
}
修改數(shù)據(jù)update
, 與刪除類似, 返回修改數(shù)據(jù)的數(shù)量.
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
showLogs("update");
String table = getTableName(uri);
if (TextUtils.isEmpty(table)) {
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int row = mDb.update(table, values, selection, selectionArgs);
if (row > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return row; // 返回更新的行數(shù)
}
查詢數(shù)據(jù)query
, 返回數(shù)據(jù)庫的游標, 處理數(shù)據(jù).
@Nullable @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
showLogs("query 當前線程: " + Thread.currentThread().getName());
String tableName = getTableName(uri);
if (TextUtils.isEmpty(tableName)) {
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
return mDb.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
}
注意Uri和表名的轉換可能為空, 使用
TextUtils.isEmpty
判空.
共享數(shù)據(jù)
使用ContentProvider的獨立進程, 模擬進程間共享數(shù)據(jù).
<provider
android:name=".BookProvider"
android:authorities="org.wangchenlong.book.provider"
android:permission="org.wangchenlong.BOOK_PROVIDER"
android:process=":provider"/>
在AndroidManifest中, 把Provider注冊在
:provider
進程中, 與主進程分離.
添加數(shù)據(jù), 通過Uri找到ContentProvider, 使用ContentResolver
的insert
方法, 添加ContentValues
數(shù)據(jù).
public void addBooks(View view) {
Uri bookUri = BookProvider.BOOK_CONTENT_URI;
ContentValues values = new ContentValues();
values.put("_id", 6);
values.put("name", "信仰上帝");
getContentResolver().insert(bookUri, values);
}
查詢數(shù)據(jù)query
, 與數(shù)據(jù)庫的使用方式類似, 解析出Cursor, 通過移動Cursor, 找到所有匹配的結果.
public void showBooks(View view) {
String content = "";
Uri bookUri = BookProvider.BOOK_CONTENT_URI;
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
if (bookCursor != null) {
while (bookCursor.moveToNext()) {
Book book = new Book();
book.bookId = bookCursor.getInt(0);
book.bookName = bookCursor.getString(1);
content += book.toString() + "\n";
Log.e(TAG, "query book: " + book.toString());
mTvShowBooks.setText(content);
}
bookCursor.close();
}
}
效果
ContentProvider封裝了跨進程共享的邏輯, 我們只需要Uri即可訪問數(shù)據(jù), 使用共享數(shù)據(jù)非常便捷, 需要掌握簡單的使用方式.
OK, that's all! Enjoy it!