From 6e9f04b50861366515c159d41d4d74235cee262c Mon Sep 17 00:00:00 2001 From: Bruce Ashton Date: Sun, 4 Oct 2026 00:11:36 +1300 Subject: [PATCH] Android: don't start the game thread twice when recreated during setup On first launch the assets are copied in an AsyncTask. If the activity was recreated during the copy (for example by a dark mode or wallpaper colour change), onDestroy replaced the static NH_State and the new activity started a second copy. Both copies then called startNetHack on the new state, and the second Thread.start() threw IllegalThreadStateException, crashing the app. Each copy now remembers the NH_State it was started for and only starts the game if that state is still current. Co-Authored-By: Claude Opus 5.5 --- .../src/com/tbd/forkfront/ForkFront.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/sys/android/forkfront/src/com/tbd/forkfront/ForkFront.java b/sys/android/forkfront/src/com/tbd/forkfront/ForkFront.java index 2affc2cee..5a03de68a 100644 --- a/sys/android/forkfront/src/com/tbd/forkfront/ForkFront.java +++ b/sys/android/forkfront/src/com/tbd/forkfront/ForkFront.java @@ -115,7 +115,7 @@ public String decode(byte[] bytes) { }; nhState = new NH_State(this, decoder); - new UpdateAssets(this, onAssetsReady).execute((Void[])null); + new UpdateAssets(this, assetsReadyFor(nhState)).execute((Void[])null); } else { @@ -195,20 +195,29 @@ public void onConfigurationChanged(Configuration newConfig) } // ____________________________________________________________________________________ - private UpdateAssets.Listener onAssetsReady = new UpdateAssets.Listener() + // If the activity is recreated while the assets are being copied, the old copy finishes + // after a new NH_State has replaced this one. Only the copy started for the current state + // may start the game, or the game thread would be started twice. + private UpdateAssets.Listener assetsReadyFor(final NH_State state) { - @Override - public void onAssetsReady(File path) + return new UpdateAssets.Listener() { - // Create save directory if it doesn't exist - File nhSaveDir = new File(path, "save"); - if(!nhSaveDir.exists()) - nhSaveDir.mkdir(); + @Override + public void onAssetsReady(File path) + { + if(state != nhState) + return; - PreferenceManager.setDefaultValues(ForkFront.this, R.xml.preferences, false); - nhState.startNetHack(path.getAbsolutePath()); - } - }; + // Create save directory if it doesn't exist + File nhSaveDir = new File(path, "save"); + if(!nhSaveDir.exists()) + nhSaveDir.mkdir(); + + PreferenceManager.setDefaultValues(ForkFront.this, R.xml.preferences, false); + state.startNetHack(path.getAbsolutePath()); + } + }; + } // ____________________________________________________________________________________ @Override