diff --git a/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.md b/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.md index 00a82e39..ab635f4c 100644 --- a/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.md +++ b/docs-source/src/en/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.md @@ -404,6 +404,8 @@ fun edit(): Editor > 创建新的 `Editor`。 +在模块环境中或启用了 `isUsingNativeStorage` 后使用。 + ::: warning 在 (Xposed) 宿主环境下只读,无法使用。 @@ -426,6 +428,8 @@ fun edit(initiate: Editor.() -> Unit) 自动调用 `Editor.apply` 方法。 +在模块环境中或启用了 `isUsingNativeStorage` 后使用。 + ::: warning 在 (Xposed) 宿主环境下只读,无法使用。 @@ -444,16 +448,12 @@ fun clearCache() **Function Illustrate** -> 清除 `XSharedPreferences` 中缓存的键值数据。 +> 清除 `YukiHookPrefsBridge` 中缓存的键值数据。 无论是否开启 `YukiHookAPI.Configs.isEnablePrefsBridgeCache`。 调用此方法将清除当前存储的全部键值缓存。 -下次将从 `XSharedPreferences` 重新读取。 - -在 (Xposed) 宿主环境中使用。 - ## Editor - class ```kotlin:no-line-numbers @@ -470,6 +470,8 @@ inner class Editor internal constructor() 请使用 `edit` 方法来获取 `Editor`。 +在模块环境中或启用了 `isUsingNativeStorage` 后使用。 + ::: warning 在 (Xposed) 宿主环境下只读,无法使用。 diff --git a/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.md b/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.md index 511a8782..4a5c204d 100644 --- a/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.md +++ b/docs-source/src/zh-cn/api/public/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.md @@ -396,6 +396,8 @@ fun edit(): Editor > 创建新的 `Editor`。 +在模块环境中或启用了 `isUsingNativeStorage` 后使用。 + ::: warning 在 (Xposed) 宿主环境下只读,无法使用。 @@ -418,6 +420,8 @@ fun edit(initiate: Editor.() -> Unit) 自动调用 `Editor.apply` 方法。 +在模块环境中或启用了 `isUsingNativeStorage` 后使用。 + ::: warning 在 (Xposed) 宿主环境下只读,无法使用。 @@ -436,16 +440,12 @@ fun clearCache() **功能描述** -> 清除 `XSharedPreferences` 中缓存的键值数据。 +> 清除 `YukiHookPrefsBridge` 中缓存的键值数据。 无论是否开启 `YukiHookAPI.Configs.isEnablePrefsBridgeCache`。 调用此方法将清除当前存储的全部键值缓存。 -下次将从 `XSharedPreferences` 重新读取。 - -在 (Xposed) 宿主环境中使用。 - ## Editor - class ```kotlin:no-line-numbers @@ -462,6 +462,8 @@ inner class Editor internal constructor() 请使用 `edit` 方法来获取 `Editor`。 +在模块环境中或启用了 `isUsingNativeStorage` 后使用。 + ::: warning 在 (Xposed) 宿主环境下只读,无法使用。 diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.kt index 13483702..d9c35785 100644 --- a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.kt +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookPrefsBridge.kt @@ -39,6 +39,7 @@ import com.highcapable.yukihookapi.hook.log.yLoggerW import com.highcapable.yukihookapi.hook.xposed.bridge.YukiXposedModule import com.highcapable.yukihookapi.hook.xposed.bridge.delegate.XSharedPreferencesDelegate import com.highcapable.yukihookapi.hook.xposed.parasitic.AppParasitics +import com.highcapable.yukihookapi.hook.xposed.prefs.cache.PreferencesCacheManager import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData import com.highcapable.yukihookapi.hook.xposed.prefs.ui.ModulePreferenceFragment import de.robv.android.xposed.XSharedPreferences @@ -108,49 +109,12 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu else "${YukiXposedModule.modulePackageName.ifBlank { context?.packageName ?: "unknown" }}_preferences" } - /** 是否使用键值缓存 */ - private var isUsingKeyValueCache = YukiHookAPI.Configs.isEnablePrefsBridgeCache - /** 是否使用新版存储方式 EdXposed、LSPosed */ private var isUsingNewXSharedPreferences = false /** 是否启用原生存储方式 */ private var isUsingNativeStorage = false - /** - * [XSharedPreferences] 缓存的键值数据 - */ - private object XSharedPreferencesCaches { - - /** 缓存的 [String] 键值数据 */ - var stringData = ArrayMap() - - /** 缓存的 [Set]<[String]> 键值数据 */ - var stringSetData = ArrayMap>() - - /** 缓存的 [Boolean] 键值数据 */ - var booleanData = ArrayMap() - - /** 缓存的 [Int] 键值数据 */ - var intData = ArrayMap() - - /** 缓存的 [Long] 键值数据 */ - var longData = ArrayMap() - - /** 缓存的 [Float] 键值数据 */ - var floatData = ArrayMap() - - /** 清除所有缓存的键值数据 */ - fun clear() { - stringData.clear() - stringSetData.clear() - booleanData.clear() - intData.clear() - longData.clear() - floatData.clear() - } - } - /** 检查 API 装载状态 */ private fun checkApi() { if (YukiHookAPI.isLoadedFromBaseContext) error("YukiHookPrefsBridge not allowed in Custom Hook API") @@ -202,6 +166,12 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu if (isUsingNewXSharedPreferences.not()) makeWorldReadable(context, prefsFileName = "${currentPrefsName}.xml") } + /** + * 获取当前 [PreferencesCacheManager] 对象 + * @return [PreferencesCacheManager] + */ + private val cacheManager get() = PreferencesCacheManager.instance() + /** * 获取 [XSharedPreferences] 是否可读 * @@ -247,7 +217,7 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @return [YukiHookPrefsBridge] */ fun name(name: String): YukiHookPrefsBridge { - isUsingKeyValueCache = YukiHookAPI.Configs.isEnablePrefsBridgeCache + cacheManager.enableByConfig() prefsName = name return this } @@ -261,7 +231,7 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @return [YukiHookPrefsBridge] */ fun direct(): YukiHookPrefsBridge { - isUsingKeyValueCache = false + cacheManager.disable() return this } @@ -288,19 +258,11 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @return [String] */ fun getString(key: String, value: String = "") = - (if (isXposedEnvironment && isUsingNativeStorage.not()) - if (isUsingKeyValueCache) - XSharedPreferencesCaches.stringData[key].let { - (it ?: currentXsp.getString(key, value) ?: value).let { value -> - XSharedPreferencesCaches.stringData[key] = value - value - } - } - else resetCacheSet { currentXsp.getString(key, value) ?: value } - else currentSp.getString(key, value) ?: value).let { - makeWorldReadable() - it - } + cacheManager.getString(key) { + if (isXposedEnvironment && isUsingNativeStorage.not()) + currentXsp.getString(key, value) ?: value + else currentSp.getString(key, value) ?: value + }.also { makeWorldReadable() } /** * 获取 [Set]<[String]> 键值 @@ -309,23 +271,15 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * * - 建议使用 [PrefsData] 创建模板并使用 [get] 获取数据 * @param key 键值名称 - * @param value 默认数据 + * @param value 默认数据 - [HashSet]<[String]> * @return [Set]<[String]> */ - fun getStringSet(key: String, value: Set) = - (if (isXposedEnvironment && isUsingNativeStorage.not()) - if (isUsingKeyValueCache) - XSharedPreferencesCaches.stringSetData[key].let { - (it ?: currentXsp.getStringSet(key, value) ?: value).let { value -> - XSharedPreferencesCaches.stringSetData[key] = value - value - } - } - else resetCacheSet { currentXsp.getStringSet(key, value) ?: value } - else currentSp.getStringSet(key, value) ?: value).let { - makeWorldReadable() - it - } + fun getStringSet(key: String, value: Set = hashSetOf()) = + cacheManager.getStringSet(key) { + if (isXposedEnvironment && isUsingNativeStorage.not()) + currentXsp.getStringSet(key, value) ?: value + else currentSp.getStringSet(key, value) ?: value + }.also { makeWorldReadable() } /** * 获取 [Boolean] 键值 @@ -338,19 +292,11 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @return [Boolean] */ fun getBoolean(key: String, value: Boolean = false) = - (if (isXposedEnvironment && isUsingNativeStorage.not()) - if (isUsingKeyValueCache) - XSharedPreferencesCaches.booleanData[key].let { - it ?: currentXsp.getBoolean(key, value).let { value -> - XSharedPreferencesCaches.booleanData[key] = value - value - } - } - else resetCacheSet { currentXsp.getBoolean(key, value) } - else currentSp.getBoolean(key, value)).let { - makeWorldReadable() - it - } + cacheManager.getBoolean(key) { + if (isXposedEnvironment && isUsingNativeStorage.not()) + currentXsp.getBoolean(key, value) + else currentSp.getBoolean(key, value) + }.also { makeWorldReadable() } /** * 获取 [Int] 键值 @@ -363,19 +309,11 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @return [Int] */ fun getInt(key: String, value: Int = 0) = - (if (isXposedEnvironment && isUsingNativeStorage.not()) - if (isUsingKeyValueCache) - XSharedPreferencesCaches.intData[key].let { - it ?: currentXsp.getInt(key, value).let { value -> - XSharedPreferencesCaches.intData[key] = value - value - } - } - else resetCacheSet { currentXsp.getInt(key, value) } - else currentSp.getInt(key, value)).let { - makeWorldReadable() - it - } + cacheManager.getInt(key) { + if (isXposedEnvironment && isUsingNativeStorage.not()) + currentXsp.getInt(key, value) + else currentSp.getInt(key, value) + }.also { makeWorldReadable() } /** * 获取 [Float] 键值 @@ -388,19 +326,11 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @return [Float] */ fun getFloat(key: String, value: Float = 0f) = - (if (isXposedEnvironment && isUsingNativeStorage.not()) - if (isUsingKeyValueCache) - XSharedPreferencesCaches.floatData[key].let { - it ?: currentXsp.getFloat(key, value).let { value -> - XSharedPreferencesCaches.floatData[key] = value - value - } - } - else resetCacheSet { currentXsp.getFloat(key, value) } - else currentSp.getFloat(key, value)).let { - makeWorldReadable() - it - } + cacheManager.getFloat(key) { + if (isXposedEnvironment && isUsingNativeStorage.not()) + currentXsp.getFloat(key, value) + else currentSp.getFloat(key, value) + }.also { makeWorldReadable() } /** * 获取 [Long] 键值 @@ -413,19 +343,11 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @return [Long] */ fun getLong(key: String, value: Long = 0L) = - (if (isXposedEnvironment && isUsingNativeStorage.not()) - if (isUsingKeyValueCache) - XSharedPreferencesCaches.longData[key].let { - it ?: currentXsp.getLong(key, value).let { value -> - XSharedPreferencesCaches.longData[key] = value - value - } - } - else resetCacheSet { currentXsp.getLong(key, value) } - else currentSp.getLong(key, value)).let { - makeWorldReadable() - it - } + cacheManager.getLong(key) { + if (isXposedEnvironment && isUsingNativeStorage.not()) + currentXsp.getLong(key, value) + else currentSp.getLong(key, value) + }.also { makeWorldReadable() } /** * 智能获取指定类型的键值 @@ -596,7 +518,7 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu /** * 创建新的 [Editor] * - * - 在模块环境中使用 + * - 在模块环境中或启用了 [isUsingNativeStorage] 后使用 * * - ❗在 (Xposed) 宿主环境下只读 - 无法使用 * @return [Editor] @@ -608,42 +530,28 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * * 自动调用 [Editor.apply] 方法 * - * - 在模块环境中使用 + * - 在模块环境中或启用了 [isUsingNativeStorage] 后使用 * * - ❗在 (Xposed) 宿主环境下只读 - 无法使用 * @param initiate 方法体 */ - fun edit(initiate: Editor.() -> Unit = {}) = edit().apply(initiate).apply() + fun edit(initiate: Editor.() -> Unit) = edit().apply(initiate).apply() /** - * 清除 [XSharedPreferences] 中缓存的键值数据 + * 清除 [YukiHookPrefsBridge] 中缓存的键值数据 * * 无论是否开启 [YukiHookAPI.Configs.isEnableModulePrefsCache] * * 调用此方法将清除当前存储的全部键值缓存 - * - * 下次将从 [XSharedPreferences] 重新读取 - * - * - 在 (Xposed) 宿主环境中使用 */ - fun clearCache() = XSharedPreferencesCaches.clear() - - /** - * 恢复 [isUsingKeyValueCache] 为默认状态 - * @param result 回调方法体的结果 - * @return [T] - */ - private inline fun resetCacheSet(result: () -> T): T { - isUsingKeyValueCache = YukiHookAPI.Configs.isEnablePrefsBridgeCache - return result() - } + fun clearCache() = cacheManager.edit().clear().apply() /** * [YukiHookPrefsBridge] 的存储代理类 * * - ❗请使用 [edit] 方法来获取 [Editor] * - * - 在模块环境中使用 + * - 在模块环境中或启用了 [isUsingNativeStorage] 后使用 * * - ❗在 (Xposed) 宿主环境下只读 - 无法使用 */ @@ -652,13 +560,17 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu /** 创建新的存储代理类 */ private var editor = runCatching { currentSp.edit() }.getOrNull() + /** 创建 [PreferencesCacheManager] 存储代理类 */ + private var cacheEditor = cacheManager.edit() + /** * 移除全部包含 [key] 的存储数据 * @param key 键值名称 * @return [Editor] */ - fun remove(key: String) = moduleEnvironment { + fun remove(key: String) = specifiedScope { editor?.remove(key) + cacheEditor.remove(key) makeWorldReadable() } @@ -673,8 +585,9 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * 移除全部存储数据 * @return [Editor] */ - fun clear() = moduleEnvironment { + fun clear() = specifiedScope { editor?.clear() + cacheEditor.clear() makeWorldReadable() } @@ -686,8 +599,9 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @param value 键值数据 * @return [Editor] */ - fun putString(key: String, value: String) = moduleEnvironment { + fun putString(key: String, value: String) = specifiedScope { editor?.putString(key, value) + cacheEditor.updateString(key, value) makeWorldReadable() } @@ -699,8 +613,9 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @param value 键值数据 * @return [Editor] */ - fun putStringSet(key: String, value: Set) = moduleEnvironment { + fun putStringSet(key: String, value: Set) = specifiedScope { editor?.putStringSet(key, value) + cacheEditor.updateStringSet(key, value) makeWorldReadable() } @@ -712,8 +627,9 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @param value 键值数据 * @return [Editor] */ - fun putBoolean(key: String, value: Boolean) = moduleEnvironment { + fun putBoolean(key: String, value: Boolean) = specifiedScope { editor?.putBoolean(key, value) + cacheEditor.updateBoolean(key, value) makeWorldReadable() } @@ -725,8 +641,9 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @param value 键值数据 * @return [Editor] */ - fun putInt(key: String, value: Int) = moduleEnvironment { + fun putInt(key: String, value: Int) = specifiedScope { editor?.putInt(key, value) + cacheEditor.updateInt(key, value) makeWorldReadable() } @@ -738,8 +655,9 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @param value 键值数据 * @return [Editor] */ - fun putFloat(key: String, value: Float) = moduleEnvironment { + fun putFloat(key: String, value: Float) = specifiedScope { editor?.putFloat(key, value) + cacheEditor.updateFloat(key, value) makeWorldReadable() } @@ -751,8 +669,9 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * @param value 键值数据 * @return [Editor] */ - fun putLong(key: String, value: Long) = moduleEnvironment { + fun putLong(key: String, value: Long) = specifiedScope { editor?.putLong(key, value) + cacheEditor.updateLong(key, value) makeWorldReadable() } @@ -787,19 +706,19 @@ class YukiHookPrefsBridge private constructor(private var context: Context? = nu * 提交更改 (同步) * @return [Boolean] 是否成功 */ - fun commit() = editor?.commit()?.also { makeWorldReadable() } ?: false + fun commit() = editor?.commit()?.also { cacheEditor.apply(); makeWorldReadable() } ?: false /** 提交更改 (异步) */ - fun apply() = editor?.apply().also { makeWorldReadable() } ?: Unit + fun apply() = editor?.apply().also { cacheEditor.apply(); makeWorldReadable() } ?: Unit /** - * 仅在模块环境执行 + * 仅在模块环境或 [isUsingNativeStorage] 执行 * * 非模块环境使用会打印警告信息 * @param callback 在模块环境执行 * @return [Editor] */ - private inline fun moduleEnvironment(callback: () -> Unit): Editor { + private inline fun specifiedScope(callback: () -> Unit): Editor { if (isXposedEnvironment.not() || isUsingNativeStorage) callback() else yLoggerW(msg = "YukiHookPrefsBridge.Editor not allowed in Xposed Environment") return this diff --git a/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/prefs/cache/PreferencesCacheManager.kt b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/prefs/cache/PreferencesCacheManager.kt new file mode 100644 index 00000000..902089ed --- /dev/null +++ b/yukihookapi/src/main/java/com/highcapable/yukihookapi/hook/xposed/prefs/cache/PreferencesCacheManager.kt @@ -0,0 +1,247 @@ +/* + * YukiHookAPI - An efficient Hook API and Xposed Module solution built in Kotlin. + * Copyright (C) 2019-2023 HighCapable + * https://github.com/fankes/YukiHookAPI + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This file is Created by fankes on 2023/4/20. + */ +package com.highcapable.yukihookapi.hook.xposed.prefs.cache + +import com.highcapable.yukihookapi.YukiHookAPI +import com.highcapable.yukihookapi.hook.utils.memory.factory.createLruCache +import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge + +/** + * [YukiHookPrefsBridge] 缓存管理类 + */ +internal class PreferencesCacheManager private constructor() { + + internal companion object { + + /** 当前 [PreferencesCacheManager] 单例 */ + private var instance: PreferencesCacheManager? = null + + /** + * 获取 [PreferencesCacheManager] 单例 + * @return [PreferencesCacheManager] + */ + internal fun instance() = instance ?: PreferencesCacheManager().apply { instance = this } + } + + /** 是否使用键值缓存功能 */ + private var isUsingKeyValueCache = YukiHookAPI.Configs.isEnablePrefsBridgeCache + + private val stringData = createLruCache() + private val stringSetData = createLruCache>() + private val booleanData = createLruCache() + private val intData = createLruCache() + private val longData = createLruCache() + private val floatData = createLruCache() + + /** + * 获取缓存的 [String] 键值 + * @param key 键值名称 + * @param value 无缓存获取回调 + */ + internal fun getString(key: String, value: () -> String) = + if (isUsingKeyValueCache) stringData.get(key) ?: resetCacheSet { value().also { stringData.put(key, it) } } else value() + + /** + * 获取缓存的 [Set]<[String]> 键值 + * @param key 键值名称 + * @param value 无缓存获取回调 + */ + internal fun getStringSet(key: String, value: () -> Set) = + if (isUsingKeyValueCache) stringSetData.get(key) ?: resetCacheSet { value().also { stringSetData.put(key, it) } } else value() + + /** + * 获取缓存的 [Boolean] 键值 + * @param key 键值名称 + * @param value 无缓存获取回调 + */ + internal fun getBoolean(key: String, value: () -> Boolean) = + if (isUsingKeyValueCache) booleanData.get(key) ?: resetCacheSet { value().also { booleanData.put(key, it) } } else value() + + /** + * 获取缓存的 [Int] 键值 + * @param key 键值名称 + * @param value 无缓存获取回调 + */ + internal fun getInt(key: String, value: () -> Int) = + if (isUsingKeyValueCache) intData.get(key) ?: resetCacheSet { value().also { intData.put(key, it) } } else value() + + /** + * 获取缓存的 [Long] 键值 + * @param key 键值名称 + * @param value 无缓存获取回调 + */ + internal fun getLong(key: String, value: () -> Long) = + if (isUsingKeyValueCache) longData.get(key) ?: resetCacheSet { value().also { longData.put(key, it) } } else value() + + /** + * 获取缓存的 [Float] 键值 + * @param key 键值名称 + * @param value 无缓存获取回调 + */ + internal fun getFloat(key: String, value: () -> Float) = + if (isUsingKeyValueCache) floatData.get(key) ?: resetCacheSet { value().also { floatData.put(key, it) } } else value() + + /** + * 创建新的 [Editor] + * @return [Editor] + */ + internal fun edit() = Editor() + + /** 启用缓存功能 - 跟随 [YukiHookAPI.Configs.isEnablePrefsBridgeCache] 控制 */ + internal fun enableByConfig() { + isUsingKeyValueCache = YukiHookAPI.Configs.isEnablePrefsBridgeCache + } + + /** 禁用缓存功能 */ + internal fun disable() { + isUsingKeyValueCache = false + } + + /** + * [PreferencesCacheManager] 的存储代理类 + * + * - ❗请使用 [edit] 方法来获取 [Editor] + */ + internal inner class Editor internal constructor() { + + /** 预提交任务数组 */ + private val preSubmitTasks = HashSet<() -> Unit>() + + /** + * 更新缓存的 [String] 键值 + * @param key 键值名称 + * @param value 键值内容 + * @return [Editor] + */ + internal fun updateString(key: String, value: String): Editor { + if (isUsingKeyValueCache) preSubmitTasks.add { stringData.put(key, value) } + return this + } + + /** + * 更新缓存的 [Set]<[String]> 键值 + * @param key 键值名称 + * @param value 键值内容 + * @return [Editor] + */ + internal fun updateStringSet(key: String, value: Set): Editor { + if (isUsingKeyValueCache) preSubmitTasks.add { stringSetData.put(key, value) } + return this + } + + /** + * 更新缓存的 [Boolean] 键值 + * @param key 键值名称 + * @param value 键值内容 + * @return [Editor] + */ + internal fun updateBoolean(key: String, value: Boolean): Editor { + if (isUsingKeyValueCache) preSubmitTasks.add { booleanData.put(key, value) } + return this + } + + /** + * 更新缓存的 [Int] 键值 + * @param key 键值名称 + * @param value 键值内容 + * @return [Editor] + */ + internal fun updateInt(key: String, value: Int): Editor { + if (isUsingKeyValueCache) preSubmitTasks.add { intData.put(key, value) } + return this + } + + /** + * 更新缓存的 [Long] 键值 + * @param key 键值名称 + * @param value 键值内容 + * @return [Editor] + */ + internal fun updateLong(key: String, value: Long): Editor { + if (isUsingKeyValueCache) preSubmitTasks.add { longData.put(key, value) } + return this + } + + /** + * 更新缓存的 [Float] 键值 + * @param key 键值名称 + * @param value 键值内容 + * @return [Editor] + */ + internal fun updateFloat(key: String, value: Float): Editor { + if (isUsingKeyValueCache) preSubmitTasks.add { floatData.put(key, value) } + return this + } + + /** + * 移除缓存的键值 + * @param key 键值名称 + * @return [Editor] + */ + internal fun remove(key: String): Editor { + if (isUsingKeyValueCache) runCatching { + if (stringSetData.get(key) != null) preSubmitTasks.add { stringData.remove(key) } + if (stringSetData.get(key) != null) preSubmitTasks.add { stringSetData.remove(key) } + if (booleanData.get(key) != null) preSubmitTasks.add { booleanData.remove(key) } + if (intData.get(key) != null) preSubmitTasks.add { intData.remove(key) } + if (longData.get(key) != null) preSubmitTasks.add { longData.remove(key) } + if (floatData.get(key) != null) preSubmitTasks.add { floatData.remove(key) } + }; return this + } + + /** + * 清除所有缓存的键值数据 + * @return [Editor] + */ + internal fun clear(): Editor { + preSubmitTasks.add { + stringData.evictAll() + stringSetData.evictAll() + booleanData.evictAll() + intData.evictAll() + longData.evictAll() + floatData.evictAll() + }; return this + } + + /** 提交更改 */ + internal fun apply() { + preSubmitTasks.takeIf { it.isNotEmpty() }?.onEach { it() }?.clear() + } + } + + /** + * 恢复 [isUsingKeyValueCache] 为默认状态 + * @param result 回调方法体的结果 + * @return [T] + */ + private inline fun resetCacheSet(result: () -> T): T { + enableByConfig() + return result() + } +} \ No newline at end of file