標籤:軟體 應用 android 系統 監聽
- 利用內容提供者讀取連絡人
- 利用內容提供者插入連絡人
- 內容觀察者的原理
- 利用內容觀察者監聽系統應用程式資料庫或者自己應用程式資料庫的變化
利用內容提供者讀取連絡人
讀取連絡人相對於讀取簡訊來說就複雜很多了,我們一步一步來吧。
先看看一下連絡人的資料庫,是位於什麼地方!
既然很複雜,我們就一步步分析吧,我們把contacts2.db匯出到電腦中,並使用SQLite資料庫軟體開啟。你可以看到一大堆的表和視圖,當然我們使用到的也只有三張。分別是raw_contacts、data、mimetypes分別儲存著連絡人ID、連絡人資料、連絡人資料對應的MIMIE類型。
我們逐個看一下,首先是raw_contacts表。
在本表的contact_id列儲存的是手機中連絡人所對應的ID。通過上面的圖可以知道,在手機中有兩個連絡人。
接下來看一下data表。
在本表中,我們最關心的有三列mimetype_id、raw_contact_id和data1。其中mimetype_id是mimetype表的主鍵ID;相應的raw_contact_id就是raw_contact的contact_id列了。那麼data1列代表的什麼呢?
你猜的不錯,就是我們連絡人的資料。橫向來看,raw_contact_id相同的行中data1列資料群組成了一個連絡人的資料。
在看一下mimetypes表的情況吧。
也是比較簡單,不同的mimetype內容代表的不同資料類型,比如:電話號、姓名、Email等。
看起來很麻煩的樣子,貌似還需要講data表和mimetypes表聯集查詢才可以。但是實際上不用這麼麻煩,Google工程師已經為我們準備了view_data的視圖,已經將data和mimetypes兩張表的資料連接起來了。我們趕緊看看裡面的內容吧。
瞭解視圖結構後,我們通過contact_id查詢一下,就可以得到資料的內容和內容類型了。
分析完連絡人的表,接下來就是如何使用內容解析者去查詢了,跟之前一樣,依舊要知道主機名稱和路徑。跟著我一起看一下源碼,這次連絡人應用內容提供者的路徑為android4.4\packages\providers\ContactsProvider。開啟資訊清單檔,可以看到如下資訊:
<provider android:name="ContactsProvider2" android:authorities="contacts;com.android.contacts" android:label="@string/provider_label" android:multiprocess="false" android:exported="true" android:readPermission="android.permission.READ_CONTACTS" android:writePermission="android.permission.WRITE_CONTACTS"></provider>
主機名稱是:com.andriod.contacts,記住別忘記添加讀連絡人和寫連絡人的許可權。
再開啟src\com\android\providers\contacts\ContactsProvider2.java檔案,看一下路徑是什麼。
static { // Contacts URI matching table matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID); ... ... matcher.addURI(ContactsContract.AUTHORITY, "data", DATA); matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID); ... ...}
可以得知,查看raw_contacts的路徑就是raw_contacts,查看data表的路徑就是data。
-
該準備的東西都準備完畢了,那麼就進入到代碼的步驟吧。
-
① 在raw_contacts表中查詢出來連絡人的ID集合
-
② 以連絡人ID為過濾條件,在view_data視圖中,尋找mimetype和data1
以下是代碼:
// 在raw_contacts表中查詢出來連絡人的ID集合Uri rawContactsUri = Uri.parse("content://com.android.contacts/raw_contacts");Uri viewDataUri = Uri.parse("content://com.android.contacts/data");Cursor rawContactsCursor = getContentResolver().query(rawContactsUri, null, null, null, null);if (rawContactsCursor != null) { while (rawContactsCursor.moveToNext()) { int columnIndex = rawContactsCursor.getColumnIndex("contact_id"); String contact_id = rawContactsCursor.getString(columnIndex); // System.out.println("連絡人ID:" + contact_id); // 以連絡人ID為過濾條件,在view_data視圖中,尋找mimetype和data1 Cursor viewDataCursor = getContentResolver().query(viewDataUri, new String[] { "mimetype", "data1" }, "contact_id=?", new String[] { contact_id }, null); if (viewDataCursor != null) { while (viewDataCursor.moveToNext()) { String mimetype = viewDataCursor.getString(0); String data1 = viewDataCursor.getString(1); if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) { System.out.println("連絡人ID:" + contact_id + ",電話:" + data1); } else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) { System.out.println("連絡人ID:" + contact_id + ",email:" + data1); } else if ("vnd.android.cursor.item/name".equals(mimetype)) { System.out.println("連絡人ID:" + contact_id + ",連絡人姓名:" + data1); } } viewDataCursor.close(); } } rawContactsCursor.close();}
利用內容提供者插入連絡人
-
插入連絡人分為以下幾步:
-
①手動插入連絡人ID;ID值為資料庫中總條目數加1;
-
②再依次向
data表中插入連絡人資料。
整體的代碼還是比較清晰地,請看下吧:
String name = et_name.getText().toString().trim();String phone = et_phone.getText().toString().trim();int contact_id = 0;Uri rawContactsUri = Uri.parse("content://com.android.contacts/raw_contacts");Uri viewDataUri = Uri.parse("content://com.android.contacts/data");// 手動插入連絡人ID;ID值為資料庫中總條目數加1;Cursor rawContactsCursor = getContentResolver().query(rawContactsUri, null, null, null, null);if (rawContactsCursor != null) { int count = rawContactsCursor.getCount(); contact_id = count + 1; rawContactsCursor.close();}// 插入連絡人IDContentValues values = new ContentValues();values.put("contact_id", contact_id);getContentResolver().insert(rawContactsUri, values);// 再依次向`data`表中插入連絡人資料。ContentValues nameValues = new ContentValues();nameValues.put("raw_contact_id", contact_id);nameValues.put("mimetype", "vnd.android.cursor.item/name");nameValues.put("data1", name);getContentResolver().insert(viewDataUri, nameValues);ContentValues phoneValues = new ContentValues();phoneValues.put("raw_contact_id", contact_id);phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2");phoneValues.put("data1", phone);getContentResolver().insert(viewDataUri, phoneValues);
以上都是範例程式碼,如果要實際使用還需要做很多很多工作。
內容觀察者的原理
ContentObserver——內容觀察者,目的是觀察(捕捉)特定Uri引起的資料庫的變化,繼而做一些相應的處理,它類似於資料庫技術中的觸發器(Trigger),當ContentObserver所觀察的Uri發生變化時,便會觸發它。
-
使用ContentObserver的情況主要有一下兩者情況:
-
①需要頻繁檢測的資料庫或者某個資料是否發生改變,如果使用線程去操作,很不經濟而且很耗時 ;
-
②在使用者不知曉的情況下對資料庫做一些事件,比如:悄悄發送資訊、拒絕接受簡訊黑名單等;
利用內容觀察者監聽系統應用程式資料庫或者自己應用程式資料庫的變化
這裡就給出一個簡單的例子吧。博主的學識還是太淺了,剛才瀏覽了網上一些人寫的內容觀察者的部落格,覺得自己會的東西還是太少了。小小總結一下。當以後用到的時候再仔細的總結。
使用觀察者監聽系統應用的變化
監聽資料庫的變化,主要使用到了ContentResler類中的registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)方法。第一個參數是我們監視的Uri,第二個參數為true時會匹配所有以我們指定的Uri開頭的地址,第三個參數就是我們的觀察者了。
假設我們要監聽系統簡訊資料庫的變化,可以這麼做:
Uri uri = Uri.parse("content://sms");getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); System.out.println("簡訊內容發生改變了"); }});
已經知道了如何監視一個Uri的變化後,監聽我們自己應用程式資料庫的變化,只需要將Uri修改一下就可以了。但是,這裡還差一步,當我們通過內容提供者去訪問已有資料後,還要通知一下,這樣才可以讓觀察者接收到內容改變的訊息。
這裡就使用到了ContentReslover中的notifyChange(Uri uri, ContentObserver observer)方法。
// 提供給外部應用調用的查詢方法@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { int MATCH_CODE = MURI_MATCHER.match(uri); if (MATCH_CODE == QUERY_SUCCESS) { // 大喊一聲,資料發生改變了 getContext().getContentResolver().notifyChange(uri, null); return db.query(ACCOUNT, projection, selection, selectionArgs, null, null, sortOrder); } return null;}
這兩篇部落格寫的比較倉促,請多多包含了。
Android基礎筆記(十四)- 內容提供者讀取連絡人