在Android M及更高版本中使用 Settings.System 丟擲異常"You cannot keep your settings in the secure settings. "
最近專案需要從Android L遷移到Android M。在升級的過程中,遇到了如下的問題。
在專案中,有一些公共的資料是存放在存放在 系統資料庫SettingsProvider的System表中
在Android L中的使用方法是:
當需要寫資料時呼叫:
Settings.System.putStringForUser(ContentResolver cr, String name, int value, int userHandle)
當需要獲取資料時呼叫:
Settings.System.getStringForUser(ContentResolver cr, String name, final int userHandle)
將key-value存入系統公共的資料庫SettingsProvider的System的表中,在簡單的跨程序資料的儲存和共享的場景下十分簡單高效。
但是當專案執行在Android M版本以後當繼續使用該資料庫時丟擲瞭如下異常。
IllegalArgumentException: You cannot keep your settings in the secure settings.
從字面上理解,不能夠使用settings。
既然在L版本中可用,在M版本中不可用,那麼肯定是因為在M版本中引入了許可權檢查,並且丟擲異常。
從framework原始碼入手:
呼叫Settings.System.putStringForUser最終都是對資料庫SettingsProvider進行操作。
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.Java
在梳理原始碼以後發現,在M版本中,在Settings.System表中執行更新或者刪除操作時,都要會呼叫函式warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk。該函式是M新增,而問題正在於該函式
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(int targetSdkVersion, String name) { // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash. if (targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) { if (Settings.System.PRIVATE_SETTINGS.contains(name)) { Slog.w(LOG_TAG, "You shouldn't not change private system settings." + " This will soon become an error."); } else { Slog.w(LOG_TAG, "You shouldn't keep your settings in the secure settings." + " This will soon become an error."); } } else { if (Settings.System.PRIVATE_SETTINGS.contains(name)) { throw new IllegalArgumentException("You cannot change private secure settings."); } else { throw new IllegalArgumentException("You cannot keep your settings in" + " the secure settings."); } } }
說明當app嘗試更新Settings.System的時候,會進行版本校驗,如果targetSDK 小於等於Build.VERSION_CODES.LOLLIPOP_MR1,即低版本的apk時,給出warning,保證程式碼的相容性。
而當tagetSDK大於Build.VERSION_CODES.LOLLIPOP_MR1,即M版本及更高的版本的時候,則會丟擲異常,禁止apk寫或者刪除SettingsProvider資料庫中的System表。
分析到這裡,針對這樣的問題,由如下兩個解決方案。
1. 如果對targetSDK沒有要求,則將targetSDK降為Build.VERSION_CODES.LOLLIPOP_MR1以下,利用Android程式向前相容性,規避問題,但是這樣的方法並不是最優方案,有可能在後續版本中,Android甚至可能放棄相容,直接丟擲異常。
2. 放棄寫Settings.System,改用Settings.Global儲存共享資料。