1、簡介
ContentProvider 為存儲和獲取數(shù)據(jù)提供統(tǒng)一的接口诊县÷饴ィ可以在不同的應(yīng)用程序之間共享數(shù)據(jù)。Android已經(jīng)為常見的一些數(shù)據(jù)提供了默認(rèn)的 ContentProvider赴肚。
數(shù)據(jù)通過唯一的 URI 標(biāo)識來源素跺。ContentProvider 將數(shù)據(jù)看作表,查詢 / 操作數(shù)據(jù)的時候誉券,通過類似數(shù)據(jù)庫操作的 insert / delete / query / update
方法來實(shí)現(xiàn)增刪查改操作指厌。
作為應(yīng)用間數(shù)據(jù)交換 / 共享接口,當(dāng)然需要有一個“橋梁”來連接數(shù)據(jù)提供方和使用方踊跟。數(shù)據(jù)提供方提供數(shù)據(jù)踩验,使用方使用 content://authorities/path
類似的 URI 來訪問數(shù)據(jù)鸥诽。
2、數(shù)據(jù)提供方
2.1箕憾、Provider 聲明
數(shù)據(jù)提供方需要在 Androidmanifest.xml
中聲明 ContentProvider 組件牡借。一個 ContentProvider 組件聲明如下:
<permission
android:name="com.straw.providerhost.course.read"
android:protectionLevel="normal"/>
<permission
android:name="com.straw.providerhost.course.write"
android:protectionLevel="normal"/>
<application>
<provider
android:authorities="com.straw.course"
android:name="com.straw.providerhost.CourseContentProvider"
android:exported="true"
android:readPermission="com.straw.providerhost.course.read"
android:writePermission="com.straw.providerhost.course.write"/>
</application>
屬性 | 含義 |
---|---|
authorities | 指定 authorieties(類似包名的格式,如 com.xx.xx )袭异,訪問者通過 content://authorities/path 的形式訪問 |
name | 這個 Provider 對應(yīng)的 Java 類名 |
exported | 為 true 則表示導(dǎo)出钠龙。不設(shè)置或設(shè)置為 false 時,其他應(yīng)用如果使用這個 Provider御铃,則會出現(xiàn) java.lang.SecurityException: Permission Denial 錯誤 |
readPermission | 讀數(shù)據(jù)者所需聲明權(quán)限碴里,數(shù)據(jù)訪問者不聲明權(quán)限則會出現(xiàn) java.lang.SecurityException: Permission Denial 錯誤 |
writePermission | 寫數(shù)據(jù)者所需聲明權(quán)限,數(shù)據(jù)訪問者不聲明權(quán)限則會出現(xiàn) java.lang.SecurityException: Permission Denial 錯誤 |
2.2上真、其他情況
那兩個 App 同時聲明相同 authorities
的 ContentProvider 可不可以呢咬腋?答案是 不行。如果兩個 App 聲明了相同 authorities
的 Provider睡互,第二個 App 在安裝時會出現(xiàn) INSTALL_FAILED_CONFLICTING_PROVIDER
錯誤根竿。錯誤如下:
2.3、ContentProvider 類
一個具體的 ContentProvider 類需要繼承自 android.content.ContentProvider
湃缎,并且實(shí)現(xiàn) onCreate / getType / insert / delete / update / query
這幾個方法犀填。一般我們在 onCreate 方法中打開數(shù)據(jù)庫蠢壹,在對應(yīng)的操作方法中根據(jù) URI 的不同嗓违,操作不同的數(shù)據(jù)。
先定義一個幫助類图贸,來聲明一些常量蹂季。例如:
public class CourseProviders {
public static final String AUTHORITIES = "com.straw.course";
public static final String COURSE_PATH = "course";
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITIES);
public static final Uri COURSE_URI = Uri.withAppendedPath(BASE_URI, COURSE_PATH);
public static class CourseColumn {
public static final String ID = "id";
public static final String NAME = "NAME";
public static final String TEACHER_NAME = "teacher_name";
public static final String WATCH_COUNT = "watch_count";
public static final String VIDEO_URL = "video_url";
}
}
然后實(shí)現(xiàn)具體的 ContentProvider:
public class CourseContentProvider extends ContentProvider {
private CourseSqliteHelper mSqliteHelper;
private SQLiteDatabase mDatabase;
private UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int PROVIDE_COURSE = 1;
@Override
public boolean onCreate() {
mSqliteHelper = new CourseSqliteHelper(getContext());
mDatabase = mSqliteHelper.getWritableDatabase();
mUriMatcher.addURI(CourseProviders.AUTHORITIES,
CourseProviders.COURSE_PATH, PROVIDE_COURSE);
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
switch (mUriMatcher.match(uri)) {
case PROVIDE_COURSE:
return mDatabase.query(CourseSqliteHelper.COURSE_TABLE_NAME,
projection, selection, selectionArgs, null, null, sortOrder);
default:
break;
}
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
Uri result = null;
switch (mUriMatcher.match(uri)) {
case PROVIDE_COURSE:
long rowId = mDatabase.insert(
CourseSqliteHelper.COURSE_TABLE_NAME, null, values);
result = ContentUris.withAppendedId(uri, rowId);
break;
default:
break;
}
return result;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
switch (mUriMatcher.match(uri)) {
case PROVIDE_COURSE:
return mDatabase.delete(CourseSqliteHelper.COURSE_TABLE_NAME,
selection, selectionArgs);
default:
break;
}
return 0;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
switch (mUriMatcher.match(uri)) {
case PROVIDE_COURSE:
return mDatabase.update(CourseSqliteHelper.COURSE_TABLE_NAME,
values, selection, selectionArgs);
default:
break;
}
return 0;
}
}
通過 android.content.UriMatcher
提供的 match
方法,很方便的將 content://authorities/path
這樣的訪問 URI 匹配到正確的數(shù)據(jù)訪問路徑疏日。
3偿洁、數(shù)據(jù)訪問方
我們可以在本應(yīng)用內(nèi)通過 ContentProvider 訪問,也可以在其他應(yīng)用中訪問沟优。在 ContentProvider 所在應(yīng)用外訪問時涕滋,會拉起 ContentProvider 所在的 App(會拉起 Application,但不會打開任何 Activity)挠阁。
3.1宾肺、權(quán)限聲明
首先,在使用方的 AndroidManifest.xml
中需要聲明所使用 ContentProvider 的權(quán)限侵俗,如果只讀就聲明讀權(quán)限锨用,如果讀寫都需要就聲明讀寫權(quán)限。如:
<uses-permission android:name="com.straw.providerhost.course.read"/>
<uses-permission android:name="com.straw.providerhost.course.write"/>
3.2隘谣、具體使用
private void queryAll() {
Cursor cursor = context.getContentResolver().query(CourseProviders.COURSE_URI, null, null, null, null);
if (cursor == null) {
return;
}
mCourseInfoList.clear();
while (cursor.moveToNext()) {
CourseInfo info = new CourseInfo();
info.mId = cursor.getString(cursor.getColumnIndex(CourseProviders.CourseColumn.ID));
info.mName = cursor.getString(cursor.getColumnIndex(CourseProviders.CourseColumn.NAME));
info.mTeacherName = cursor.getString(cursor.getColumnIndex(CourseProviders.CourseColumn.TEACHER_NAME));
info.mWatchCount = cursor.getInt(cursor.getColumnIndex(CourseProviders.CourseColumn.WATCH_COUNT));
info.mVideoUrl = cursor.getString(cursor.getColumnIndex(CourseProviders.CourseColumn.VIDEO_URL));
mCourseInfoList.add(info);
}
cursor.close();
mAdapter.notifyDataSetChanged();
}
通過 context.getContentResolver
方法獲取 ContentResolver
增拥,然后使用其提供的 insert / delete / update / query
方法進(jìn)行 增刪改查
操作即可。