From 3ec9b9a4f7de2c39816a5e5f407a1d85e6b5ecd2 Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Mon, 13 Oct 2025 02:52:19 +0700 Subject: [PATCH] fix: run Module.onContext properly for recreated processes --- .../kotlin/io/github/revenge/xposed/Main.kt | 30 +++++++++++++++---- .../xposed/modules/HookScriptLoaderModule.kt | 4 +-- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/io/github/revenge/xposed/Main.kt b/app/src/main/kotlin/io/github/revenge/xposed/Main.kt index c7efb07..62873f9 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/Main.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/Main.kt @@ -16,8 +16,19 @@ import io.github.revenge.xposed.modules.bridge.AdditionalBridgeMethodsModule import io.github.revenge.xposed.modules.bridge.BridgeModule import kotlinx.coroutines.CompletableDeferred -object HookReadyHolder { - val deferred = CompletableDeferred() +object HookStateHolder { + /** + * Whether all hooks are completed, and we are ready to load the JS bundle. + */ + val readyDeferred = CompletableDeferred() + + /** + * Whether we have successfully received a [Context] yet. + * Sometimes the app process is recreated and Xposed hooks way too late for us to get [Context] from [ContextWrapper.attachBaseContext]. + * But since Xposed hooks before [Activity.onCreate], we can still get it from there and still initialize properly. + */ + @Volatile + var gotContext = false } class Main : Module(), IXposedHookLoadPackage, IXposedHookZygoteInit { @@ -51,16 +62,25 @@ class Main : Module(), IXposedHookLoadPackage, IXposedHookZygoteInit { ContextWrapper::class.java.hookMethod("attachBaseContext", Context::class.java) { after { + val ctx = args[0] as Context + HookStateHolder.gotContext = true Log.i("Received Context") - this@Main.onContext(args[0] as Context) + this@Main.onContext(ctx) } } reactActivity.hookMethod("onCreate", Bundle::class.java) { after { + val act = thisObject as Activity Log.i("Received Activity") - this@Main.onActivity(thisObject as Activity) - HookReadyHolder.deferred.complete(Unit) + + if (!HookStateHolder.gotContext) { + Log.w("Activity created before we got Context, process may have been recreated!") + this@Main.onContext(act.applicationContext) + } + + this@Main.onActivity(act) + HookStateHolder.readyDeferred.complete(Unit) } } diff --git a/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt b/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt index 4359066..b027109 100644 --- a/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt +++ b/app/src/main/kotlin/io/github/revenge/xposed/modules/HookScriptLoaderModule.kt @@ -6,7 +6,7 @@ import de.robv.android.xposed.IXposedHookZygoteInit import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.callbacks.XC_LoadPackage import io.github.revenge.xposed.Constants -import io.github.revenge.xposed.HookReadyHolder +import io.github.revenge.xposed.HookStateHolder import io.github.revenge.xposed.Module import io.github.revenge.xposed.Utils.Log import io.github.revenge.xposed.modules.HookScriptLoaderModule.Companion.PRELOADS_DIR @@ -88,7 +88,7 @@ class HookScriptLoaderModule : Module() { Log.i("Running custom scripts...") runBlocking { - val ready = async { HookReadyHolder.deferred.join() } + val ready = async { HookStateHolder.readyDeferred.join() } val download = async { UpdaterModule.downloadScript().join() } awaitAll(ready, download)