1. 程式人生 > >Pro Android學習筆記(一六十):聯絡人API(3):聯絡人資料

Pro Android學習筆記(一六十):聯絡人API(3):聯絡人資料

聯絡人資訊

檢視reference中android.provider.ContactsContract.CommonDataKinds可以知道該版本的Android API聯絡人中帶有哪些資訊。在API level 19中,我們看到有以下的資訊:

而各個資訊,例如Email,又有著他的結構,我們可以繼續檢視ContactsContract.CommondataKinds.Email。

聯絡人資料庫

一個賬號account擁有其自己的聯絡人,這些聯絡人稱為raw contacts,具有自己的資料資訊,例如email,phone號碼,地址等。Android在people應用中提供了raw contacts的整合,不同賬號的raw contact進行整合,只在列表中出現一次。

聯絡人資訊存放在/data/data/com.android.providers.contacts/databases中。

在profile.db中存放的是personal profile賬號的下屬聯絡人。我們將contacts2.db和profile.db匯出來,通過SQLiteManager看看裡面的內容,也可以用SDK中的platform-tools/sqlite3.exe命令,具體的參見Pro Android學習筆記(五):瞭解Content Provider(上)

sqlite> .schema raw_contacts;
sqlite> .tables
_sync_state               phone_lookup              view_data_usage_stat
_sync_state_metadata      photo_files               view_entities


accounts                  properties                view_groups
agg_exceptions            raw_contacts              view_raw_contacts
android_metadata          search_index              view_raw_entities
calls                     search_index_content      view_stream_items
contacts                  search_index_docsize      view_v1_contact_methods
data
                      search_index_segdir       view_v1_extensions
data_usage_stat           search_index_segments     view_v1_group_membership
default_directory         search_index_stat         view_v1_groups
deleted_contacts          settings                  view_v1_organizations
directories               status_updates            view_v1_people
groups                    stream_item_photos        view_v1_phones
mimetypes                 stream_items              view_v1_photos
name_lookup               v1_settings               visible_contacts
nickname_lookup           view_contacts             voicemail_status
packages                  view_data

raw contacts

每個賬號下面有raw contacts表格,記錄各自的raw contacts資訊。通過圖形工具,我們來檢視raw_contacts的定義:

CREATE TABLE raw_contacts (
    _id INTEGER PRIMARY KEY AUTOINCREMENT
    account_id INTEGER REFERENCES accounts(_id),表明raw contact屬於哪個account,可以在accounts的表中,根據_id查到account_name和account_type。
    sourceid TEXT表明這個raw contact在這個account中是唯一的,例如account為Google email賬號,這裡是使用者的email ID。如何組織管理賬號以及裡面聯絡人和手機制造商有關,這裡也可能weiNull。
    raw_contact_is_read_only INTEGER NOT NULL DEFAULT 0,
    version INTEGER NOT NULL DEFAULT 1,
    dirty INTEGER NOT NULL DEFAULT 0,
    deleted INTEGER NOT NULL DEFAULT 0,
    contact_id INTEGER REFERENCES contacts(_id),
表格contacts中是整合的賬號表,這裡是對應整合表contacts中的_id。
    aggregation_mode INTEGER NOT NULL DEFAULT 0,
    aggregation_needed INTEGER NOT NULL DEFAULT 1,
    custom_ringtone TEXT,
    send_to_voicemail INTEGER NOT NULL DEFAULT 0,
    times_contacted INTEGER NOT NULL DEFAULT 0,
    last_time_contacted INTEGER,
    starred INTEGER NOT NULL DEFAULT 0,
    pinned INTEGER NOT NULL DEFAULT 2147483647,

    display_name TEXT,這是隻讀,有表格data中的的data列觸發生成。
    display_name_alt TEXT,
    display_name_source INTEGER NOT NULL DEFAULT 0,
    phonetic_name TEXT,
    phonetic_name_style TEXT,
    sort_key TEXT COLLATE PHONEBOOK,
    phonebook_label TEXT,
    phonebook_bucket INTEGER,
    sort_key_alt TEXT COLLATE PHONEBOOK,
    phonebook_label_alt TEXT,
    phonebook_bucket_alt INTEGER,
    name_verified INTEGER NOT NULL DEFAULT 0,

    sync1 TEXT,  sync2 TEXT,  sync3 TEXT, sync4 TEXT )sync用於裝置上contact與伺服器之間的同步。

我們也可以在android.providers.ContactsContact.RawContact中檢視相關資訊。

使用SDK的定義可以特別在不同SDK版本表格出現列的增刪改情況,仍可以安全地使用。但是SDK中看起來很複雜和繁瑣,不容易理清,所以我們直接看錶格定義。自開放式,我們將使用SDK裡面的定義。

data表

在raw_contacts表格並沒有存放contact的所有資訊,例如電話號碼、郵件等,是存放在data表中。

CREATE TABLE data (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
package_id INTEGER REFERENCES package(_id),
mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL,表明是哪個資訊,例如名字、電話號碼等等,mimetype也將決定資訊儲存data1~data15的含義 。
raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL,data表和raw_contacts表之間相互索引。raw_contact_id是可以重複的,用於分別存放不同的資訊。
is_read_only INTEGER NOT NULL DEFAULT 0,
is_primary INTEGER NOT NULL DEFAULT 0,
is_super_primary INTEGER NOT NULL DEFAULT 0,
data_version INTEGER NOT NULL DEFAULT 0,
data1 TEXT,data2 TEXT,data3 TEXT,data4 TEXT,data5 TEXT,data6 TEXT,data7 TEXT,data8 TEXT,data9 TEXT,data10 TEXT,data11 TEXT,data12 TEXT,data13 TEXT,data14 TEXT,data15 TEXT,
data_sync1 TEXT, data_sync2 TEXT, data_sync3 TEXT, data_sync4 TEXT )

我們看一個查詢的結果:

下面是表mimetype的內容,通過data中的mime_type_id可以檢索具體的型別。用表格來儲存型別,而不是進行固化,這可以靈活地增加、刪除和修改定義,是個好的設計。

data表的各列也可以檢視ContactsContract.Data,具體的某個mimetype對應的data1~data15各標識什麼,可以查閱ContactsContract.CommonDataKinds.*,例如ContactsContract.CommonDataKinds.Phone。

整合聯絡人:表contacts

在不同的賬號下面可以會有相同的聯絡人,Android進行了整合,存放在可讀表contacts中,raw_contacts和data表有trigger可以觸發來填寫或者修改contacts表的資料。

CREATE TABLE contacts (
    _id INTEGER PRIMARY KEY AUTOINCREMENT,
    name_raw_contact_id INTEGER REFERENCES raw_contacts(_id),
    photo_id INTEGER REFERENCES data(_id),  
    photo_file_id INTEGER REFERENCES photo_files(_id),
    custom_ringtone TEXT,
    send_to_voicemail INTEGER NOT NULL DEFAULT 0,
    times_contacted INTEGER NOT NULL DEFAULT 0,
    last_time_contacted INTEGER,
    starred INTEGER NOT NULL DEFAULT 0,
    has_phone_number INTEGER NOT NULL DEFAULT 0,
    has_valid_sip_account INTEGER NOT NULL DEFAULT 0,
    lookup TEXT, contacts表格資料是系統根據raw contacts的資料進行生成和修改,這個表的資料會根據raw contacts的變化而變化,採用 將_id代表某個聯絡人,並將之作為索引,在某些情況下並不合適,因此引入了lookup,lookup是進行過編碼,含有account以及其下的raw_contact聯絡人ID,可將之作為生成LookUpURI進行查詢,獲取最新整合聯合人的資訊。我們在下一學習的小例子進行演示。
    status_update_id INTEGER REFERENCES data(_id))

Andriod如何判斷是否可以進行判斷。如果兩個raw_contacts名字相同,或者first name和last name的順序不同,但內容一致,或者是縮寫或暱稱(例如Bob和Robert);如果name只有first name或者last name,將查詢其他屬性,例如電話號碼、郵件,如果其他屬性也匹配,可以進行整合;如果raw contact沒有name,那麼將查詢其他屬性。

基於這些判斷進行整合是可能出現誤判的。在raw_contacts中aggregation_mode列,有三個值:AGGEREGATION_MODE_DEFAULT,AGGERGATION_MODE_DISABLED(禁止整合,如果已經整合,將從整合中分離出來,為其分配一個新的contact ID),以及AGGEREGATION_MODE_SUSPENDED(當屬性變化是,並不整合到整合聯絡人中)。

檢視:view_contacts

API並不直接暴露contacts表,而是通過檢視view_contacts讀取。當通過URI:ContactsContract.Contacts.CONTENT_URI來查詢時,實際查詢的是view_contacts。

CREATE VIEW view_contacts AS

SELECT contacts._id AS _id,
contacts.custom_ringtone AS custom_ringtone,
name_raw_contact.display_name_source AS display_name_source,
name_raw_contact.display_name AS display_name,
name_raw_contact.display_name_alt AS display_name_alt,
name_raw_contact.phonetic_name AS phonetic_name,
name_raw_contact.phonetic_name_style AS phonetic_name_style,
name_raw_contact.sort_key AS sort_key,
name_raw_contact.sort_key_alt AS sort_key_alt,
has_phone_number,
has_valid_sip_account,
name_raw_contact_id,
lookup,
photo_id,
photo_file_id,
CAST(EXISTS (SELECT _id FROM visible_contacts WHERE contacts._id=visible_contacts._id) AS INTEGER) AS in_visible_group,
status_update_id,
contacts.last_time_contacted AS last_time_contacted,
contacts.send_to_voicemail AS send_to_voicemail,
contacts.starred AS starred,
contacts.times_contacted AS times_contacted,
(CASE WHEN photo_file_id IS NULL    
    THEN (
        CASE WHEN photo_id IS NULL OR photo_id=0
            THEN NULL
        ELSE 'content://com.android.contacts/contacts/'||contacts._id|| '/photo' END)
    ELSE 'content://com.android.contacts/display_photo/'||photo_file_id END) AS photo_uri,
(CASE WHEN photo_id IS NULL OR photo_id=0
    THEN NULL
    ELSE 'content://com.android.contacts/contacts/'||contacts._id|| '/photo' END) AS photo_thumb_uri,
0 AS is_user_profile,
name_raw_contact.view_mode AS view_mode,
name_raw_contact.sim_index AS sim_index

FROM contacts JOIN raw_contacts AS name_raw_contact ON(name_raw_contact_id=name_raw_contact._id)

檢視:view_entities

合併raw_contacts和data,可以獲得每個raw contacts的全部資訊。

CREATE VIEW view_entities AS

SELECT raw_contacts.contact_id AS _id,
raw_contacts.contact_id AS contact_id,
raw_contacts.deleted AS deleted,
is_primary,
is_super_primary,
data_version,
data.package_id,
package AS res_package,
data.mimetype_id,
mimetype AS mimetype,
is_read_only,
data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, data11, data12, data13, data14, data15,
data_sync1, data_sync2, data_sync3, data_sync4,
raw_contacts.account_id,
accounts.account_name AS account_name,
accounts.account_type AS account_type,
accounts.data_set AS data_set,

(CASE WHEN accounts.data_set IS NULL
    THEN accounts.account_type
    ELSE accounts.account_type||'/'||accounts.data_set END) AS account_type_and_data_set,

raw_contacts.sourceid AS sourceid,
raw_contacts.name_verified AS name_verified,
raw_contacts.version AS version,
raw_contacts.dirty AS dirty,
raw_contacts.sync1 AS sync1,raw_contacts.sync2 AS sync2,raw_contacts.sync3 AS sync3,raw_contacts.sync4 AS sync4,
contacts.custom_ringtone AS custom_ringtone,
name_raw_contact.display_name_source AS display_name_source,
name_raw_contact.display_name AS display_name,
name_raw_contact.display_name_alt AS display_name_alt,
name_raw_contact.phonetic_name AS phonetic_name,
name_raw_contact.phonetic_name_style AS phonetic_name_style,
name_raw_contact.sort_key AS sort_key,
name_raw_contact.phonebook_label AS phonebook_label,
name_raw_contact.phonebook_bucket AS phonebook_bucket,
name_raw_contact.sort_key_alt AS sort_key_alt,
name_raw_contact.phonebook_label_alt AS phonebook_label_alt,
name_raw_contact.phonebook_bucket_alt AS phonebook_bucket_alt,
has_phone_number,
name_raw_contact_id,
lookup,
photo_id,
photo_file_id,
CAST(EXISTS (SELECT _id FROM visible_contacts WHERE contacts._id=visible_contacts._id) AS INTEGER) AS in_visible_group,
status_update_id,
contacts.contact_last_updated_timestamp,
contacts.last_time_contacted AS last_time_contacted,
contacts.send_to_voicemail AS send_to_voicemail,
contacts.starred AS starred,
contacts.pinned AS pinned,
contacts.times_contacted AS times_contacted,
(CASE WHEN photo_file_id IS NULL
    THEN (CASE WHEN photo_id IS NULL OR photo_id=0
        THEN NULL
        ELSE 'content://com.android.contacts/contacts/'||raw_contacts.contact_id|| '/photo' END)
    ELSE 'content://com.android.contacts/display_photo/'||photo_file_id END) AS photo_uri,
(CASE WHEN photo_id IS NULL OR photo_id=0
    THEN NULL ELSE 'content://com.android.contacts/contacts/'||raw_contacts.contact_id|| '/photo' END)
    AS photo_thumb_uri,
0 AS is_user_profile,
data_sync1, data_sync2, data_sync3, data_sync4,
raw_contacts._id AS raw_contact_id,
data._id AS data_id,
groups.sourceid AS group_sourceid  FROM raw_contacts
    JOIN accounts ON (raw_contacts.account_id=accounts._id)
    JOIN contacts ON (raw_contacts.contact_id=contacts._id)
    JOIN raw_contacts AS name_raw_contact ON(name_raw_contact_id=name_raw_contact._id)
    LEFT OUTER JOIN data ON (data.raw_contact_id=raw_contacts._id)
    LEFT OUTER JOIN packages ON (data.package_id=packages._id)
    LEFT OUTER JOIN mimetypes ON (data.mimetype_id=mimetypes._id)
    LEFT OUTER JOIN groups ON (mimetypes.mimetype='vnd.android.cursor.item/group_membership' AND groups._id=data.data1)