前一篇文章[ANR]為什么SharedPreference會引起ANR,我們知道, SharedPreference的apply方法雖然是異步寫入文件的,但是會在Activity和Service生命周期方法調用的時候,等待所有的寫入完成,可能引起卡頓和ANR。
解決方法
為了快速解決這個問題,我們可以通過代理系統(tǒng)SharedPreference的所有apply方法,改為直接在子線程調用commit,即可。下面看代碼。ps:以下代碼可以直接復制使用。
class NoMainThreadWriteSharedPreferences private constructor(private val sysPrefs: SharedPreferences, val name: String) :
SharedPreferences {
private val preferencesCache: MutableMap<String, Any?> = HashMap()
companion object {
private val executor: ExecutorService = Executors.newSingleThreadExecutor()
private val INSTANCES: MutableMap<String, NoMainThreadWriteSharedPreferences> = HashMap()
@JvmStatic
fun getInstance(sharedPreferences: SharedPreferences, name: String): SharedPreferences {
return INSTANCES.getOrPut(name, { NoMainThreadWriteSharedPreferences(sharedPreferences, name) })
}
@VisibleForTesting
@JvmStatic
fun reset() {
INSTANCES.clear()
}
}
init {
// 初始化的時候,緩存所有的鍵值對,也可以等到調用getBoolean等獲取key方法的時候,再做
preferencesCache.putAll(sysPrefs.all)
}
override fun contains(key: String?) = preferencesCache[key] != null
override fun getAll() = HashMap(preferencesCache)
override fun getBoolean(key: String, defValue: Boolean): Boolean {
return preferencesCache[key] as Boolean? ?: defValue
}
override fun getInt(key: String, defValue: Int): Int {
return preferencesCache[key] as Int? ?: defValue
}
override fun getLong(key: String, defValue: Long): Long {
return preferencesCache[key] as Long? ?: defValue
}
override fun getFloat(key: String, defValue: Float): Float {
return preferencesCache[key] as Float? ?: defValue
}
override fun getStringSet(key: String, defValues: MutableSet<String>?): MutableSet<String>? {
@Suppress("UNCHECKED_CAST")
return preferencesCache[key] as MutableSet<String>? ?: defValues
}
override fun getString(key: String, defValue: String?): String? {
return preferencesCache[key] as String? ?: defValue
}
override fun edit(): SharedPreferences.Editor {
return Editor(sysPrefs.edit())
}
override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
sysPrefs.registerOnSharedPreferenceChangeListener(listener)
}
override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
sysPrefs.unregisterOnSharedPreferenceChangeListener(listener)
}
inner class Editor(private val sysEdit: SharedPreferences.Editor) : SharedPreferences.Editor {
private val modifiedData: MutableMap<String, Any?> = HashMap()
private var keysToRemove: MutableSet<String> = HashSet()
private var clear = false
override fun commit(): Boolean {
submit()
return true
}
// apply和commit都調用submit方法
override fun apply() {
submit()
}
private fun submit() {
synchronized(preferencesCache) {
// 先更新到本地的內存緩存
storeMemCache()
// 再用異步線程,調用系統(tǒng)commit方法去更新
queuePersistentStore()
}
}
private fun storeMemCache() {
if (clear) {
preferencesCache.clear()
clear = false
} else {
preferencesCache.keys.removeAll(keysToRemove)
}
keysToRemove.clear()
preferencesCache.putAll(modifiedData)
modifiedData.clear()
}
private fun queuePersistentStore() {
try {
executor.submit {
sysEdit.commit()
}
} catch (ex: Exception) {
}
}
override fun remove(key: String): SharedPreferences.Editor {
keysToRemove.add(key)
modifiedData.remove(key)
sysEdit.remove(key)
return this
}
override fun clear(): SharedPreferences.Editor {
clear = true
sysEdit.clear()
return this
}
override fun putLong(key: String, value: Long): SharedPreferences.Editor {
modifiedData[key] = value
sysEdit.putLong(key, value)
return this
}
override fun putInt(key: String, value: Int): SharedPreferences.Editor {
modifiedData[key] = value
sysEdit.putInt(key, value)
return this
}
override fun putBoolean(key: String, value: Boolean): SharedPreferences.Editor {
modifiedData[key] = value
sysEdit.putBoolean(key, value)
return this
}
override fun putStringSet(key: String, values: MutableSet<String>?): SharedPreferences.Editor {
modifiedData[key] = values
sysEdit.putStringSet(key, values)
return this
}
override fun putFloat(key: String, value: Float): SharedPreferences.Editor {
modifiedData[key] = value
sysEdit.putFloat(key, value)
return this
}
override fun putString(key: String, value: String?): SharedPreferences.Editor {
modifiedData[key] = value
sysEdit.putString(key, value)
return this
}
}
}
使用的時候,通過在自定義的Application和BaseActivity中,修改getSharedPreferences方法,將我們的類作為系統(tǒng)SharedPreference的包裝類返回。
public SharedPreferences getSharedPreferences(String name, int mode) {
return NoMainThreadWriteSharedPreferences.getInstance(super.getSharedPreferences(name, mode), name);
}
總結
優(yōu)點:
- 可以快速修復
Activity和Service生命周期onStart和onPause方法,等待SharedPreference寫入文件的ANR問題
缺點:
-
onSharedPreferenceChangeListener的回調會變慢,本來只需要等到寫入內存緩存即可回調,現(xiàn)在需要等待真正寫入文件才能回調
待解決問題:
- 如果用戶調用
commit和apply多次,還是會寫入多次,不會合并請求,且SharedPreference的寫入都是完整文件的寫入 - 沒有解決調用
getBoolean等方法獲取value時,等待SharedPreference讀到緩存,引起的ANR問題 - 沒有解決
getPreferencesDir引起的ANR問題