Android ContentProvider基礎(chǔ)應(yīng)用詳解
一、適用場(chǎng)景
1、ContentProvider為存儲(chǔ)和讀取數(shù)據(jù)提供了統(tǒng)一的接口
2、 使用ContentProvider,應(yīng)用程序可以實(shí)現(xiàn)數(shù)據(jù)共享
3、 android內(nèi)置的許多數(shù)據(jù)都是使用ContentProvider形式,供開發(fā)者調(diào)用的(如視頻,音頻,圖片,通訊錄等)
二、概念介紹
1、ContentProvider簡(jiǎn)介
當(dāng)應(yīng)用繼承ContentProvider類,并重寫該類用于提供數(shù)據(jù)和存儲(chǔ)數(shù)據(jù)的方法,就可以向其他應(yīng)用共享其數(shù)據(jù)。雖然使用其他方法也可以對(duì)外共享數(shù)據(jù),但數(shù)據(jù)訪問方式會(huì)因數(shù)據(jù)存儲(chǔ)的方式而不同,如:采用文件方式對(duì)外共享數(shù)據(jù),需要進(jìn)行文件操作讀寫數(shù)據(jù);采用sharedpreferences共享數(shù)據(jù),需要使用sharedpreferences API讀寫數(shù)據(jù)。而使用ContentProvider共享數(shù)據(jù)的好處是統(tǒng)一了數(shù)據(jù)訪問方式。
2、Uri類簡(jiǎn)介
Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")
在Content Provider中使用的查詢字符串有別于標(biāo)準(zhǔn)的SQL查詢。很多諸如select, add, delete, modify等操作我們都使用一種特殊的URI來進(jìn)行,這種URI由3個(gè)部分組成, “content://”, 代表數(shù)據(jù)的路徑,和一個(gè)可選的標(biāo)識(shí)數(shù)據(jù)的ID。以下是一些示例URI:
content://media/internal/images? 這個(gè)URI將返回設(shè)備上存儲(chǔ)的所有圖片
content://contacts/people/? 這個(gè)URI將返回設(shè)備上的所有聯(lián)系人信息
content://contacts/people/45 這個(gè)URI返回單個(gè)結(jié)果(聯(lián)系人信息中ID為45的聯(lián)系人記錄)
盡管這種查詢字符串格式很常見,但是它看起來還是有點(diǎn)令人迷惑。為此,Android提供一系列的幫助類(在android.provider包下),里面包含了很多以類變量形式給出的查詢字符串,這種方式更容易讓我們理解一點(diǎn),因此,如上面content://contacts/people/45這個(gè)URI就可以寫成如下形式:Uri person = ContentUris.withAppendedId(People.CONTENT_URI,? 45)。
三、使用步驟
1、首先創(chuàng)建一個(gè)繼承自ContentProvider的類,并實(shí)現(xiàn)其6個(gè)方法:
此6個(gè)方法只有onCreate方法運(yùn)行在UI線程中,這里在onCreate方法中進(jìn)行一些數(shù)據(jù)的初始化工作,初始了SQLite數(shù)據(jù)庫(kù)【這里拿封裝數(shù)據(jù)庫(kù)操作舉例,也可以是其他】,創(chuàng)建了兩張表book、user 并分別默認(rèn)插入了兩本書,和兩個(gè)管理員。
package com.hongri.androidipc.contentprovider;
/**
* @author zhongyao
* @date 2018/6/11
*
* onCreate方法運(yùn)行在主線程(main)中
* 其他方法運(yùn)行在Binder線程池中
*
*/
public class MyContentProvider extends ContentProvider {
private static final String TAG = MyContentProvider.class.getSimpleName() + " ";
/**
* 唯一標(biāo)識(shí)
*/
public static final String AUTHORITIES = "com.hongri.androidipc.contentprovider.provider";
public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/book");
public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");
public static final int BOOK_URI_CODE = 0;
public static final int USER_URI_CODE = 1;
private DbOpenHelper mDbHelper;
private SQLiteDatabase mDB;
private Context mContext;
private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
mUriMatcher.addURI(AUTHORITIES, "book", BOOK_URI_CODE);
mUriMatcher.addURI(AUTHORITIES, "user", USER_URI_CODE);
}
/**
* ContentProvider的創(chuàng)建:做一些初始化的工作
* onCreate方法 運(yùn)行在主線程,其他方法運(yùn)行在工作線程
* @return
*/
@Override
public boolean onCreate() {
String currentThreadName = Thread.currentThread().getName();
Logger.d(TAG + "onCreate--" + "currentThreadName:" + currentThreadName);
mContext = getContext();
mDbHelper = new DbOpenHelper(getContext(), "", null, 1);
mDB = mDbHelper.getWritableDatabase();
mDB.execSQL("delete from " + DbOpenHelper.BOOK_TABLE_NAME);
mDB.execSQL("delete from " + DbOpenHelper.USER_TABLE_NAME);
mDB.execSQL("insert into book values('1','Android',234);");
mDB.execSQL("insert into book values('2','Java',348);");
mDB.execSQL("insert into user values('1','hongri',1);");
mDB.execSQL("insert into user values('2','huyin',1);");
return true;
}
private String getTableName(Uri uri) {
switch (mUriMatcher.match(uri)) {
case BOOK_URI_CODE:
return DbOpenHelper.BOOK_TABLE_NAME;
case USER_URI_CODE:
return DbOpenHelper.USER_TABLE_NAME;
default:
break;
}
return "";
}
/**
* 用來返回一個(gè)MIME類型(媒體類型)
*
* @param uri
* @return
*/
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
/**
* 增
*
* @param uri
* @param values
* @return
*/
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
String tableName = getTableName(uri);
mDB.insert(tableName, null, values);
mContext.getContentResolver().notifyChange(uri, null);
return uri;
}
/**
* 刪
*
* @param uri
* @param selection
* @param selectionArgs
* @return
*/
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
String tableName = getTableName(uri);
int count = mDB.delete(tableName, selection, selectionArgs);
if (count > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 查
*
* @param uri
* @param projection
* @param selection
* @param selectionArgs
* @param sortOrder
* @return
*/
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
String currentThreadName = Thread.currentThread().getName();
Logger.d(TAG + "query: currentThreadName:" + currentThreadName + " uri:" + uri);
String tableName = getTableName(uri);
return mDB.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
}
/**
* 改
*
* @param uri
* @param values
* @param selection
* @param selectionArgs
* @return
*/
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) {
String tableName = getTableName(uri);
if (tableName == null) {
return 0;
}
int row = mDB.update(tableName, values, selection, selectionArgs);
if (row > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return row;
}
}
上面封裝的 DbOpenHelper 代碼如下:
package com.hongri.androidipc.db;
/**
* @author zhongyao
* @date 2018/6/11
*/
public class DbOpenHelper extends SQLiteOpenHelper {
public static final String BOOK_TABLE_NAME = "book";
public static final String USER_TABLE_NAME = "user";
public static final String DB_NAME = "library";
public static final int DB_VERSION = 1;
private String BOOK_SQL;
private String USER_SQL;
public DbOpenHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, DB_NAME, factory, DB_VERSION);
BOOK_SQL = "CREATE TABLE IF NOT EXISTS " + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,"
+ "page INT)";
USER_SQL = "CREATE TABLE IF NOT EXISTS " + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,"
+ "sex INT)";
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(BOOK_SQL);
db.execSQL(USER_SQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2、在Manifest文件中注冊(cè)這個(gè)ContentProvider:
<provider
android:name="com.hongri.androidipc.contentprovider.MyContentProvider"
android:authorities="com.hongri.androidipc.contentprovider.provider"
android:permission="com.hongri.androidipc.PROVIDER"
android:process=":provider" />
3、在外部應(yīng)用中訪問它:
<activity
android:name=".ContentProviderActivity"
android:process=":remoteProcess">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
這里單獨(dú)開啟了一個(gè)進(jìn)程 remoteProcess 用來模擬第三方App。
該Activity頁(yè)面如下,點(diǎn)擊相關(guān)按鈕可以進(jìn)行基礎(chǔ)的增、刪、查、改操作,可通過log查看調(diào)用結(jié)果。

這里注意如果希望監(jiān)聽數(shù)據(jù)更新,那么需要注冊(cè)內(nèi)容觀察者 ContentObserver,增、刪、改動(dòng)的時(shí)候,會(huì)有回調(diào)通知,前提是在自定義的ContentProvider類方法中,調(diào)用getContentResolver().notifyChange(uri, null);方法即可。
package com.hongri.androidipc;
/**
* @author hongri
*/
public class ContentProviderActivity extends AppCompatActivity implements View.OnClickListener {
//book uri
private final Uri bookUri = MyContentProvider.BOOK_CONTENT_URI;
//user uri
private final Uri userUri = MyContentProvider.USER_CONTENT_URI;
private Button btnInsert, btnQuery, btnQueryByUser, btnModify, btnDelete;
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_provider);
btnInsert = (Button) findViewById(R.id.btnInsert);
btnQuery = (Button) findViewById(R.id.btnQuery);
btnQueryByUser = (Button) findViewById(R.id.btnQueryByUser);
btnModify = (Button) findViewById(R.id.btnModify);
btnDelete = (Button) findViewById(R.id.btnDelete);
btnInsert.setOnClickListener(this);
btnQuery.setOnClickListener(this);
btnQueryByUser.setOnClickListener(this);
btnModify.setOnClickListener(this);
btnDelete.setOnClickListener(this);
getContentResolver().registerContentObserver(bookUri, true, mContentObserver);
getContentResolver().registerContentObserver(userUri, true, mContentObserver);
}
/**
* 定義一個(gè)內(nèi)容觀察者【數(shù)據(jù)有更新時(shí)會(huì)調(diào)用】
*/
private ContentObserver mContentObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//這里selfChange都是返回false的,不用理會(huì)這個(gè)參數(shù)
Logger.d("onChange調(diào)用 --- 數(shù)據(jù)有更新");
}
};
/**
* 向 bookUri 中插入一本書
*/
private void doInsert() {
ContentValues values = new ContentValues();
values.put("name", "Android框架");
values.put("page", "1213");
Uri uri = getContentResolver().insert(bookUri, values);
Logger.d("插入成功 ---> uri:" + uri.toString());
}
/**
* 根據(jù) bookUri 查詢所有書籍
*/
private void doQuery() {
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name", "page"}, null, null, null);
while (bookCursor.moveToNext()) {
Book book = new Book();
book.set_id(bookCursor.getInt(0));
book.setName(bookCursor.getString(1));
book.setPage(bookCursor.getInt(2));
Logger.d("book:" + book.toString());
}
bookCursor.close();
}
/**
* 查詢 userUri 所有圖書員
*/
private void doQueryUser() {
Cursor userCursor = getContentResolver().query(userUri, new String[]{"_id", "name", "sex"}, null, null, null);
while (userCursor.moveToNext()) {
User user = new User();
user.set_id(userCursor.getInt(0));
user.setName(userCursor.getString(1));
user.setSex(userCursor.getInt(2));
Logger.d("user:" + user.toString());
}
userCursor.close();
}
/**
* 更新書籍【Android底層開發(fā)】
*/
private void doUpdate() {
ContentValues updateValues = new ContentValues();
updateValues.put("name", "Android底層開發(fā)");
updateValues.put("page", 3345);
int row = getContentResolver().update(bookUri, updateValues, "name=?", new String[]{"Android框架"});
if (row > 0) {
Logger.d("book:已修改");
}
}
/**
* 刪除 名為"Java" 這本書
*/
private void doDelete() {
int count = getContentResolver().delete(bookUri, "name=?", new String[]{"Java"});
if (count > 0) {
Logger.d("book:已進(jìn)行刪除操作");
}
}
@SuppressLint("NonConstantResourceId")
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.btnInsert:
/**
* 增
*/
doInsert();
break;
case R.id.btnQuery:
/**
* 查
*/
doQuery();
break;
case R.id.btnQueryByUser:
/**
* 查
*/
doQueryUser();
break;
case R.id.btnModify:
/**
* 改
*/
doUpdate();
doQuery();
break;
case R.id.btnDelete:
/**
* 刪
*/
doDelete();
doQuery();
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(mContentObserver);
}
}
以上便是ContentProvider的基礎(chǔ)應(yīng)用。
源碼地址:AndroidIPC?
到此這篇關(guān)于Android ContentProvider基礎(chǔ)應(yīng)用詳解的文章就介紹到這了,更多相關(guān)Android ContentProvider內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android ViewPager實(shí)現(xiàn)頁(yè)面左右切換效果
這篇文章主要為大家詳細(xì)介紹了Android ViewPager實(shí)現(xiàn)頁(yè)面左右切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android 實(shí)現(xiàn)釘釘自動(dòng)打卡功能
這篇文章主要介紹了Android 實(shí)現(xiàn)釘釘自動(dòng)打卡功能的步驟,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03
android仿知乎標(biāo)題欄隨ScrollView滾動(dòng)變色
這篇文章主要為大家詳細(xì)介紹了android仿知乎標(biāo)題欄隨ScrollView滾動(dòng)變色,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android 修改系統(tǒng)關(guān)機(jī)動(dòng)畫的實(shí)現(xiàn)
這篇文章主要介紹了Android 修改系統(tǒng)關(guān)機(jī)動(dòng)畫的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2016-10-10
Android開發(fā)獲取重力加速度和磁場(chǎng)強(qiáng)度的方法
Android StickListView實(shí)現(xiàn)懸停效果
Android?APP瘦身shrinkResources使用問題詳解

