fix(android): stop double-destroying the shared audio_service engine

MainActivity only overrode provideFlutterEngine, so the activity's
fragment delegate treated the plugin-cached engine as its own
(createFlutterFragment forwards shouldDestroyEngineWithHost, default
true) and destroyed it when the activity finished, while the engine
stayed registered in AudioServicePlugin's FlutterEngineCache. When
AudioService stopped afterwards, disposeFlutterEngine() called
destroy() on the already destroyed engine and crashed the app with
"Cannot execute operation because FlutterJNI is not attached to
native".

Mirror audio_service's own AudioServiceFragmentActivity: expose the
plugin engine as a cached engine (getCachedEngineId), never destroy it
with the host (shouldDestroyEngineWithHost = false), and pre-create it
in onCreate. The plugin remains the engine's sole owner and disposes
it only once, when the service stops with no activity attached.
This commit is contained in:
zarzet
2026-07-10 10:22:39 +07:00
parent 7d73e77f9b
commit f808a6a369
@@ -38,10 +38,24 @@ import java.security.MessageDigest
import java.util.Locale
class MainActivity: FlutterFragmentActivity() {
// Mirrors audio_service's AudioServiceFragmentActivity: the shared engine
// is owned by AudioServicePlugin's FlutterEngineCache. Without the
// cached-engine-id + shouldDestroyEngineWithHost overrides, the activity
// (via createFlutterFragment's destroyEngineWithFragment) destroys the
// provided engine on exit while it stays registered in the cache; when
// AudioService later stops, disposeFlutterEngine() destroys it a second
// time and crashes with "FlutterJNI is not attached to native".
override fun provideFlutterEngine(context: Context): FlutterEngine {
return AudioServicePlugin.getFlutterEngine(context)
}
override fun getCachedEngineId(): String {
AudioServicePlugin.getFlutterEngine(this)
return AudioServicePlugin.getFlutterEngineId()
}
override fun shouldDestroyEngineWithHost(): Boolean = false
private val CHANNEL = "com.zarz.spotiflac/backend"
private val DOWNLOAD_PROGRESS_STREAM_CHANNEL =
"com.zarz.spotiflac/download_progress_stream"
@@ -2060,6 +2074,9 @@ class MainActivity: FlutterFragmentActivity() {
override fun shouldHandleDeeplinking(): Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
// Ensure the shared audio_service engine exists before the activity
// delegate looks it up by cached id (see getCachedEngineId above).
AudioServicePlugin.getFlutterEngine(this)
super.onCreate(savedInstanceState)
handleExtensionOAuthIntent(intent)
}