聯絡人Contacts中資料庫contacts.db簡要分析
阿新 • • 發佈:2019-02-03
在Contacts中,contacts2.db資料庫位於/data/data/com.android.providers.contacts/databases裡,contacts2.db中有很多張表,下面主要看下幾張重要的表
裡面定義了contact2.db資料裡表名和檢視名
視圖表的定義public interface Tables { public static final String CONTACTS = "contacts"; public static final String DELETED_CONTACTS = "deleted_contacts"; public static final String RAW_CONTACTS = "raw_contacts"; public static final String STREAM_ITEMS = "stream_items"; public static final String STREAM_ITEM_PHOTOS = "stream_item_photos"; public static final String PHOTO_FILES = "photo_files"; public static final String PACKAGES = "packages"; public static final String MIMETYPES = "mimetypes"; public static final String PHONE_LOOKUP = "phone_lookup"; public static final String NAME_LOOKUP = "name_lookup"; public static final String AGGREGATION_EXCEPTIONS = "agg_exceptions"; public static final String SETTINGS = "settings"; public static final String DATA = "data"; public static final String GROUPS = "groups"; public static final String PRESENCE = "presence"; public static final String AGGREGATED_PRESENCE = "agg_presence"; public static final String NICKNAME_LOOKUP = "nickname_lookup"; public static final String CALLS = "calls"; public static final String STATUS_UPDATES = "status_updates"; public static final String PROPERTIES = "properties"; public static final String ACCOUNTS = "accounts"; public static final String VISIBLE_CONTACTS = "visible_contacts"; public static final String DIRECTORIES = "directories"; public static final String DEFAULT_DIRECTORY = "default_directory"; public static final String SEARCH_INDEX = "search_index"; public static final String VOICEMAIL_STATUS = "voicemail_status"; public static final String PRE_AUTHORIZED_URIS = "pre_authorized_uris";
public interface Views { public static final String DATA = "view_data"; public static final String RAW_CONTACTS = "view_raw_contacts"; public static final String CONTACTS = "view_contacts"; public static final String ENTITIES = "view_entities"; public static final String RAW_ENTITIES = "view_raw_entities"; public static final String GROUPS = "view_groups"; public static final String DATA_USAGE_STAT = "view_data_usage_stat"; public static final String STREAM_ITEMS = "view_stream_items"; }
ContactsDatabaseHelper繼承SQLiteOpenHelper在onCreate方法中建立了資料庫
@Override public void onCreate(SQLiteDatabase db) { Log.i(TAG, "Bootstrapping database version: " + DATABASE_VERSION); mSyncState.createDatabase(db); // Create the properties table first so the create time is available as soon as possible. // The create time is needed by BOOT_COMPLETE to send broadcasts. db.execSQL("CREATE TABLE " + Tables.PROPERTIES + " (" + PropertiesColumns.PROPERTY_KEY + " TEXT PRIMARY KEY, " + PropertiesColumns.PROPERTY_VALUE + " TEXT " + ");"); setProperty(db, DbProperties.DATABASE_TIME_CREATED, String.valueOf( System.currentTimeMillis())); db.execSQL("CREATE TABLE " + Tables.ACCOUNTS + " (" + AccountsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + AccountsColumns.ACCOUNT_NAME + " TEXT, " + AccountsColumns.ACCOUNT_TYPE + " TEXT, " + AccountsColumns.DATA_SET + " TEXT" + ");"); // One row per group of contacts corresponding to the same person db.execSQL("CREATE TABLE " + Tables.CONTACTS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + Contacts.NAME_RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," + Contacts.PHOTO_ID + " INTEGER REFERENCES data(_id)," + Contacts.PHOTO_FILE_ID + " INTEGER REFERENCES photo_files(_id)," + Contacts.CUSTOM_RINGTONE + " TEXT," + Contacts.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0," + Contacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," + Contacts.LAST_TIME_CONTACTED + " INTEGER," + Contacts.STARRED + " INTEGER NOT NULL DEFAULT 0," + Contacts.PINNED + " INTEGER NOT NULL DEFAULT " + PinnedPositions.UNPINNED + "," + Contacts.HAS_PHONE_NUMBER + " INTEGER NOT NULL DEFAULT 0," + Contacts.LOOKUP_KEY + " TEXT," + ContactsColumns.LAST_STATUS_UPDATE_ID + " INTEGER REFERENCES data(_id)," + Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " INTEGER" + ");"); ContactsTableUtil.createIndexes(db); // deleted_contacts table DeletedContactsTableUtil.create(db); // Raw_contacts table db.execSQL("CREATE TABLE " + Tables.RAW_CONTACTS + " (" + RawContacts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + RawContactsColumns.ACCOUNT_ID + " INTEGER REFERENCES " + Tables.ACCOUNTS + "(" + AccountsColumns._ID + ")," + RawContacts.SOURCE_ID + " TEXT," + RawContacts.BACKUP_ID + " TEXT," + RawContacts.RAW_CONTACT_IS_READ_ONLY + " INTEGER NOT NULL DEFAULT 0," + RawContacts.VERSION + " INTEGER NOT NULL DEFAULT 1," + RawContacts.DIRTY + " INTEGER NOT NULL DEFAULT 0," + RawContacts.DELETED + " INTEGER NOT NULL DEFAULT 0," + RawContacts.CONTACT_ID + " INTEGER REFERENCES contacts(_id)," + RawContacts.AGGREGATION_MODE + " INTEGER NOT NULL DEFAULT " + RawContacts.AGGREGATION_MODE_DEFAULT + "," + RawContactsColumns.AGGREGATION_NEEDED + " INTEGER NOT NULL DEFAULT 1," + RawContacts.CUSTOM_RINGTONE + " TEXT," + RawContacts.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0," + RawContacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," + RawContacts.LAST_TIME_CONTACTED + " INTEGER," + RawContacts.STARRED + " INTEGER NOT NULL DEFAULT 0," + RawContacts.PINNED + " INTEGER NOT NULL DEFAULT " + PinnedPositions.UNPINNED + "," + RawContacts.DISPLAY_NAME_PRIMARY + " TEXT," + RawContacts.DISPLAY_NAME_ALTERNATIVE + " TEXT," + RawContacts.DISPLAY_NAME_SOURCE + " INTEGER NOT NULL DEFAULT " + DisplayNameSources.UNDEFINED + "," + RawContacts.PHONETIC_NAME + " TEXT," + // TODO: PHONETIC_NAME_STYLE should be INTEGER. There is a // mismatch between how the column is created here (TEXT) and // how it is created in upgradeToVersion205 (INTEGER). RawContacts.PHONETIC_NAME_STYLE + " TEXT," + RawContacts.SORT_KEY_PRIMARY + " TEXT COLLATE " + ContactsProvider2.PHONEBOOK_COLLATOR_NAME + "," + RawContactsColumns.PHONEBOOK_LABEL_PRIMARY + " TEXT," + RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY + " INTEGER," + RawContacts.SORT_KEY_ALTERNATIVE + " TEXT COLLATE " + ContactsProvider2.PHONEBOOK_COLLATOR_NAME + "," + RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE + " TEXT," + RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE + " INTEGER," + RawContactsColumns.NAME_VERIFIED_OBSOLETE + " INTEGER NOT NULL DEFAULT 0," + RawContacts.SYNC1 + " TEXT, " + RawContacts.SYNC2 + " TEXT, " + RawContacts.SYNC3 + " TEXT, " + RawContacts.SYNC4 + " TEXT " + ");"); db.execSQL("CREATE INDEX raw_contacts_contact_id_index ON " + Tables.RAW_CONTACTS + " (" + RawContacts.CONTACT_ID + ");"); db.execSQL("CREATE INDEX raw_contacts_source_id_account_id_index ON " + Tables.RAW_CONTACTS + " (" + RawContacts.SOURCE_ID + ", " + RawContactsColumns.ACCOUNT_ID + ");"); db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS raw_contacts_backup_id_account_id_index ON " + Tables.RAW_CONTACTS + " (" + RawContacts.BACKUP_ID + ", " + RawContactsColumns.ACCOUNT_ID + ");"); db.execSQL("CREATE TABLE " + Tables.STREAM_ITEMS + " (" + StreamItems._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + StreamItems.RAW_CONTACT_ID + " INTEGER NOT NULL, " + StreamItems.RES_PACKAGE + " TEXT, " + StreamItems.RES_ICON + " TEXT, " + StreamItems.RES_LABEL + " TEXT, " + StreamItems.TEXT + " TEXT, " + StreamItems.TIMESTAMP + " INTEGER NOT NULL, " + StreamItems.COMMENTS + " TEXT, " + StreamItems.SYNC1 + " TEXT, " + StreamItems.SYNC2 + " TEXT, " + StreamItems.SYNC3 + " TEXT, " + StreamItems.SYNC4 + " TEXT, " + "FOREIGN KEY(" + StreamItems.RAW_CONTACT_ID + ") REFERENCES " + Tables.RAW_CONTACTS + "(" + RawContacts._ID + "));"); db.execSQL("CREATE TABLE " + Tables.STREAM_ITEM_PHOTOS + " (" + StreamItemPhotos._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + StreamItemPhotos.STREAM_ITEM_ID + " INTEGER NOT NULL, " + StreamItemPhotos.SORT_INDEX + " INTEGER, " + StreamItemPhotos.PHOTO_FILE_ID + " INTEGER NOT NULL, " + StreamItemPhotos.SYNC1 + " TEXT, " + StreamItemPhotos.SYNC2 + " TEXT, " + StreamItemPhotos.SYNC3 + " TEXT, " + StreamItemPhotos.SYNC4 + " TEXT, " + "FOREIGN KEY(" + StreamItemPhotos.STREAM_ITEM_ID + ") REFERENCES " + Tables.STREAM_ITEMS + "(" + StreamItems._ID + "));"); db.execSQL("CREATE TABLE " + Tables.PHOTO_FILES + " (" + PhotoFiles._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + PhotoFiles.HEIGHT + " INTEGER NOT NULL, " + PhotoFiles.WIDTH + " INTEGER NOT NULL, " + PhotoFiles.FILESIZE + " INTEGER NOT NULL);"); // TODO readd the index and investigate a controlled use of it // db.execSQL("CREATE INDEX raw_contacts_agg_index ON " + Tables.RAW_CONTACTS + " (" + // RawContactsColumns.AGGREGATION_NEEDED + // ");"); // Package name mapping table db.execSQL("CREATE TABLE " + Tables.PACKAGES + " (" + PackagesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + PackagesColumns.PACKAGE + " TEXT NOT NULL" + ");"); // Mimetype mapping table db.execSQL("CREATE TABLE " + Tables.MIMETYPES + " (" + MimetypesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + MimetypesColumns.MIMETYPE + " TEXT NOT NULL" + ");"); // Mimetype table requires an index on mime type db.execSQL("CREATE UNIQUE INDEX mime_type ON " + Tables.MIMETYPES + " (" + MimetypesColumns.MIMETYPE + ");"); // Public generic data table db.execSQL("CREATE TABLE " + Tables.DATA + " (" + Data._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + DataColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + DataColumns.MIMETYPE_ID + " INTEGER REFERENCES mimetype(_id) NOT NULL," + Data.RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + Data.HASH_ID + " TEXT," + Data.IS_READ_ONLY + " INTEGER NOT NULL DEFAULT 0," + Data.IS_PRIMARY + " INTEGER NOT NULL DEFAULT 0," + Data.IS_SUPER_PRIMARY + " INTEGER NOT NULL DEFAULT 0," + Data.DATA_VERSION + " INTEGER NOT NULL DEFAULT 0," + Data.DATA1 + " TEXT," + Data.DATA2 + " TEXT," + Data.DATA3 + " TEXT," + Data.DATA4 + " TEXT," + Data.DATA5 + " TEXT," + Data.DATA6 + " TEXT," + Data.DATA7 + " TEXT," + Data.DATA8 + " TEXT," + Data.DATA9 + " TEXT," + Data.DATA10 + " TEXT," + Data.DATA11 + " TEXT," + Data.DATA12 + " TEXT," + Data.DATA13 + " TEXT," + Data.DATA14 + " TEXT," + Data.DATA15 + " TEXT," + Data.SYNC1 + " TEXT, " + Data.SYNC2 + " TEXT, " + Data.SYNC3 + " TEXT, " + Data.SYNC4 + " TEXT, " + Data.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0 " + ");"); db.execSQL("CREATE INDEX data_raw_contact_id ON " + Tables.DATA + " (" + Data.RAW_CONTACT_ID + ");"); /** * For email lookup and similar queries. */ db.execSQL("CREATE INDEX data_mimetype_data1_index ON " + Tables.DATA + " (" + DataColumns.MIMETYPE_ID + "," + Data.DATA1 + ");"); /** * For contact backup restore queries. */ db.execSQL("CREATE INDEX IF NOT EXISTS data_hash_id_index ON " + Tables.DATA + " (" + Data.HASH_ID + ");"); // Private phone numbers table used for lookup db.execSQL("CREATE TABLE " + Tables.PHONE_LOOKUP + " (" + PhoneLookupColumns.DATA_ID + " INTEGER REFERENCES data(_id) NOT NULL," + PhoneLookupColumns.RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + PhoneLookupColumns.NORMALIZED_NUMBER + " TEXT NOT NULL," + PhoneLookupColumns.MIN_MATCH + " TEXT NOT NULL" + ");"); db.execSQL("CREATE INDEX phone_lookup_index ON " + Tables.PHONE_LOOKUP + " (" + PhoneLookupColumns.NORMALIZED_NUMBER + "," + PhoneLookupColumns.RAW_CONTACT_ID + "," + PhoneLookupColumns.DATA_ID + ");"); db.execSQL("CREATE INDEX phone_lookup_min_match_index ON " + Tables.PHONE_LOOKUP + " (" + PhoneLookupColumns.MIN_MATCH + "," + PhoneLookupColumns.RAW_CONTACT_ID + "," + PhoneLookupColumns.DATA_ID + ");"); db.execSQL("CREATE INDEX phone_lookup_data_id_min_match_index ON " + Tables.PHONE_LOOKUP + " (" + PhoneLookupColumns.DATA_ID + ", " + PhoneLookupColumns.MIN_MATCH + ");"); // Private name/nickname table used for lookup. db.execSQL("CREATE TABLE " + Tables.NAME_LOOKUP + " (" + NameLookupColumns.DATA_ID + " INTEGER REFERENCES data(_id) NOT NULL," + NameLookupColumns.RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + NameLookupColumns.NORMALIZED_NAME + " TEXT NOT NULL," + NameLookupColumns.NAME_TYPE + " INTEGER NOT NULL," + "PRIMARY KEY (" + NameLookupColumns.DATA_ID + ", " + NameLookupColumns.NORMALIZED_NAME + ", " + NameLookupColumns.NAME_TYPE + ")" + ");"); db.execSQL("CREATE INDEX name_lookup_raw_contact_id_index ON " + Tables.NAME_LOOKUP + " (" + NameLookupColumns.RAW_CONTACT_ID + ");"); db.execSQL("CREATE TABLE " + Tables.NICKNAME_LOOKUP + " (" + NicknameLookupColumns.NAME + " TEXT," + NicknameLookupColumns.CLUSTER + " TEXT" + ");"); db.execSQL("CREATE UNIQUE INDEX nickname_lookup_index ON " + Tables.NICKNAME_LOOKUP + " (" + NicknameLookupColumns.NAME + ", " + NicknameLookupColumns.CLUSTER + ");"); // Groups table. db.execSQL("CREATE TABLE " + Tables.GROUPS + " (" + Groups._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + GroupsColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + GroupsColumns.ACCOUNT_ID + " INTEGER REFERENCES " + Tables.ACCOUNTS + "(" + AccountsColumns._ID + ")," + Groups.SOURCE_ID + " TEXT," + Groups.VERSION + " INTEGER NOT NULL DEFAULT 1," + Groups.DIRTY + " INTEGER NOT NULL DEFAULT 0," + Groups.TITLE + " TEXT," + Groups.TITLE_RES + " INTEGER," + Groups.NOTES + " TEXT," + Groups.SYSTEM_ID + " TEXT," + Groups.DELETED + " INTEGER NOT NULL DEFAULT 0," + Groups.GROUP_VISIBLE + " INTEGER NOT NULL DEFAULT 0," + Groups.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1," + Groups.AUTO_ADD + " INTEGER NOT NULL DEFAULT 0," + Groups.FAVORITES + " INTEGER NOT NULL DEFAULT 0," + Groups.GROUP_IS_READ_ONLY + " INTEGER NOT NULL DEFAULT 0," + Groups.SYNC1 + " TEXT, " + Groups.SYNC2 + " TEXT, " + Groups.SYNC3 + " TEXT, " + Groups.SYNC4 + " TEXT " + ");"); db.execSQL("CREATE INDEX groups_source_id_account_id_index ON " + Tables.GROUPS + " (" + Groups.SOURCE_ID + ", " + GroupsColumns.ACCOUNT_ID + ");"); db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.AGGREGATION_EXCEPTIONS + " (" + AggregationExceptionColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + AggregationExceptions.TYPE + " INTEGER NOT NULL, " + AggregationExceptions.RAW_CONTACT_ID1 + " INTEGER REFERENCES raw_contacts(_id), " + AggregationExceptions.RAW_CONTACT_ID2 + " INTEGER REFERENCES raw_contacts(_id)" + ");"); db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS aggregation_exception_index1 ON " + Tables.AGGREGATION_EXCEPTIONS + " (" + AggregationExceptions.RAW_CONTACT_ID1 + ", " + AggregationExceptions.RAW_CONTACT_ID2 + ");"); db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS aggregation_exception_index2 ON " + Tables.AGGREGATION_EXCEPTIONS + " (" + AggregationExceptions.RAW_CONTACT_ID2 + ", " + AggregationExceptions.RAW_CONTACT_ID1 + ");"); db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.SETTINGS + " (" + Settings.ACCOUNT_NAME + " STRING NOT NULL," + Settings.ACCOUNT_TYPE + " STRING NOT NULL," + Settings.DATA_SET + " STRING," + Settings.UNGROUPED_VISIBLE + " INTEGER NOT NULL DEFAULT 0," + Settings.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1" + ");"); db.execSQL("CREATE TABLE " + Tables.VISIBLE_CONTACTS + " (" + Contacts._ID + " INTEGER PRIMARY KEY" + ");"); db.execSQL("CREATE TABLE " + Tables.DEFAULT_DIRECTORY + " (" + Contacts._ID + " INTEGER PRIMARY KEY" + ");"); // The table for recent calls is here so we can do table joins on people, phones, and // calls all in one place. // NOTE: When adding a new column to Tables.CALLS, make sure to also add it to // CallLogProvider.CALL_LOG_SYNC_PROJECTION, if it is a column that should be copied to // the call log of secondary users. db.execSQL("CREATE TABLE " + Tables.CALLS + " (" + Calls._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + Calls.NUMBER + " TEXT," + Calls.NUMBER_PRESENTATION + " INTEGER NOT NULL DEFAULT " + Calls.PRESENTATION_ALLOWED + "," + Calls.DATE + " INTEGER," + Calls.DURATION + " INTEGER," + Calls.DATA_USAGE + " INTEGER," + Calls.TYPE + " INTEGER," + Calls.FEATURES + " INTEGER NOT NULL DEFAULT 0," + Calls.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," + Calls.PHONE_ACCOUNT_ID + " TEXT," + Calls.PHONE_ACCOUNT_ADDRESS + " TEXT," + Calls.PHONE_ACCOUNT_HIDDEN + " INTEGER NOT NULL DEFAULT 0," + Calls.SUB_ID + " INTEGER DEFAULT -1," + Calls.NEW + " INTEGER," + Calls.CACHED_NAME + " TEXT," + Calls.CACHED_NUMBER_TYPE + " INTEGER," + Calls.CACHED_NUMBER_LABEL + " TEXT," + Calls.COUNTRY_ISO + " TEXT," + Calls.VOICEMAIL_URI + " TEXT," + Calls.IS_READ + " INTEGER," + Calls.GEOCODED_LOCATION + " TEXT," + Calls.CACHED_LOOKUP_URI + " TEXT," + Calls.CACHED_MATCHED_NUMBER + " TEXT," + Calls.CACHED_NORMALIZED_NUMBER + " TEXT," + Calls.CACHED_PHOTO_ID + " INTEGER NOT NULL DEFAULT 0," + Calls.CACHED_PHOTO_URI + " TEXT," + Calls.CACHED_FORMATTED_NUMBER + " TEXT," + Voicemails._DATA + " TEXT," + Voicemails.HAS_CONTENT + " INTEGER," + Voicemails.MIME_TYPE + " TEXT," + Voicemails.SOURCE_DATA + " TEXT," + Voicemails.SOURCE_PACKAGE + " TEXT," + Voicemails.TRANSCRIPTION + " TEXT," + Voicemails.STATE + " INTEGER," + Voicemails.DIRTY + " INTEGER NOT NULL DEFAULT 0," + Voicemails.DELETED + " INTEGER NOT NULL DEFAULT 0" + ");"); // Voicemail source status table. db.execSQL("CREATE TABLE " + Tables.VOICEMAIL_STATUS + " (" + VoicemailContract.Status._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + VoicemailContract.Status.SOURCE_PACKAGE + " TEXT UNIQUE NOT NULL," + VoicemailContract.Status.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," + VoicemailContract.Status.PHONE_ACCOUNT_ID + " TEXT," + VoicemailContract.Status.SETTINGS_URI + " TEXT," + VoicemailContract.Status.VOICEMAIL_ACCESS_URI + " TEXT," + VoicemailContract.Status.CONFIGURATION_STATE + " INTEGER," + VoicemailContract.Status.DATA_CHANNEL_STATE + " INTEGER," + VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE + " INTEGER" + ");"); db.execSQL("CREATE TABLE " + Tables.STATUS_UPDATES + " (" + StatusUpdatesColumns.DATA_ID + " INTEGER PRIMARY KEY REFERENCES data(_id)," + StatusUpdates.STATUS + " TEXT," + StatusUpdates.STATUS_TIMESTAMP + " INTEGER," + StatusUpdates.STATUS_RES_PACKAGE + " TEXT, " + StatusUpdates.STATUS_LABEL + " INTEGER, " + StatusUpdates.STATUS_ICON + " INTEGER" + ");"); createDirectoriesTable(db); createSearchIndexTable(db, false /* we build stats table later */); db.execSQL("CREATE TABLE " + Tables.DATA_USAGE_STAT + "(" + DataUsageStatColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + DataUsageStatColumns.DATA_ID + " INTEGER NOT NULL, " + DataUsageStatColumns.USAGE_TYPE_INT + " INTEGER NOT NULL DEFAULT 0, " + DataUsageStatColumns.TIMES_USED + " INTEGER NOT NULL DEFAULT 0, " + DataUsageStatColumns.LAST_TIME_USED + " INTERGER NOT NULL DEFAULT 0, " + "FOREIGN KEY(" + DataUsageStatColumns.DATA_ID + ") REFERENCES " + Tables.DATA + "(" + Data._ID + ")" + ");"); db.execSQL("CREATE UNIQUE INDEX data_usage_stat_index ON " + Tables.DATA_USAGE_STAT + " (" + DataUsageStatColumns.DATA_ID + ", " + DataUsageStatColumns.USAGE_TYPE_INT + ");"); db.execSQL("CREATE TABLE " + Tables.PRE_AUTHORIZED_URIS + " ("+ PreAuthorizedUris._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + PreAuthorizedUris.URI + " STRING NOT NULL, " + PreAuthorizedUris.EXPIRATION + " INTEGER NOT NULL DEFAULT 0);"); // When adding new tables, be sure to also add size-estimates in updateSqliteStats createContactsViews(db); createGroupsView(db); createContactsTriggers(db); createContactsIndexes(db, false /* we build stats table later */); loadNicknameLookupTable(db); // Set sequence starts. initializeAutoIncrementSequences(db); // Add the legacy API support views, etc. LegacyApiSupport.createDatabase(db); if (mDatabaseOptimizationEnabled) { // This will create a sqlite_stat1 table that is used for query optimization db.execSQL("ANALYZE;"); updateSqliteStats(db); } ContentResolver.requestSync(null /* all accounts */, ContactsContract.AUTHORITY, new Bundle()); // Only send broadcasts for regular contacts db. if (dbForProfile() == 0) { final Intent dbCreatedIntent = new Intent( ContactsContract.Intents.CONTACTS_DATABASE_CREATED); dbCreatedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(dbCreatedIntent, android.Manifest.permission.READ_CONTACTS); } }
這裡以建立contacts表為例簡要介紹下
db.execSQL("CREATE TABLE " + Tables.CONTACTS + " (" +
BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
Contacts.NAME_RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," +
Contacts.PHOTO_ID + " INTEGER REFERENCES data(_id)," +
Contacts.PHOTO_FILE_ID + " INTEGER REFERENCES photo_files(_id)," +
Contacts.CUSTOM_RINGTONE + " TEXT," +
Contacts.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0," +
Contacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," +
Contacts.LAST_TIME_CONTACTED + " INTEGER," +
Contacts.STARRED + " INTEGER NOT NULL DEFAULT 0," +
Contacts.PINNED + " INTEGER NOT NULL DEFAULT " + PinnedPositions.UNPINNED + "," +
Contacts.HAS_PHONE_NUMBER + " INTEGER NOT NULL DEFAULT 0," +
Contacts.LOOKUP_KEY + " TEXT," +
ContactsColumns.LAST_STATUS_UPDATE_ID + " INTEGER REFERENCES data(_id)," +
Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " INTEGER" +
");");
建立表的語句和sql語句差不多,建立表的主鍵,自動增加
BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
外來鍵約束,contacts表中欄位NAME_RAW_CONTACT_ID為raw_contacts表的主鍵,另外外來鍵約束的類似
Contacts.NAME_RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," +
Contacts.PHOTO_ID + " INTEGER REFERENCES data(_id)," +
Contacts.PHOTO_FILE_ID + " INTEGER REFERENCES photo_files(_id)," +
在這個類中除了建立了資料庫表,還為有些表建立了觸發器,檢視,索引,下面每個都以一個簡單的例子說下,有興趣的話可以自己去檢視原始碼
為data表建立觸發器,如果raw_contact表中某條資料被刪除,自動刪除data表中對應的資料
db.execSQL("DROP TRIGGER IF EXISTS " + Tables.RAW_CONTACTS + "_deleted;");
db.execSQL("CREATE TRIGGER " + Tables.RAW_CONTACTS + "_deleted "
+ " BEFORE DELETE ON " + Tables.RAW_CONTACTS
+ " BEGIN "
+ " DELETE FROM " + Tables.DATA
+ " WHERE " + Data.RAW_CONTACT_ID
+ "=OLD." + RawContacts._ID + ";"
+ " DELETE FROM " + Tables.AGGREGATION_EXCEPTIONS
+ " WHERE " + AggregationExceptions.RAW_CONTACT_ID1
+ "=OLD." + RawContacts._ID
+ " OR " + AggregationExceptions.RAW_CONTACT_ID2
+ "=OLD." + RawContacts._ID + ";"
+ " DELETE FROM " + Tables.VISIBLE_CONTACTS
+ " WHERE " + Contacts._ID + "=OLD." + RawContacts.CONTACT_ID
+ " AND (SELECT COUNT(*) FROM " + Tables.RAW_CONTACTS
+ " WHERE " + RawContacts.CONTACT_ID + "=OLD." + RawContacts.CONTACT_ID
+ " )=1;"
+ " DELETE FROM " + Tables.DEFAULT_DIRECTORY
+ " WHERE " + Contacts._ID + "=OLD." + RawContacts.CONTACT_ID
+ " AND (SELECT COUNT(*) FROM " + Tables.RAW_CONTACTS
+ " WHERE " + RawContacts.CONTACT_ID + "=OLD." + RawContacts.CONTACT_ID
+ " )=1;"
+ " DELETE FROM " + Tables.CONTACTS
+ " WHERE " + Contacts._ID + "=OLD." + RawContacts.CONTACT_ID
+ " AND (SELECT COUNT(*) FROM " + Tables.RAW_CONTACTS
+ " WHERE " + RawContacts.CONTACT_ID + "=OLD." + RawContacts.CONTACT_ID
+ " )=1;"
+ " END");
為raw_contacts建立視圖表,檢視總是顯示最近的資料。每當使用者查詢檢視時,資料庫引擎通過使用 SQL 語句來重建資料。
String rawContactsSelect = "SELECT "
+ RawContactsColumns.CONCRETE_ID + " AS " + RawContacts._ID + ","
+ RawContacts.CONTACT_ID + ", "
+ RawContacts.AGGREGATION_MODE + ", "
+ RawContacts.RAW_CONTACT_IS_READ_ONLY + ", "
+ RawContacts.DELETED + ", "
+ RawContacts.DISPLAY_NAME_SOURCE + ", "
+ RawContacts.DISPLAY_NAME_PRIMARY + ", "
+ RawContacts.DISPLAY_NAME_ALTERNATIVE + ", "
+ RawContacts.PHONETIC_NAME + ", "
+ RawContacts.PHONETIC_NAME_STYLE + ", "
+ RawContacts.SORT_KEY_PRIMARY + ", "
+ RawContactsColumns.PHONEBOOK_LABEL_PRIMARY + ", "
+ RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY + ", "
+ RawContacts.SORT_KEY_ALTERNATIVE + ", "
+ RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE + ", "
+ RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE + ", "
+ dbForProfile() + " AS " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + ", "
+ rawContactOptionColumns + ", "
+ syncColumns
+ " FROM " + Tables.RAW_CONTACTS
+ " JOIN " + Tables.ACCOUNTS + " ON ("
+ RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID
+ ")";
db.execSQL("CREATE VIEW " + Views.RAW_CONTACTS + " AS " + rawContactsSelect);
表中建立索引,以便更加快速高效地查詢資料,使用者無法看到索引,它們只能被用來加速搜尋/查詢,更新一個包含索引的表需要比更新一個沒有索引的表更多的時間,這是由於索引本身也需要更新。因此,理想的做法是僅僅在常常被搜尋的列(以及表)上面建立索引。
在NAME_LOOKUP表中的raw_contact_id列上建立名為name_lookup_raw_contact_id_index的索引
db.execSQL("CREATE INDEX name_lookup_raw_contact_id_index ON " + Tables.NAME_LOOKUP + " (" +
NameLookupColumns.RAW_CONTACT_ID +
");");
在聯絡人中使用資料庫的時候,一般都是通過ContentProvider來操作contact2.db裡的資料,在ContactsProvider2中定義了操作不同表的uri,在contentProvider中根據不同的uri來操作不同的表
static {
// Contacts URI matching table
final UriMatcher matcher = sUriMatcher;
// DO NOT use constants such as Contacts.CONTENT_URI here. This is the only place
// where one can see all supported URLs at a glance, and using constants will reduce
// readability.
matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_ID_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/entities", CONTACTS_ID_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions",
AGGREGATION_SUGGESTIONS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*",
AGGREGATION_SUGGESTIONS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_ID_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/display_photo",
CONTACTS_ID_DISPLAY_PHOTO);
// Special URIs that refer to contact pictures in the corp CP2.
matcher.addURI(ContactsContract.AUTHORITY, "contacts_corp/#/photo", CONTACTS_ID_PHOTO_CORP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts_corp/#/display_photo",
CONTACTS_ID_DISPLAY_PHOTO_CORP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/stream_items",
CONTACTS_ID_STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter", CONTACTS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/data", CONTACTS_LOOKUP_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/photo",
CONTACTS_LOOKUP_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/data",
CONTACTS_LOOKUP_ID_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/photo",
CONTACTS_LOOKUP_ID_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/display_photo",
CONTACTS_LOOKUP_DISPLAY_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/display_photo",
CONTACTS_LOOKUP_ID_DISPLAY_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/entities",
CONTACTS_LOOKUP_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/entities",
CONTACTS_LOOKUP_ID_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/stream_items",
CONTACTS_LOOKUP_STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#/stream_items",
CONTACTS_LOOKUP_ID_STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*",
CONTACTS_AS_MULTI_VCARD);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*",
CONTACTS_STREQUENT_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/frequent", CONTACTS_FREQUENT);
matcher.addURI(ContactsContract.AUTHORITY, "contacts/delete_usage", CONTACTS_DELETE_USAGE);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_ID_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/display_photo",
RAW_CONTACTS_ID_DISPLAY_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ID_ENTITY);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items",
RAW_CONTACTS_ID_STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/stream_items/#",
RAW_CONTACTS_ID_STREAM_ITEMS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities_corp", RAW_CONTACT_ENTITIES_CORP);
matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
matcher.addURI(ContactsContract.AUTHORITY, "data_enterprise/phones", PHONES_ENTERPRISE);
matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup", EMAILS_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup_enterprise",
EMAILS_LOOKUP_ENTERPRISE);
matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup_enterprise/*",
EMAILS_LOOKUP_ENTERPRISE);
matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID);
/** "*" is in CSV form with data IDs ("123,456,789") */
matcher.addURI(ContactsContract.AUTHORITY, "data/usagefeedback/*", DATA_USAGE_FEEDBACK_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/callables/", CALLABLES);
matcher.addURI(ContactsContract.AUTHORITY, "data/callables/#", CALLABLES_ID);
matcher.addURI(ContactsContract.AUTHORITY, "data/callables/filter", CALLABLES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/callables/filter/*", CALLABLES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/contactables/", CONTACTABLES);
matcher.addURI(ContactsContract.AUTHORITY, "data/contactables/filter", CONTACTABLES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "data/contactables/filter/*",
CONTACTABLES_FILTER);
matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#",
SYNCSTATE_ID);
matcher.addURI(ContactsContract.AUTHORITY, "profile/" + SyncStateContentProviderHelper.PATH,
PROFILE_SYNCSTATE);
matcher.addURI(ContactsContract.AUTHORITY,
"profile/" + SyncStateContentProviderHelper.PATH + "/#",
PROFILE_SYNCSTATE_ID);
matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup_enterprise/*",
PHONE_LOOKUP_ENTERPRISE);
matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
AGGREGATION_EXCEPTIONS);
matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
AGGREGATION_EXCEPTION_ID);
matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS);
matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES);
matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID);
matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
SEARCH_SUGGESTIONS);
matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
SEARCH_SUGGESTIONS);
matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
SEARCH_SHORTCUT);
matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS);
matcher.addURI(ContactsContract.AUTHORITY, "directories", DIRECTORIES);
matcher.addURI(ContactsContract.AUTHORITY, "directories/#", DIRECTORIES_ID);
matcher.addURI(ContactsContract.AUTHORITY, "complete_name", COMPLETE_NAME);
matcher.addURI(ContactsContract.AUTHORITY, "profile", PROFILE);
matcher.addURI(ContactsContract.AUTHORITY, "profile/entities", PROFILE_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY, "profile/data", PROFILE_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "profile/data/#", PROFILE_DATA_ID);
matcher.addURI(ContactsContract.AUTHORITY, "profile/photo", PROFILE_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY, "profile/display_photo", PROFILE_DISPLAY_PHOTO);
matcher.addURI(ContactsContract.AUTHORITY, "profile/as_vcard", PROFILE_AS_VCARD);
matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts", PROFILE_RAW_CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#",
PROFILE_RAW_CONTACTS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/data",
PROFILE_RAW_CONTACTS_ID_DATA);
matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contacts/#/entity",
PROFILE_RAW_CONTACTS_ID_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY, "profile/status_updates",
PROFILE_STATUS_UPDATES);
matcher.addURI(ContactsContract.AUTHORITY, "profile/raw_contact_entities",
PROFILE_RAW_CONTACT_ENTITIES);
matcher.addURI(ContactsContract.AUTHORITY, "stream_items", STREAM_ITEMS);
matcher.addURI(ContactsContract.AUTHORITY, "stream_items/photo", STREAM_ITEMS_PHOTOS);
matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#", STREAM_ITEMS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo", STREAM_ITEMS_ID_PHOTOS);
matcher.addURI(ContactsContract.AUTHORITY, "stream_items/#/photo/#",
STREAM_ITEMS_ID_PHOTOS_ID);
matcher.addURI(ContactsContract.AUTHORITY, "stream_items_limit", STREAM_ITEMS_LIMIT);
matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", DISPLAY_PHOTO_ID);
matcher.addURI(ContactsContract.AUTHORITY, "photo_dimensions", PHOTO_DIMENSIONS);
matcher.addURI(ContactsContract.AUTHORITY, "deleted_contacts", DELETED_CONTACTS);
matcher.addURI(ContactsContract.AUTHORITY, "deleted_contacts/#", DELETED_CONTACTS_ID);
}
在Contacts中,有時候看到查詢的uri明明是對應raw_contacts表,但真在在查詢資料庫的時候卻是查詢view_raw_contacts裡的資料
Cursor cursor = mContentResolver.query(
RawContacts.CONTENT_URI, PROJECTION_FILTERED_MEMBERS,
accountClause + " AND (" +
RawContacts.DISPLAY_NAME_PRIMARY + " LIKE ? OR " +
RawContacts.DISPLAY_NAME_ALTERNATIVE + " LIKE ? )",
args, RawContacts.DISPLAY_NAME_PRIMARY + " COLLATE LOCALIZED ASC");
RawContacts.CONTENT_URI定義如下:
/**
* The content:// style URI for this table, which requests a directory of
* raw contact rows matching the selection criteria.
*/
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts");
下面來看看為啥在查詢上面的uri時,會查詢到view_raw_contacts裡,ContentProvider在提供資料共享時,需要重寫增刪改查這四個方法,主要說下query這個方法的重寫
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
return query(uri, projection, selection, selectionArgs, sortOrder, null);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder, CancellationSignal cancellationSignal) {
if (VERBOSE_LOGGING) {
Log.v(TAG, "query: uri=" + uri + " projection=" + Arrays.toString(projection) +
" selection=[" + selection + "] args=" + Arrays.toString(selectionArgs) +
" order=[" + sortOrder + "] CPID=" + Binder.getCallingPid() +
" User=" + UserUtils.getCurrentUserHandle(getContext()));
}
waitForAccess(mReadAccessLatch);
// Query the profile DB if appropriate.
if (mapsToProfileDb(uri)) {
switchToProfileMode();
return mProfileProvider.query(uri, projection, selection, selectionArgs, sortOrder,
cancellationSignal);
}
// Otherwise proceed with a normal query against the contacts DB.
switchToContactMode();
String directory = getQueryParameter(uri, ContactsContract.DIRECTORY_PARAM_KEY);
final long directoryId =
(directory == null ? -1 :
(directory.equals("0") ? Directory.DEFAULT :
(directory.equals("1") ? Directory.LOCAL_INVISIBLE : Long.MIN_VALUE)));
if (directoryId > Long.MIN_VALUE) {
final Cursor cursor = queryLocal(uri, projection, selection, selectionArgs, sortOrder,
directoryId, cancellationSignal);
return addSnippetExtrasToCursor(uri, cursor);
}
DirectoryInfo directoryInfo = getDirectoryAuthority(directory);
if (directoryInfo == null) {
Log.e(TAG, "Invalid directory ID: " + uri);
return null;
}
Builder builder = new Uri.Builder();
builder.scheme(ContentResolver.SCHEME_CONTENT);
builder.authority(directoryInfo.authority);
builder.encodedPath(uri.getEncodedPath());
if (directoryInfo.accountName != null) {
builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, directoryInfo.accountName);
}
if (directoryInfo.accountType != null) {
builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, directoryInfo.accountType);
}
String limit = getLimit(uri);
if (limit != null) {
builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, limit);
}
Uri directoryUri = builder.build();
if (projection == null) {
projection = getDefaultProjection(uri);
}
Cursor cursor;
try {
cursor = getContext().getContentResolver().query(
directoryUri, projection, selection, selectionArgs, sortOrder);
if (cursor == null) {
return null;
}
} catch (RuntimeException e) {
Log.w(TAG, "Directory query failed: uri=" + uri, e);
return null;
}
// Load the cursor contents into a memory cursor (backed by a cursor window) and close the
// underlying cursor.
try {
MemoryCursor memCursor = new MemoryCursor(null, cursor.getColumnNames());
memCursor.fillFromCursor(cursor);
return memCursor;
} finally {
cursor.close();
}
}
在重寫query這個方法裡,主要是用到了queryLocal這個方法,根據不同的uri去查詢對應的表
protected Cursor queryLocal(final Uri uri, final String[] projection, String selection,
String[] selectionArgs, String sortOrder, final long directoryId,
final CancellationSignal cancellationSignal) {
final SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String groupBy = null;
String having = null;
String limit = getLimit(uri);
boolean snippetDeferred = false;
// The expression used in bundleLetterCountExtras() to get count.
String addressBookIndexerCountExpression = null;
final int match = sUriMatcher.match(uri);
switch (match) {
case SYNCSTATE:
case PROFILE_SYNCSTATE:
return mDbHelper.get().getSyncState().query(db, projection, selection,
selectionArgs, sortOrder);
......
case RAW_CONTACTS:
case PROFILE_RAW_CONTACTS: {
setTablesAndProjectionMapForRawContacts(qb, uri);
break;
}
.......
}
在setTablesAndProjectionMapForRawContacts 設定要查詢的表和欄位
private void setTablesAndProjectionMapForRawContacts(SQLiteQueryBuilder qb, Uri uri) {
StringBuilder sb = new StringBuilder();
sb.append(Views.RAW_CONTACTS);
qb.setTables(sb.toString()); //設定查詢的表為raw_contacts的視圖表
qb.setProjectionMap(sRawContactsProjectionMap);
appendAccountIdFromParameter(qb, uri);
}
如果想進一步瞭解ContactsProvider,可以去看看ContactsProvider的原始碼,這裡提供一個下載google原始碼映象的連結