在android系統移植時,經常要儲存系統某一變量的值,最簡單的方法就是儲存到系統資料庫中,而不是儲存在apk的xml中,隻要一句話:
Settings.System.putString(ContentResolver resolver, String name, String value)
讀也非常簡單:
Settings.System.getString(ContentResolver resolver, String name)
隻要帶一個Context,就可以讀寫這個資料庫,讓我感到困惑的是:對資料庫進行操作,插入和修改是兩種不同指令,android是怎麼知道我到底是進行插入還是修改操作呢?于是跟蹤到源碼裡看個清楚:
首先,資料庫儲存在哪? 源碼路徑: \frameworks\base\packages\SettingsProvider\src\com\android\providers\settings 在 DatabaseHelper類裡,建立資料庫和表 public class DatabaseHelper extends SQLiteOpenHelper {
...... private void createSecureTable(SQLiteDatabase db) { db.execSQL( "CREATE TABLE secure (" + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT UNIQUE ON CONFLICT REPLACE," + "value TEXT" + ");" ); db.execSQL( "CREATE INDEX secureIndex1 ON secure (name);" ); }
private void createGlobalTable(SQLiteDatabase db) { db.execSQL( "CREATE TABLE global (" + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT UNIQUE ON CONFLICT REPLACE," + "value TEXT" + ");" ); db.execSQL( "CREATE INDEX globalIndex1 ON global (name);" ); } @ Override public void onCreate(SQLiteDatabase db) { db.execSQL( "CREATE TABLE system (" + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT UNIQUE ON CONFLICT REPLACE," + "value TEXT" + ");" ); db.execSQL( "CREATE INDEX systemIndex1 ON system (name);" );
createSecureTable(db);
// Only create the global table for the singleton 'owner' user if (mUserHandle == UserHandle.USER_OWNER) { createGlobalTable(db); }
db.execSQL( "CREATE TABLE bluetooth_devices (" + "_id INTEGER PRIMARY KEY," + "name TEXT," + "addr TEXT," + "channel INTEGER," + "type INTEGER" + ");" );
db.execSQL( "CREATE TABLE bookmarks (" + "_id INTEGER PRIMARY KEY," + "title TEXT," + "folder TEXT," + "intent TEXT," + "shortcut INTEGER," + "ordering INTEGER" + ");" );
db.execSQL( "CREATE INDEX bookmarksIndex1 ON bookmarks (folder);" ); db.execSQL( "CREATE INDEX bookmarksIndex2 ON bookmarks (shortcut);" );
// Populate bookmarks table with initial bookmarks boolean onlyCore = false ; try { onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService( "package" )).isOnlyCoreApps(); } catch (RemoteException e) { } if (!onlyCore) { loadBookmarks(db); }
// Load initial volume levels into DB loadVolumeLevels(db);
// Load inital settings values loadSettings(db); }
這裡建立了幾張表,這個資料庫生成的db檔案,儲存成(在android系統):
/data/data/com.android.providers.settings/databases/settings.db
Settings.System.putString()這個方法是把資料寫入到了system表中
至于是插入還是修改,關鍵在于建立表時執行的SQL語句:
"CREATE TABLE secure (" + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + "name TEXT UNIQUE ON CONFLICT REPLACE," + "value TEXT" + ");"
system表中,總共三個字段:_id name value
再看下name字段: name TEXT UNIQUE ON CONFLICT REPLACE
ON CONFLICT子句可以定義替代的限制沖突判定算法。預設為ABORT。同一個表中的不同限制可以使用不同的預設沖突判定算法。若一條COPY、INSERT或UPDATE指令指定了不同的沖突判定算法,則該算法将替代CREATE TABLE語句中說明的預設算法
ON CONFLICT子句不是獨立的SQL指令。這是一條可以出現在許多其他SQL指令中的非标準的子句。由于它并不是标準的SQL語言,這裡單獨介紹它。
ON CONFLICT子句的文法在如上的CREATE TABLE指令中示出。對于INSERT和UPDATE,關鍵詞“ON CONFLICT”由“OR”替代,這樣文法顯得自然。例如,不用寫“INSERT ON CONFLICT IGNORE”而是“INSERT OR IGNORE”。二者表示相同的意思。
ON CONFLICT子句定義了解決限制沖突的算法。有五個選擇:ROLLBACK、ABORT、FAIL、IGNORE和REPLACE,預設方案是ABORT。選項含義如下:
ROLLBACK 當發生限制沖突,立即ROLLBACK,即結束目前事務處理,指令中止并傳回SQLITE_CONSTRAINT代碼。若目前無活動事務(除了每一條指令建立的預設事務以外),則該算法與ABORT相同。
ABORT 當發生限制沖突,指令收回已經引起的改變并中止傳回SQLITE_CONSTRAINT。但由于不執行ROLLBACK,是以前面的指令産生的改變将予以保留。預設采用這一行為。
FAIL 當發生限制沖突,指令中止傳回SQLITE_CONSTRAINT。但遇到沖突之前的所有改變将被保留。例如,若一條UPDATE語句在100行遇到沖突100th,前99行的改變将被保留,而對100行或以後的改變将不會發生。
IGNORE 當發生限制沖突,發生沖突的行将不會被插入或改變。但指令将照常執行。在沖突行之前或之後的行将被正常的插入和改變,且不傳回錯誤資訊。
REPLACE 當發生UNIQUE限制沖突,先存在的,導緻沖突的行在更改或插入發生沖突的行之前被删除。這樣,更改和插入總是被執行。指令照常執行且不傳回錯誤資訊。當發生NOT NULL限制沖突,導緻沖突的NULL值會被字段預設值取代。若字段無預設值,執行ABORT算法。
當沖突應對政策為滿足限制而删除行時,它不會調用删除觸發器。但在新版中這一特性可能被改變。
INSERT或UPDATE的OR子句定義的算法會覆寫CREATE TABLE所定義的。ABORT算法将在沒有定義任何算法時預設使用。-->這就是關鍵
以上是資料庫部分 Settings.System源碼路徑:
frameworks\base\core\java\android\provider
protected static boolean putString(ContentResolver resolver, Uri uri, String name, String value) { // The database will take care of replacing duplicates. try { ContentValues values = new ContentValues(); values.put( NAME , name); values.put( VALUE , value); resolver.insert(uri, values); return true ; } catch (SQLException e) { Log. w( TAG , "Can't set key " + name + " in " + uri, e); return false ; } }