Android通訊錄的管理(聯(lián)系人的增刪改查)

Android中的聯(lián)系人存儲是通過ContentProvider實現(xiàn)的骨杂。因此APP對系統(tǒng)通訊錄進行操作涉及到ContentProvider接口的使用肛炮。

通訊錄存儲常用的數(shù)據(jù)庫表

使用有關(guān)接口前,首先了解一下通訊錄數(shù)據(jù)庫中常用的數(shù)據(jù)庫表:

表名 表用途
contacts 聯(lián)系人表欠橘,存儲了實際的聯(lián)系人姓名逞带,頭像铡原,最后通話時間等信息。
會對實際的聯(lián)系人數(shù)據(jù)進行一定去重由捎。
raw_contacts 實際的聯(lián)系人數(shù)據(jù)表兔综,每一行是一個單獨的聯(lián)系人。
會存在多行對應(yīng)同一個contacts表中條目的情況狞玛。
data 所有聯(lián)系人信息數(shù)據(jù)软驰。通過raw_contact_id外鍵與raw_contacts建立聯(lián)系。



contacts與raw_contacts的區(qū)分

一個raw_contacts對應(yīng)一個聯(lián)系人心肪,程序中或用戶操作生成新的聯(lián)系人锭亏,就是直接在這個表中插入新條目。

contacts是實際通訊錄中顯示的聯(lián)系人——當(dāng)raw_contacts中存在相同名稱的聯(lián)系人時硬鞍,系統(tǒng)會將這幾個聯(lián)系人合并慧瘤。

(例如通過通訊錄添加兩個名字相同的名片,這時系統(tǒng)會提示是否要對這兩個名片進行合并固该。)

data表

1.data表每一行都是一項數(shù)據(jù)(姓名锅减,電話,Email伐坏,網(wǎng)址怔匣,生日等)。并通過外鍵raw_contacts_idraw_contacts表關(guān)聯(lián)起來桦沉。

2.由1所述每瞒,一個聯(lián)系人根據(jù)情況會有多條data數(shù)據(jù)。數(shù)據(jù)存儲在data1-15這15列中纯露。

例如某一行存儲電話號碼剿骨,那么在表中data1列存儲電話號碼,data2列存儲號碼類型(單位/家庭/組織等)苔埋。

又例如某一行存儲的聯(lián)系人姓名懦砂,那么data1列存儲顯示在界面上的名稱,data2存儲名,data3存儲姓荞膘。

3.依數(shù)據(jù)類型不同罚随,data1-14的含義會不同;data15默認存儲blob二進制形式的數(shù)據(jù)羽资。

4.那么又如何區(qū)分不同行數(shù)據(jù)的真實類型呢淘菩?是通過data表中mimetype_id列的值(整形)來進行區(qū)分。根據(jù)這一列的取值屠升,對data1-14進行不同的解析潮改。mimetype_id中數(shù)值與類型的對應(yīng)關(guān)系在mimetypes表中定義。例如:

_id mimetypes 含義
1 vnd.android.cursor.item/email_v2 電子郵件
2 vnd.android.cursor.item/im 即時通訊
3 vnd.android.cursor.item/nickname 昵稱



在編寫代碼時腹暖,實際傳入的是mimetypes中的字符串參數(shù)汇在,而不是ID值。

以上數(shù)據(jù)庫中所有表及字段的定義脏答,都可在android.provider.ContactsContract中找到糕殉。

通訊錄存儲的數(shù)據(jù)文件在/data/data/com.android.providers.contacts/databases/目錄下,需要手機獲取Root權(quán)限殖告。

對通訊錄進行增刪改查

按電話號碼查詢聯(lián)系人

Uri phoneUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(phone));

ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(phoneUri, new String[]{ContactsContract.CommonDataKinds.Phone._ID, 
    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.CONTACT_ID}, null, null, null);

while (cursor.moveToNext()) {
    int id = cursor.getInt(0);
    String name = cursor.getString(1);
    int contactId = cursor.getInt(2);
    if (name.equals(user.getName())) {
        deleteList.add(id);
    }
}



注意這里使用的URL是ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI阿蝶,而不是ContactsContract.PhoneLookup.CONTENT_FILTER_URI
這是由于PhoneLookup.CONTENT_FILTER_URI會以用戶提供的手機號查詢后黄绩,再使用標(biāo)準(zhǔn)格式的電話號碼再次查找羡洁,會返回兩個相同的結(jié)果。例如用戶提供了號碼17000000000爽丹,那么程序會先查詢17000000000號碼筑煮,再查詢+86 17000000000,并且兩次查詢都會成功习劫。

查詢通訊錄中所有聯(lián)系人

Uri uri = ContactsContract.Data.CONTENT_URI;
ContentResolver resolver = context.getContentResolver();
Cursor cursorUser = resolver.query(uri, new String[]{ContactsContract.CommonDataKinds.Phone._ID,
    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID}, null, null, null);

while( cursorUser.moveToNext()) {
    int id = cursorUser.getInt(0); // 按上面數(shù)組的聲明順序獲取
    String name = cursorUser.getString(1);
    int rawContactsId = cursorUser.getInt(2);
}

刪除聯(lián)系人某項數(shù)據(jù)(Data中某一項)

int id; // data表中對應(yīng)的id值
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
    .withSelection(ContactsContract.Data._ID + "=?", new String[]{String.valueOf(d)})
    .build());

context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);

向通訊錄中添加新的聯(lián)系人

ArrayList<ContentProviderOperation> ops = new ArrayList<>();
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)  // 此處傳入null添加一個raw_contact空數(shù)據(jù)
    .build());
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)  // RAW_CONTACT_ID是第一個事務(wù)添加得到的咆瘟,因此這里傳入0,applyBatch返回的ContentProviderResult[]數(shù)組中第一項
    .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
    .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, userName)
    .build());
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
    .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
    .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber)
    .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_WORK)
    .build());

context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);

其中withValueBackReference接口傳參代表此鍵值是事務(wù)中之前操作得到的結(jié)果诽里,因此需要傳入之前事務(wù)的index值袒餐。由于添加聯(lián)系人是在第一步操作,對應(yīng)結(jié)果數(shù)組的第0項谤狡。

向已有聯(lián)系人中添加新數(shù)據(jù)

ArrayList<ContentProviderOperation> ops = new ArrayList<>();
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    .withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactsId)  // 這里關(guān)鍵是傳入正確的raw_contacts_id值
    .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
    .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber)
    .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_WORK)
    .build());

context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);

參考文檔

通訊錄Android官方文檔灸眼,常用數(shù)據(jù)庫表及相應(yīng)含義
PhoneLookup.CONTENT_FILTER_URI returns twice the same contact
What are the semantics of withValueBackReference?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市墓懂,隨后出現(xiàn)的幾起案子焰宣,更是在濱河造成了極大的恐慌,老刑警劉巖捕仔,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匕积,死亡現(xiàn)場離奇詭異盈罐,居然都是意外死亡,警方通過查閱死者的電腦和手機闪唆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門盅粪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悄蕾,你說我怎么就攤上這事票顾。” “怎么了帆调?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵奠骄,是天一觀的道長。 經(jīng)常有香客問我番刊,道長含鳞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任撵枢,我火速辦了婚禮民晒,結(jié)果婚禮上精居,老公的妹妹穿的比我還像新娘锄禽。我一直安慰自己,他們只是感情好靴姿,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布沃但。 她就那樣靜靜地躺著,像睡著了一般佛吓。 火紅的嫁衣襯著肌膚如雪宵晚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天维雇,我揣著相機與錄音淤刃,去河邊找鬼。 笑死吱型,一個胖子當(dāng)著我的面吹牛逸贾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播津滞,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼铝侵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了触徐?” 一聲冷哼從身側(cè)響起咪鲜,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撞鹉,沒想到半個月后疟丙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颖侄,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年享郊,在試婚紗的時候發(fā)現(xiàn)自己被綠了发皿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡拂蝎,死狀恐怖穴墅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情温自,我是刑警寧澤玄货,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站悼泌,受9級特大地震影響松捉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜馆里,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一隘世、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸠踪,春花似錦丙者、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至评汰,卻和暖如春纷捞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背被去。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工主儡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惨缆。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓糜值,卻偏偏與公主長得像,于是被迫代替她去往敵國和親踪央。 傳聞我的和親對象是個殘疾皇子臀玄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容