background_locator_fixed icon indicating copy to clipboard operation
background_locator_fixed copied to clipboard

[Question] How to correctly stop background locator & release its memory usage?

Open hieudz opened this issue 2 years ago • 6 comments

I my app, user can turn on/off background location through setting using a toggle.

When the toggle switch to off, I called await BackgroundLocator.unRegisterLocationUpdate();

When the toggle switch to on, I called

static Future<void> startBackgroundTracking() async {
    Mutex().protect(() async {
      await init();

      SharedPreferences prefs = await SharedPreferences.getInstance();

      BackgroundLocator.registerLocationUpdate(
        callbackFromBackground,
        initDataCallback: {},
        initCallback: initBackgroundTracking,
        autoStop: false,
        iosSettings: const IOSSettings(accuracy: LocationAccuracy.NAVIGATION, distanceFilter: 1),
        androidSettings: AndroidSettings(
          accuracy: LocationAccuracy.NAVIGATION,
          distanceFilter: 0,
          androidNotificationSettings: AndroidNotificationSettings(...),
        ),
      );
    });
  }

Where init() is:

static Future<void> init() async {
  _port = ReceivePort();
  if (IsolateNameServer.lookupPortByName(_isolateName) != null) {
    IsolateNameServer.removePortNameMapping(_isolateName);
  }

  _port.listen((dynamic data) async {
    // TODO: Listen from background and do something with it
  });

  await BackgroundLocator.initialize();
}

Somehow, the memory keep increasing when ever I call startBackgroundTracking(), even though I have already unregister BackgroundLocator

image Here is the snapshots of memory usage, small bumps are when I turn on the background tracking

hieudz avatar Aug 06 '22 08:08 hieudz

Btw, somehow it sends the location multiple times at the same time

D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=271.16943, latitude=37.5016455, accuracy=20.0, speed_accuracy=0.0, time=1.659818976698E12, is_mocked=false, speed=0.0070893257, longitude=127.0342517}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=271.16943, latitude=37.5016455, accuracy=20.0, speed_accuracy=0.0, time=1.659818976698E12, is_mocked=false, speed=0.0070893257, longitude=127.0342517}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=271.16943, latitude=37.5016455, accuracy=20.0, speed_accuracy=0.0, time=1.659818976698E12, is_mocked=false, speed=0.0070893257, longitude=127.0342517}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=271.16943, latitude=37.5016455, accuracy=20.0, speed_accuracy=0.0, time=1.659818976698E12, is_mocked=false, speed=0.0070893257, longitude=127.0342517}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=271.16943, latitude=37.5016455, accuracy=20.0, speed_accuracy=0.0, time=1.659818976698E12, is_mocked=false, speed=0.0070893257, longitude=127.0342517}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=271.16943, latitude=37.5016455, accuracy=20.0, speed_accuracy=0.0, time=1.659818976698E12, is_mocked=false, speed=0.0070893257, longitude=127.0342517}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=354.10712, latitude=37.5016457, accuracy=20.0, speed_accuracy=0.0, time=1.65981898171E12, is_mocked=false, speed=0.0034208533, longitude=127.0342518}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=354.10712, latitude=37.5016457, accuracy=20.0, speed_accuracy=0.0, time=1.65981898171E12, is_mocked=false, speed=0.0034208533, longitude=127.0342518}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=354.10712, latitude=37.5016457, accuracy=20.0, speed_accuracy=0.0, time=1.65981898171E12, is_mocked=false, speed=0.0034208533, longitude=127.0342518}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=354.10712, latitude=37.5016457, accuracy=20.0, speed_accuracy=0.0, time=1.65981898171E12, is_mocked=false, speed=0.0034208533, longitude=127.0342518}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=354.10712, latitude=37.5016457, accuracy=20.0, speed_accuracy=0.0, time=1.65981898171E12, is_mocked=false, speed=0.0034208533, longitude=127.0342518}}
D/plugin  ( 1455): sendLocationEvent {callback=43457826900314605, location={altitude=62.10000228881836, heading=354.10712, latitude=37.5016457, accuracy=20.0, speed_accuracy=0.0, time=1.65981898171E12, is_mocked=false, speed=0.0034208533, longitude=127.0342518}}

hieudz avatar Aug 06 '22 21:08 hieudz

Hi @hieudz, Interesting problem, I'm actually in vacations so I won't have time to investigate yet, I'll see when I can do something about it. It's kind of a serious problem though, so if someone feels like investigating, feel free to do so. I'll add a new flair for these kind of problems with community help wanted.

Yukams avatar Aug 07 '22 08:08 Yukams

I haven't figure out the detail why this happened, but I know why my app generate multiple callback error.

Basically, I triggered the startBackgroundTracking() twice within a short period (e.g. 0.0001s), which somehow causes race condition in the plug in. I suspect that background_locator_2 just protect this using

if (IsolateHolderService.isServiceRunning) {
                // The service is running already
                Log.d("BackgroundLocatorPlugin", "Locator service is already running")
                result?.success(true)
                return
            }

but it seems to be not enough for immediatel triggers like this.

Temp Solution: We can use mutex and wait for the operations to complete. It will stop sending multiple callback, but it will still have memory leak

P/s: I tried to used Mutex() as above, but I used it wrongly (I should've created Mutex mutex = Mutex() then called mutex.protect(...))

hieudz avatar Aug 08 '22 17:08 hieudz

Update: This also happened to the example app. Just turn it on/off multiple times, we can observe the memory leak image

Update: Narrowed down the problem, the memory leak disappeared when I comment out IsolateHolderService.backgroundEngine?.dartExecutor?.executeDartCallback(args) at IsolateHolderExtension.kt:51

Update: Problem founded, created by this commit https://github.com/Yukams/background_locator_fixed/commit/5d214753366c5272406f4d89f1c0d65b92e70e86

Since we create a new FlutterEngine everytime without releasing it later(?), it causes memory leak IsolateHolderService.backgroundEngine = null + IsolateHolderService.backgroundEngine = FlutterEngine(context)

Update: Here is my pull request https://github.com/Yukams/background_locator_fixed/pull/30

hieudz avatar Aug 09 '22 07:08 hieudz

I'll update it asap thank you for your contribution !

Yukams avatar Sep 07 '22 20:09 Yukams

thank you so much for your contribution.

additionally, I'm using your fixed branch on flutter 3.0.5 only on android.

until now I never stopped the service but now I need it to stop when the app is terminated.

but now every time I terminate the app and reopen I get doubled callbacks like this: image

this is what I do when I reopen the app: if (!isUpdated || !isServiceRunning) { await initBackgroundLocator(); await _registerLocator(staticRepository); }

daviddagan avatar Sep 13 '22 14:09 daviddagan