一、創(chuàng)建內容提供器的步驟
- 新建 MyProvider 繼承自 ContentProvider鸟整。復寫幾個方法。
- 借助 UriMatcher 匹配內容 URI,得知傳入的 Uri 對象是想訪問哪張表中的哪條數(shù)據来屠。
- 在 MyProvider 的幾個方法中用 switch 判斷 uriMatcher.match(uri),根據不同 Uri 的目的震鹉,把目的定位到相應表或行俱笛。
- 明確目的后,具體靠對 SQLiteDatabase 進行操作完成增刪改查传趾。
- 記住要在 AndroidManifest.xml 里注冊迎膜。
1. 新建 MyProvider 繼承自 ContentProvider。復寫幾個方法浆兰。
public class MyProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
return 0;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
}
1. onCreate()
- 初始化內容提供器的時候調用磕仅,通常會在這里完成對數(shù)據庫的創(chuàng)建和升級等操作.
- 返回 true 表示內容提供器初始化成功珊豹,返回 false 則表示失敗。
- 注意榕订,只有當存在 ContentResolver 嘗試訪問我們程序中的數(shù)據時店茶,內容提供器才會被初始化。
2. query()
- 從內容提供器中查詢數(shù)據劫恒。
- 使用 uri 參數(shù)來確定查詢哪張表贩幻,projection 參數(shù)用于確定查詢哪些列,selection 和 selectionArgs 參數(shù)用于約束查詢哪些行兼贸,sortOrder 參數(shù)用于對結果進行排序段直。
- 查詢的結果存放在 Cursor 對象中返回。
3. insert()
- 向內容提供器中添加一條數(shù)據溶诞。
- 使用 uri 參數(shù)來確定要添加到的表鸯檬,待添加的數(shù)據保存在 values 參數(shù)中。
- 添加完成后螺垢,返回一個用于表示這條新記錄的 URI喧务。
4. update()
- 更新內容提供器中已有的數(shù)據。
- 使用 uri 參數(shù)來確定更新哪一張表中的數(shù)據枉圃,新數(shù)據保存在 values 參數(shù)中功茴,selection 和 selectionArgs 參數(shù)用于約束更新哪些行。
- 受影響的行數(shù)將作為返回值返回孽亲。
5. delete()
- 從內容提供器中刪除數(shù)據坎穿。
- 使用 uri 參數(shù)來確定刪除哪一張表中的數(shù)據,selection 和 selectionArgs 參數(shù)用于約束刪除哪些行返劲。
- 被刪除的行數(shù)將作為返回值返回玲昧。
6. getType()
- 根據傳入的內容 URI 來返回相應的 MIME 類型。
一個內容 URI 所對應的 MIME 字符串主要由三部分組分:
- 必須以 vnd. 開頭篮绿。
- 如果內容 URI 以路徑結尾孵延,則后接 android.cursor.dir/,如果內容 URI 以 id 結尾亲配,則后接 android.cursor.item/尘应。
- 最后接上 vnd.<authority>.<path>。
- 對于 content://com.example.app.provider/table1吼虎,對應的 MIME 類型為:
vnd.android.cursor.dir/vnd.com.example.app.provider.table1
- 對于 content://com.example.app.provider/table1/1犬钢,對應的 MIME 類型為:
vnd.android.cursor.item/vnd.com.example.app.provider.table1
- 實現(xiàn) getType() 邏輯 :
public class MyProvider extends ContentProvider {
......
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
default:
break;
}
return null;
}
}
2. 借助 UriMatcher 匹配內容 URI,得知傳入的 Uri 對象是想訪問哪張表中的哪條數(shù)據思灰。
- 能夠匹配任意表的內容 URI:
content://com.example.app.provider/*
能夠匹配 table1 表中任意一行數(shù)據的內容 URI:
content://com.example.app.provider/table1/#
- UriMatcher 中提供了一個 addURI() 方法玷犹,接收三個參數(shù),可以分別把權限官辈、路徑和一個自定義代碼傳進去箱舞。
- 當調用 UriMatcher 的 match() 方法時,就可以將一個 Uri 對象傳入拳亿,返
回值是某個能夠匹配這個 Uri 對象所對應的自定義代碼晴股。 - 利用這個代碼,我們就可以判斷出調用方期望訪問的是哪張表或哪一行中的數(shù)據了肺魁。
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
public static final int TABLE2_DIR = 2;
public static final int TABLE2_ITEM = 3;
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider ", "table1/#", TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2", TABLE2_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2/#", TABLE2_ITEM);
}
3. 在 MyProvider 的幾個方法中用 switch 判斷 uriMatcher.match(uri)电湘,根據不同 Uri 的目的,把目的定位到相應表或行鹅经。
public class MyProvider extends ContentProvider {
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
public static final int TABLE2_DIR = 2;
public static final int TABLE2_ITEM = 3;
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider ", "table1/#", TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2", TABLE2_ITEM);
uriMatcher.addURI("com.example.app.provider ", "table2/#", TABLE2_ITEM);
}
......
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
// 查詢table1表中的所有數(shù)據
break;
case TABLE1_ITEM:
// 查詢table1表中的單條數(shù)據
break;
case TABLE2_DIR:
// 查詢table2表中的所有數(shù)據
break;
case TABLE2_ITEM:
// 查詢table2表中的單條數(shù)據
break;
default:
break;
}
......
}
......
}
安全問題:
所有的 CRUD 操作都一定要匹配到相應的內容 URI 格式才能進行的寂呛,而我們當然不可能向 UriMatcher 中添加隱私數(shù)據的 URI,所以這部分數(shù)據根本無法被外部程序訪問到瘾晃,安全問題也就不存在了贷痪。
4. 明確目的后,具體靠對 SQLiteDatabase 進行操作完成增刪改查蹦误。
public class DatabaseProvider extends ContentProvider {
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
public static final String AUTHORITY = "com.example.databasetest.provider";
private static UriMatcher uriMatcher;
private MyDatabaseHelper dbHelper;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
@Override
public boolean onCreate() {dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// 查詢數(shù)據
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("Book", projection, "id = ?", new String[]{ bookId }, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("Category", projection, "id = ?", new String[] { categoryId }, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// 添加數(shù)據
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("Category", null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);
break;
default:
break;
}
return uriReturn;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 更新數(shù)據
SQLiteDatabase db = dbHelper.getWritableDatabase();
int updatedRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
updatedRows = db.update("Book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("Book", values, "id = ?", new String[] { bookId });
break;
case CATEGORY_DIR:
updatedRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("Category", values, "id = ?", new String[] { categoryId });
break;
default:
break;
}
return updatedRows;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 刪除數(shù)據
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deletedRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deletedRows = db.delete("Book", selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("Book", "id = ?", new String[] { bookId });
break;
case CATEGORY_DIR:
deletedRows = db.delete("Category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("Category", "id = ?", new String[] { categoryId });
break;
default:
break;
}
return deletedRows;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.category";
}
return null;
}
}
- getPathSegments() 方法
它會將內容 URI 權限之后的部分以 “/” 符號進行分割劫拢,并把分割后的結果放入到一個字符串列表中,這個列表的第 0 個位置存放的就是路徑强胰,第 1 個位置存放的就是 id舱沧。
5. 記住要在 AndroidManifest.xml 里注冊。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.databasetest"
android:versionCode="1"
android:versionName="1.0" >
......
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
......
<provider
android:name="com.example.databasetest.DatabaseProvider"
android:authorities="com.example.databasetest.provider" >
</provider>
</application>
</manifest>
二偶洋、使用自己創(chuàng)建的內容提供器
public class MainActivity extends Activity {
private String newId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button addData = (Button) findViewById(R.id.add_data);
addData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 添加數(shù)據
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
ContentValues values = new ContentValues();
values.put("name", "A Clash of Kings");
values.put("author", "George Martin");
values.put("pages", 1040);
values.put("price", 22.85);
Uri newUri = getContentResolver().insert(uri, values);
newId = newUri.getPathSegments().get(1);
}
});
Button queryData = (Button) findViewById(R.id.query_data);
queryData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 查詢數(shù)據
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.
getColumnIndex("price"));
Log.d("MainActivity", "book name is " + name);
Log.d("MainActivity", "book author is " + author);
Log.d("MainActivity", "book pages is " + pages);
Log.d("MainActivity", "book price is " + price);
}
cursor.close();
}
}
});
Button updateData = (Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 更新數(shù)據
Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
ContentValues values = new ContentValues();
values.put("name", "A Storm of Swords");
values.put("pages", 1216);
values.put("price", 24.05);
getContentResolver().update(uri, values, null, null);
}
});
Button deleteData = (Button) findViewById(R.id.delete_data);
deleteData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 刪除數(shù)據
Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
getContentResolver().delete(uri, null, null);
}
});
}
}