標籤:
1.1. 什麼是內容提供者
內容提供者是Android中的四大組件之一,可以將應用中的資料對外進行共用
內容提供者將資料的訪問方式統一,不必針對不同資料類型採取不同的存取原則
內容提供者將資料封裝,只暴露出我們希望提供給其他程式的資料
內容提供者中資料更改可被監聽
1.2. 建立內容提供者
定義類繼承ContentProvider,根據需要重寫內部方法
在資訊清單檔的<application>節點下進行配置,<provider>標籤中需要指定name和authorities屬性
name為類名,包名從程式Package開始,以“.”開始
authorities:是訪問Provider時的路徑,要唯一
URI代表要操作的資料,由scheme、authorites、path三部分組成
content://cn.itcast.provider.itcast/person
scheme:固定為content,代表訪問內容提供者
authorites:<provider>節點中的authorites屬性
path:程式定義的路徑,可根據商務邏輯定義
1.3. 完成CRUD方法
Ÿ 當程式調用CRUD方法時會傳入Uri
Ÿ 我們通過Uri判斷調用者要操作的資料
可以使用工具類UriMatcher來判斷Uri
addURI方法可以添加Uri
match方法可以匹配一個Uri判斷其類型
Ÿ 根據商務邏輯操作資料
1.4. 完成getType方法
Ÿ 如果返回資料是單條資料:vnd.android.cursor.item
Ÿ 如果返回資料是多條資料:vnd.android.cursor.dir
public class PersonProvider extends android.content.ContentProvider { private static final int PERSON = 0; private static final int PERSON_ID = 1; private UriMatcher matcher; private DBOpenHelper helper; @Override public boolean onCreate() { matcher = new UriMatcher(UriMatcher.NO_MATCH); matcher.addURI("com.demo.sqlite.provider", "person", PERSON); // 添加一個可以匹配的Uri matcher.addURI("com.demo.sqlite.provider", "person/#", PERSON_ID); helper = new DBOpenHelper(getContext()); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch (matcher.match(uri)) { // 對Uri進行匹配 case PERSON_ID: String idSelection = "id = " + ContentUris.parseId(uri); // Converts the last path segment to a long. // 注意此處字串拼裝 selection = selection == null ? idSelection : idSelection + " AND " + selection; case PERSON: SQLiteDatabase db = helper.getReadableDatabase(); return db.query("person", projection, selection, selectionArgs, null, null, sortOrder); default: throw new IllegalArgumentException("No Match Uri " + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { switch (matcher.match(uri)) { case PERSON: SQLiteDatabase db = helper.getWritableDatabase(); long id = db.insert("person", null, values); // 使用 db.insert() 方法插入資料,返回 id // 而 db.exceSQL(sql)方式插入資料,返回 void // return Uri.parse("content://com.demo.sqlite.provider/person/" + id); // 與下句效果相同 return ContentUris.withAppendedId(uri, id); // Appends the given ID to the end of the path. default: throw new IllegalArgumentException("No Match Uri " + uri); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { switch (matcher.match(uri)) { case PERSON_ID: long id = ContentUris.parseId(uri); selection = selection == null ? "id = " + id : "id = " +id + " AND " +selection; case PERSON: SQLiteDatabase db = helper.getWritableDatabase(); // return : The number of rows affected return db.delete("person", selection, selectionArgs); default: throw new IllegalArgumentException("No Match Uri " + uri); } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { switch (matcher.match(uri)) { case PERSON_ID: long id = ContentUris.parseId(uri); selection = selection == null ? "id = " + id : "id = " + id + " AND " + selection; case PERSON: SQLiteDatabase db = helper.getWritableDatabase(); // @return the number of rows affected return db.update("person", values, selection, selectionArgs); default: throw new IllegalArgumentException("No Match Uri " + uri); } } @Override public String getType(Uri uri) { switch (matcher.match(uri)) { case PERSON_ID: return "vnd.android.cursor.item/person"; case PERSON: return "vnd.android.cursor.dir/person"; default: throw new IllegalArgumentException("No Match Uri " + uri); } }
1.5. 訪問內容提供者
Ÿ 通過Context獲得ContentResolver對象
Ÿ 調用ContentResolver對象的方法即可訪問內容提供者
private static final String TAG = "ProviderTest"; // 查詢 id = 36 public void testQuery1() { // 通過 ContentResolver 調用 ContentProvider 提供的方法 ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.demo.sqlite.provider/person"); Cursor c = resolver.query(uri, new String[] { "id", "name", "balance" }, "id = ?", new String[] { "36" }, null); if (c.moveToNext()) { Person person = new Person(c.getInt(0), c.getString(1), c.getInt(2)); Logger.i(TAG, person.toString()); } } // 查詢所有 person public void testQuery2() { // 通過 ContentResolver 調用 ContentProvider 提供的方法 ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.demo.sqlite.provider/person"); Cursor c = resolver.query(uri, null, null, null, null); while (c.moveToNext()) { Person person = new Person(c.getInt(0), c.getString(1), c.getInt(2)); Logger.i(TAG, person.toString()); } } // 通過附帶 id 查詢 person public void testQuery3() { // 通過 ContentResolver 調用 ContentProvider 提供的方法 ContentResolver resolver = getContext().getContentResolver(); // Uri Uri uri = Uri.parse("content://com.demo.sqlite.provider/person/55"); Cursor c = resolver.query(uri, null, "name = ?", new String[] { "Ashia_54" }, null); while (c.moveToNext()) { Person person = new Person(c.getInt(0), c.getString(1), c.getInt(2)); Logger.i(TAG, person.toString()); } } public void testInsert() { ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.demo.sqlite.provider/person"); ContentValues values = new ContentValues(); Person person = new Person("another Person uri insert", 7000); values.put("name", person.getName()); values.put("balance", person.getBalance()); Uri reUri = resolver.insert(uri, values); Cursor c = resolver.query(reUri, null, null, null, null); if (c.moveToNext()) { Person rePerson = new Person(c.getInt(0), c.getString(1), c.getInt(2)); Logger.i(TAG, rePerson.toString()); } } public void testDelete() { // ContentResolver :This class provides applications access to the content model. ContentResolver resolver = getContext().getContentResolver(); Uri url = Uri.parse("content://com.demo.sqlite.provider/person/121"); // 忘記加 = ?, 異常如下 // android.database.sqlite.SQLiteException: bind or column index out of range long id = resolver.delete(url, "name = ?", new String[] {"zhangsan"}); Logger.i(TAG, id + ""); id = resolver.delete(url, null, null); Logger.i(TAG, id + ""); } public void testDeleteAll() { ContentResolver resolver = getContext().getContentResolver(); Uri url = Uri.parse("content://com.demo.sqlite.provider/person"); int deleteNum = resolver.delete(url, null, null); Logger.i(TAG, deleteNum + ""); } public void testUpdate() { ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.demo.sqlite.provider/person/122"); ContentValues values = new ContentValues(); values.put("balance", 8000); int update = resolver.update(uri, values, "balance = ?", new String[] {"9000"}); Logger.i(TAG, update + ""); } public void testUpdateById() { ContentResolver resolver = getContext().getContentResolver(); ContentValues values = new ContentValues(); values.put("name", "new name"); values.put("balance", 7000); values.put("id", 2); Uri uri = Uri.parse("content://com.demo.sqlite.provider/person/123"); int update = resolver.update(uri, values, null, null); Logger.i(TAG, update + ""); } // Uri寫錯,異常 // java.lang.IllegalArgumentException: Unknown URI // 主鍵 id 重複,異常 // android.database.sqlite.SQLiteConstraintException public void testGetType() { ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.demo.sqlite.provider/person/1"); Logger.i(TAG, resolver.getType(uri)); uri = Uri.parse("content://com.demo.sqlite.provider/person"); Logger.i(TAG, resolver.getType(uri)); }
1.6. 監聽內容提供者資料變化
Ÿ 在內容提供者中可以通知其他程式資料發生變化
通過Context的getContentResolver()方法擷取ContentResolver
調用其notifyChange()方法發送資料修改通知
Ÿ 在其他程式中可以通過ContentObserver監聽資料變化
通過Context的getContentResolver()方法擷取ContentResolver
調用其registerContentObserver()方法指定對某個Uri註冊ContentObserver
自訂ContentObserver,重寫onChange()方法擷取資料
發送資料修改通知
public Uri insert(Uri uri, ContentValues values) { switch (matcher.match(uri)) { case PERSON: SQLiteDatabase db = helper.getWritableDatabase(); long id = db.insert("person", null, values); // Notify registered observers that a row was updated. 註冊 observer // @param observer The observer that originated the change, may be null // 產生改變的Observer. 此處為Provider帶來的改變,傳 null this.getContext().getContentResolver().notifyChange(uri, null); // 發送修改通知 // return Uri.parse("content://com.demo.sqlite.provider/person/" + id); return ContentUris.withAppendedId(uri, id); default: throw new IllegalArgumentException("No Match Uri " + uri); } }
定義一個ContentObserver,監聽Uri所對應的內容提供者的變化
protected static final String TAG = "ObserverActivity"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 定義一個ContentObserver ContentObserver observer = new ContentObserver(new Handler()) { /** * Returns true if this observer is interested in notifications for * changes made through the cursor the observer is registered with. */ // 是否傳遞自己的改變 @Override public boolean deliverSelfNotifications() { return super.deliverSelfNotifications(); } /** * This method is called when a change occurs to the cursor that is * being observed. * * @param selfChange * true if the update was caused by a call to commit on * the cursor that is being observed. */ // 當被監聽的內容發生了改變時,調用該方法 @Override public void onChange(boolean selfChange) { Logger.i(TAG, "監聽到了變化"); // 列印出最後插入的資訊 ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://com.demo.sqlite.provider/person"); // SELECT * FROM person ORDER BY id DESC LIMIT 1; Cursor c = resolver.query(uri, null, null, null, "id DESC LIMIT 1"); if(c.moveToNext()) { String result = "id = " + c.getInt(0) + ", name = " + c.getString(1) + ", balance = " + c.getInt(2); Logger.i(TAG, result); Toast.makeText(ObserverActivity.this, result, 1).show(); } // 父類未做任何實現 super.onChange(selfChange); } }; Uri uri = Uri.parse("content://com.demo.sqlite.provider/person"); // 註冊一個ContentObserver /* * @param notifyForDescendents * If true changes to URIs beginning with uri will also cause notifications to be sent. * Iffalse only changes to the exact URI specified by uri will cause notifications to be sent. * If true, than any URI values at or below the specified URI will also trigger a match. */ // 是否監聽以 "uri" 開頭 的其他 uri getContentResolver().registerContentObserver(uri, true, observer); }
通過ContentObserver,監測內容改變,自動更新ListView
bt_insert = (Button) findViewById(R.id.bt_insert); bt_insert.setOnClickListener(new OnClickListener() { public void onClick(View v) { ContentResolver resolver = ListViewSimpleCursorAdapterActivity.this.getContentResolver(); Uri url = Uri.parse("content://com.demo.sqlite.provider/person"); ContentValues values = new ContentValues(); values.put("name", "test name"); values.put("balance", 3000); resolver.insert(url, values); } }); lv_person.setOnItemClickListener(new MyOnItemClickListener()); Uri uri = Uri.parse("content://com.demo.sqlite.provider/person"); getContentResolver().registerContentObserver(uri, false, new MyContentObserver(new Handler())); public class MyContentObserver extends ContentObserver { public MyContentObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { // 當所監聽的內容發生了改變,則更新lv中的資料 Cursor c = new PersonDAO(ListViewSimpleCursorAdapterActivity.this).queryAllCursor(); SimpleCursorAdapter adapter = new SimpleCursorAdapter(ListViewSimpleCursorAdapterActivity.this, // R.layout.listview_item, // c, // new String[] { "_id", "name", "balance" }, // new int[] { R.id.tv_id, R.id.tv_name, R.id.tv_balance }); lv_person.setAdapter(adapter); lv_person.setOnItemClickListener(new MyOnItemClickListener()); super.onChange(selfChange); Logger.i(TAG, "onChange"); } }
錯誤
Uri寫錯,異常 java.lang.IllegalArgumentException: Unknown URI
主鍵 id 重複,異常 android.database.sqlite.SQLiteConstraintException
測試ListView更新時,介面總是自動關閉
如果自己是內容提供者,也是內容觀察者,
在測試案例中更改內容,應用會強制退出
(轉)ContentProvider 內容提供者