diff --git a/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/HookEntry.kt b/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/HookEntry.kt index baebad85..e93a40f7 100644 --- a/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/HookEntry.kt +++ b/demo-module/src/main/java/com/highcapable/yukihookapi/demo_module/hook/HookEntry.kt @@ -38,7 +38,7 @@ import com.highcapable.yukihookapi.hook.xposed.proxy.YukiHookXposedInitProxy @InjectYukiHookWithXposed class HookEntry : YukiHookXposedInitProxy { - override fun onHook() { + override fun onInit() { // 配置 YuKiHookAPI // 可简写为 configs {} YukiHookAPI.configs { @@ -48,7 +48,14 @@ class HookEntry : YukiHookXposedInitProxy { isDebug = true // 是否启用调试日志的输出功能 isAllowPrintingLogs = true + // 是否启用 [YukiHookModulePrefs] 的键值缓存功能 + // 若无和模块频繁交互数据在宿主重新启动之前建议开启 + // 若需要实时交互数据建议关闭或从 [YukiHookModulePrefs] 中进行动态配置 + isEnableModulePrefsCache = true } + } + + override fun onHook() { // 开始你的 Hook // 可简写为 encase {} YukiHookAPI.encase { diff --git a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt index 5cb6849b..4bd03611 100644 --- a/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt +++ b/yukihookapi-ksp-xposed/src/api/kotlin/com/highcapable/yukihookapi_ksp_xposed/YukiHookXposedProcessor.kt @@ -246,7 +246,13 @@ class YukiHookXposedProcessor : SymbolProcessorProvider { " override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam?) {\n" + " if (lpparam == null) return\n" + " try {\n" + + " $className().onInit()\n" + + " if (YukiHookAPI.isXposedCallbackSetUp) {\n" + + " loggerE(tag = \"YukiHookAPI\", msg = \"You cannot loading a hooker in \\\"onInit\\\" method! Aborted\")\n" + + " return\n" + + " }\n" + " $className().onHook()\n" + + " YukiHookAPI.onXposedInitialized()\n" + " } catch (e: Throwable) {\n" + " loggerE(tag = \"YukiHookAPI\", msg = \"YukiHookAPI try to load HookEntryClass failed\", e = e)\n" + " }\n" + diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt index 4cc7482c..f41dcc21 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/YukiHookAPI.kt @@ -40,6 +40,7 @@ import com.highcapable.yukihookapi.hook.factory.processName import com.highcapable.yukihookapi.hook.log.* import com.highcapable.yukihookapi.hook.param.PackageParam import com.highcapable.yukihookapi.hook.param.wrapper.PackageParamWrapper +import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.callbacks.XC_LoadPackage @@ -60,12 +61,25 @@ object YukiHookAPI { /** 是否还未输出欢迎信息 */ private var isShowSplashLogOnceTime = true + /** Xposed 是否装载完成 */ + private var isXposedInitialized = false + /** 获取当前 [YukiHookAPI] 的版本 */ const val API_VERSION_NAME = "1.0.4" /** 获取当前 [YukiHookAPI] 的版本号 */ const val API_VERSION_CODE = 5 + /** + * 模块是否装载了 Xposed 回调方法 + * + * - ❗此变量为私有功能性 API - 你不应该手动调用此变量 + * @return [Boolean] + */ + @DoNotUseField + val isXposedCallbackSetUp + get() = !isXposedInitialized && packageParamCallback != null + /** * 预设的 Xposed 模块包名 * @@ -140,6 +154,15 @@ object YukiHookAPI { * 当 [isAllowPrintingLogs] 关闭后 [isDebug] 也将同时关闭 */ var isAllowPrintingLogs = true + + /** + * 是否启用 [YukiHookModulePrefs] 的键值缓存功能 + * + * - 为防止内存复用过高问题 - 此功能默认启用 + * + * 你可以手动在 [YukiHookModulePrefs] 中自由开启和关闭缓存功能以及清除缓存 + */ + var isEnableModulePrefsCache = true } /** @@ -151,6 +174,16 @@ object YukiHookAPI { */ fun configs(initiate: Configs.() -> Unit) = Configs.apply(initiate) + /** + * 标识 Xposed API 装载完成 + * + * - ❗装载代码将自动生成 - 你不应该手动使用此方法装载 Xposed 模块事件 + */ + @DoNotUseMethod + fun onXposedInitialized() { + isXposedInitialized = true + } + /** * 装载 Xposed API 回调 * diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt index b109e90b..f608a44f 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/prefs/YukiHookModulePrefs.kt @@ -79,6 +79,9 @@ class YukiHookModulePrefs(private val context: Context? = null) { /** 缓存数据 */ private var xPrefCacheKeyValueFloats = HashMap() + /** 是否使用键值缓存 */ + private var isUsingKeyValueCache = YukiHookAPI.Configs.isEnableModulePrefsCache + /** 检查是否处于自定义 Hook API 状态 */ private fun checkApiInBaseContext() { if (YukiHookAPI.isLoadedFromBaseContext) error("YukiHookModulePrefs not allowed in Custom Hook API") @@ -124,10 +127,24 @@ class YukiHookModulePrefs(private val context: Context? = null) { * @return [YukiHookModulePrefs] */ fun name(name: String): YukiHookModulePrefs { + isUsingKeyValueCache = YukiHookAPI.Configs.isEnableModulePrefsCache prefsName = name return this } + /** + * 忽略缓存直接读取键值 + * + * 无论是否开启 [YukiHookAPI.Configs.isEnableModulePrefsCache] + * + * - 仅在 [XSharedPreferences] 下生效 + * @return [YukiHookModulePrefs] + */ + fun direct(): YukiHookModulePrefs { + isUsingKeyValueCache = false + return this + } + /** * 获取 [String] 键值 * @@ -138,12 +155,14 @@ class YukiHookModulePrefs(private val context: Context? = null) { */ fun getString(key: String, default: String = "") = (if (isXposedEnvironment) - xPrefCacheKeyValueStrings[key].let { - (it ?: xPref.getString(key, default) ?: default).let { value -> - xPrefCacheKeyValueStrings[key] = value - value + if (isUsingKeyValueCache) + xPrefCacheKeyValueStrings[key].let { + (it ?: xPref.getString(key, default) ?: default).let { value -> + xPrefCacheKeyValueStrings[key] = value + value + } } - } + else xPref.getString(key, default) ?: default else sPref.getString(key, default) ?: default).let { makeWorldReadable() it @@ -159,12 +178,14 @@ class YukiHookModulePrefs(private val context: Context? = null) { */ fun getBoolean(key: String, default: Boolean = false) = (if (isXposedEnvironment) - xPrefCacheKeyValueBooleans[key].let { - it ?: xPref.getBoolean(key, default).let { value -> - xPrefCacheKeyValueBooleans[key] = value - value + if (isUsingKeyValueCache) + xPrefCacheKeyValueBooleans[key].let { + it ?: xPref.getBoolean(key, default).let { value -> + xPrefCacheKeyValueBooleans[key] = value + value + } } - } + else xPref.getBoolean(key, default) else sPref.getBoolean(key, default)).let { makeWorldReadable() it @@ -180,12 +201,14 @@ class YukiHookModulePrefs(private val context: Context? = null) { */ fun getInt(key: String, default: Int = 0) = (if (isXposedEnvironment) - xPrefCacheKeyValueInts[key].let { - it ?: xPref.getInt(key, default).let { value -> - xPrefCacheKeyValueInts[key] = value - value + if (isUsingKeyValueCache) + xPrefCacheKeyValueInts[key].let { + it ?: xPref.getInt(key, default).let { value -> + xPrefCacheKeyValueInts[key] = value + value + } } - } + else xPref.getInt(key, default) else sPref.getInt(key, default)).let { makeWorldReadable() it @@ -201,12 +224,14 @@ class YukiHookModulePrefs(private val context: Context? = null) { */ fun getFloat(key: String, default: Float = 0f) = (if (isXposedEnvironment) - xPrefCacheKeyValueFloats[key].let { - it ?: xPref.getFloat(key, default).let { value -> - xPrefCacheKeyValueFloats[key] = value - value + if (isUsingKeyValueCache) + xPrefCacheKeyValueFloats[key].let { + it ?: xPref.getFloat(key, default).let { value -> + xPrefCacheKeyValueFloats[key] = value + value + } } - } + else xPref.getFloat(key, default) else sPref.getFloat(key, default)).let { makeWorldReadable() it @@ -222,12 +247,14 @@ class YukiHookModulePrefs(private val context: Context? = null) { */ fun getLong(key: String, default: Long = 0L) = (if (isXposedEnvironment) - xPrefCacheKeyValueLongs[key].let { - it ?: xPref.getLong(key, default).let { value -> - xPrefCacheKeyValueLongs[key] = value - value + if (isUsingKeyValueCache) + xPrefCacheKeyValueLongs[key].let { + it ?: xPref.getLong(key, default).let { value -> + xPrefCacheKeyValueLongs[key] = value + value + } } - } + else xPref.getLong(key, default) else sPref.getLong(key, default)).let { makeWorldReadable() it @@ -321,4 +348,19 @@ class YukiHookModulePrefs(private val context: Context? = null) { sPref.edit().putLong(key, value).apply() makeWorldReadable() } + + /** + * 无论是否开启 [YukiHookAPI.Configs.isEnableModulePrefsCache] + * + * 调用此方法将清除当前存储的全部键值缓存 + * + * 下次将从 [XSharedPreferences] 重新读取 + */ + fun clearCache() { + xPrefCacheKeyValueStrings.clear() + xPrefCacheKeyValueBooleans.clear() + xPrefCacheKeyValueInts.clear() + xPrefCacheKeyValueLongs.clear() + xPrefCacheKeyValueFloats.clear() + } } \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/proxy/YukiHookXposedInitProxy.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/proxy/YukiHookXposedInitProxy.kt index 88a0db1d..288bbc03 100644 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/proxy/YukiHookXposedInitProxy.kt +++ b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/xposed/proxy/YukiHookXposedInitProxy.kt @@ -31,6 +31,7 @@ package com.highcapable.yukihookapi.hook.xposed.proxy import com.highcapable.yukihookapi.YukiHookAPI import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed +import com.highcapable.yukihookapi.hook.factory.configs import com.highcapable.yukihookapi.hook.factory.encase /** @@ -38,14 +39,27 @@ import com.highcapable.yukihookapi.hook.factory.encase * * - ❗请在此类上添加注释 [InjectYukiHookWithXposed] 标记模块 Hook 入口 * + * [YukiHookAPI] 初始化时将自动调用 [onInit] 方法 + * * Hook 开始时将自动调用 [onHook] 方法 * + * 请在 [onInit] 中调用 [YukiHookAPI.configs] 或直接调用 [configs] + * * 请在 [onHook] 中调用 [YukiHookAPI.encase] 或直接调用 [encase] * * 详情请参考 [YukiHookXposedInitProxy 接口](https://github.com/fankes/YukiHookAPI/wiki/%E4%BD%9C%E4%B8%BA-Xposed-%E6%A8%A1%E5%9D%97%E4%BD%BF%E7%94%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE#yukihookxposedinitproxy-%E6%8E%A5%E5%8F%A3) */ interface YukiHookXposedInitProxy { + /** + * 配置 [YukiHookAPI.Configs] 的初始化方法 + * + * - ❗在这里只能进行初始化配置 - 不能进行 Hook 操作 + * + * 此方法可选 - 你也可以选择不对 [YukiHookAPI.Configs] 进行配置 + */ + fun onInit() {} + /** * 模块装载调用入口方法 *