Skip to content

Commit 4f27045

Browse files
committed
Fix FlutterJNI.loadLibrary called more than once
Added aditional cleanup for the flutter engine and darExecutor + destrying so it's not re-loaded. Also, added logic to avoid unnecesary reload and reuse instead
1 parent 4c3c47e commit 4f27045

5 files changed

Lines changed: 129 additions & 24 deletions

File tree

android/src/main/kotlin/com/icapps/background_location_tracker/BackgroundLocationTrackerPlugin.kt

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ class BackgroundLocationTrackerPlugin : FlutterPlugin, MethodCallHandler, Activi
4242
channel?.setMethodCallHandler(null)
4343
channel = null
4444
applicationContext = null
45+
46+
// Clean up the background manager when plugin is detached
47+
FlutterBackgroundManager.cleanup()
4548
}
4649

4750
override fun onMethodCall(call: MethodCall, result: Result) {
@@ -73,6 +76,7 @@ class BackgroundLocationTrackerPlugin : FlutterPlugin, MethodCallHandler, Activi
7376

7477
// New static properties for background execution
7578
private var flutterEngine: FlutterEngine? = null
79+
private var isEngineInitialized = false
7680

7781
// For compatibility with older plugins
7882
@Deprecated("Use FlutterEngine's plugin registry instead")
@@ -87,9 +91,42 @@ class BackgroundLocationTrackerPlugin : FlutterPlugin, MethodCallHandler, Activi
8791
// Method to get or create the Flutter engine for background execution
8892
@JvmStatic
8993
fun getFlutterEngine(context: Context): FlutterEngine {
90-
return flutterEngine ?: FlutterEngine(context).also {
91-
flutterEngine = it
92-
pluginRegistrantCallback?.invoke(it)
94+
synchronized(this) {
95+
if (flutterEngine == null || !isEngineInitialized) {
96+
Logger.debug(TAG, "Creating new Flutter engine for background execution")
97+
flutterEngine = FlutterEngine(context).also { engine ->
98+
pluginRegistrantCallback?.invoke(engine)
99+
isEngineInitialized = true
100+
Logger.debug(TAG, "Flutter engine created and initialized")
101+
}
102+
} else {
103+
Logger.debug(TAG, "Reusing existing Flutter engine")
104+
}
105+
return flutterEngine!!
106+
}
107+
}
108+
109+
// Method to properly cleanup the Flutter engine
110+
@JvmStatic
111+
fun cleanupFlutterEngine() {
112+
synchronized(this) {
113+
flutterEngine?.let { engine ->
114+
try {
115+
Logger.debug(TAG, "Cleaning up Flutter engine")
116+
// Stop the Dart isolate if it's running
117+
if (engine.dartExecutor.isExecutingDart) {
118+
engine.dartExecutor.onDetachedFromJNI()
119+
}
120+
// Destroy the engine
121+
engine.destroy()
122+
Logger.debug(TAG, "Flutter engine destroyed")
123+
} catch (e: Exception) {
124+
// Log the exception but don't crash
125+
Logger.debug(TAG, "Error during Flutter engine cleanup: ${e.message}")
126+
}
127+
flutterEngine = null
128+
isEngineInitialized = false
129+
}
93130
}
94131
}
95132
}

android/src/main/kotlin/com/icapps/background_location_tracker/flutter/FlutterBackgroundManager.kt

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ internal object FlutterBackgroundManager {
2222
private var backgroundChannel: MethodChannel? = null
2323
private var isInitialized = false
2424
private var pendingLocation: Location? = null
25+
private var isSettingUp = false
2526

2627
private fun getInitializedFlutterEngine(ctx: Context): FlutterEngine {
2728
Logger.debug("BackgroundManager", "Getting Flutter engine")
28-
2929
return BackgroundLocationTrackerPlugin.getFlutterEngine(ctx)
3030
}
3131

@@ -43,19 +43,61 @@ internal object FlutterBackgroundManager {
4343
}
4444

4545
private fun setupBackgroundChannelIfNeeded(ctx: Context) {
46-
if (backgroundChannel != null) {
47-
return // Already setup
46+
if (backgroundChannel != null || isSettingUp) {
47+
Logger.debug("BackgroundManager", "Setup already in progress or completed")
48+
return // Already setup or in progress
4849
}
4950

51+
isSettingUp = true
5052
Logger.debug("BackgroundManager", "Setting up background channel and dart executor")
5153
val engine = getInitializedFlutterEngine(ctx)
5254

55+
// Check if the DartExecutor is already running to prevent the error
56+
if (engine.dartExecutor.isExecutingDart) {
57+
Logger.debug("BackgroundManager", "DartExecutor is already running, reusing existing executor")
58+
backgroundChannel = MethodChannel(engine.dartExecutor, BACKGROUND_CHANNEL_NAME)
59+
backgroundChannel?.setMethodCallHandler { call, result ->
60+
when (call.method) {
61+
"initialized" -> {
62+
Logger.debug("BackgroundManager", "Dart background isolate already initialized")
63+
isInitialized = true
64+
isSettingUp = false
65+
result.success(true)
66+
67+
// Send any pending location
68+
pendingLocation?.let { location ->
69+
Logger.debug("BackgroundManager", "Sending pending location after reuse")
70+
sendLocationToChannel(ctx, location)
71+
pendingLocation = null
72+
}
73+
}
74+
else -> {
75+
result.notImplemented()
76+
}
77+
}
78+
}
79+
80+
// Mark as initialized since the executor is already running
81+
isInitialized = true
82+
isSettingUp = false
83+
84+
// Send any pending location immediately if we have one
85+
pendingLocation?.let { location ->
86+
Logger.debug("BackgroundManager", "Sending pending location immediately (executor already running)")
87+
sendLocationToChannel(ctx, location)
88+
pendingLocation = null
89+
}
90+
91+
return
92+
}
93+
5394
backgroundChannel = MethodChannel(engine.dartExecutor, BACKGROUND_CHANNEL_NAME)
5495
backgroundChannel?.setMethodCallHandler { call, result ->
5596
when (call.method) {
5697
"initialized" -> {
5798
Logger.debug("BackgroundManager", "Dart background isolate initialized")
5899
isInitialized = true
100+
isSettingUp = false
59101
result.success(true)
60102

61103
// Send any pending location
@@ -72,13 +114,22 @@ internal object FlutterBackgroundManager {
72114
}
73115

74116
if (!flutterLoader.initialized()) {
117+
Logger.debug("BackgroundManager", "Initializing FlutterLoader")
75118
flutterLoader.startInitialization(ctx)
76119
}
120+
77121
flutterLoader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) {
122+
Logger.debug("BackgroundManager", "FlutterLoader initialization complete, executing Dart callback")
78123
val callbackHandle = SharedPrefsUtil.getCallbackHandle(ctx)
79124
val callbackInfo = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
80125
val dartBundlePath = flutterLoader.findAppBundlePath()
81-
engine.dartExecutor.executeDartCallback(DartExecutor.DartCallback(ctx.assets, dartBundlePath, callbackInfo))
126+
127+
try {
128+
engine.dartExecutor.executeDartCallback(DartExecutor.DartCallback(ctx.assets, dartBundlePath, callbackInfo))
129+
} catch (e: Exception) {
130+
Logger.debug("BackgroundManager", "Error executing Dart callback: ${e.message}")
131+
isSettingUp = false
132+
}
82133
}
83134
}
84135

@@ -122,7 +173,21 @@ internal object FlutterBackgroundManager {
122173
fun cleanup() {
123174
Logger.debug("BackgroundManager", "Cleaning up background resources")
124175
isInitialized = false
176+
isSettingUp = false
177+
backgroundChannel?.setMethodCallHandler(null)
125178
backgroundChannel = null
126179
pendingLocation = null
180+
181+
// Instead of destroying the engine completely, just mark it as not initialized
182+
// This allows for reuse without the "DartExecutor already running" error
183+
Logger.debug("BackgroundManager", "Background channel cleaned up, ready for reuse")
184+
}
185+
186+
fun forceCleanup() {
187+
Logger.debug("BackgroundManager", "Force cleaning up all background resources")
188+
cleanup()
189+
190+
// Clean up the Flutter engine completely to prevent DartExecutor reuse issues
191+
BackgroundLocationTrackerPlugin.cleanupFlutterEngine()
127192
}
128193
}

android/src/main/kotlin/com/icapps/background_location_tracker/service/LocationUpdatesService.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ internal class LocationUpdatesService : Service() {
160160
if (wakeLock?.isHeld == true) {
161161
wakeLock?.release()
162162
}
163+
164+
// Force clean up Flutter background resources when service is destroyed
165+
FlutterBackgroundManager.forceCleanup()
163166
}
164167

165168
/**

example/pubspec.lock

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ packages:
1313
dependency: transitive
1414
description:
1515
name: async
16-
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
16+
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
1717
url: "https://pub.dev"
1818
source: hosted
19-
version: "2.12.0"
19+
version: "2.13.0"
2020
background_location_tracker:
2121
dependency: "direct main"
2222
description:
2323
path: ".."
2424
relative: true
2525
source: path
26-
version: "1.5.0"
26+
version: "1.6.0"
2727
boolean_selector:
2828
dependency: transitive
2929
description:
@@ -68,10 +68,10 @@ packages:
6868
dependency: transitive
6969
description:
7070
name: fake_async
71-
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
71+
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
7272
url: "https://pub.dev"
7373
source: hosted
74-
version: "1.3.2"
74+
version: "1.3.3"
7575
ffi:
7676
dependency: transitive
7777
description:
@@ -155,10 +155,10 @@ packages:
155155
dependency: transitive
156156
description:
157157
name: leak_tracker
158-
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
158+
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
159159
url: "https://pub.dev"
160160
source: hosted
161-
version: "10.0.8"
161+
version: "10.0.9"
162162
leak_tracker_flutter_testing:
163163
dependency: transitive
164164
description:
@@ -456,10 +456,10 @@ packages:
456456
dependency: transitive
457457
description:
458458
name: vm_service
459-
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
459+
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
460460
url: "https://pub.dev"
461461
source: hosted
462-
version: "14.3.1"
462+
version: "15.0.0"
463463
web:
464464
dependency: transitive
465465
description:

pubspec.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ packages:
55
dependency: transitive
66
description:
77
name: async
8-
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
8+
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
99
url: "https://pub.dev"
1010
source: hosted
11-
version: "2.12.0"
11+
version: "2.13.0"
1212
boolean_selector:
1313
dependency: transitive
1414
description:
@@ -45,10 +45,10 @@ packages:
4545
dependency: transitive
4646
description:
4747
name: fake_async
48-
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
48+
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
4949
url: "https://pub.dev"
5050
source: hosted
51-
version: "1.3.2"
51+
version: "1.3.3"
5252
flutter:
5353
dependency: "direct main"
5454
description: flutter
@@ -63,10 +63,10 @@ packages:
6363
dependency: transitive
6464
description:
6565
name: leak_tracker
66-
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
66+
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
6767
url: "https://pub.dev"
6868
source: hosted
69-
version: "10.0.8"
69+
version: "10.0.9"
7070
leak_tracker_flutter_testing:
7171
dependency: transitive
7272
description:
@@ -180,10 +180,10 @@ packages:
180180
dependency: transitive
181181
description:
182182
name: vm_service
183-
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
183+
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
184184
url: "https://pub.dev"
185185
source: hosted
186-
version: "14.3.1"
186+
version: "15.0.0"
187187
sdks:
188188
dart: ">=3.7.0-0 <4.0.0"
189189
flutter: ">=3.18.0-18.0.pre.54"

0 commit comments

Comments
 (0)