rekabhq / background_locator

A Flutter plugin for updating location in background.
MIT License
288 stars 328 forks source link

Fatal Exception: NSInternalInconsistencyException Sending a message before the FlutterEngine has been run. -[BackgroundLocatorPlugin sendLocationEvent:] #237

Closed oligazar closed 3 years ago

oligazar commented 3 years ago

iOS devices. background_locator: 1.6.0+1-beta After tracking longer time in background at the moment of transition to foreground sometimes app crashes.

Fatal Exception: NSInternalInconsistencyException Sending a message before the FlutterEngine has been run. -[BackgroundLocatorPlugin sendLocationEvent:]

Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x18ab2586c __exceptionPreprocess
1  libobjc.A.dylib                0x19fb3ec50 objc_exception_throw
2  CoreFoundation                 0x18aa2b000 -[CFPrefsSearchListSource addManagedSourceForIdentifier:user:]
3  Foundation                     0x18bdbf91c -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4  Flutter                        0x1060e3c14 (Missing)
5  background_locator             0x105ed0b24 -[BackgroundLocatorPlugin sendLocationEvent:] + 114 (BackgroundLocatorPlugin.m:114)
6  background_locator             0x105ed0904 -[BackgroundLocatorPlugin prepareLocationMap:] + 83 (BackgroundLocatorPlugin.m:83)
7  background_locator             0x105ed0984 -[BackgroundLocatorPlugin locationManager:didUpdateLocations:] + 92 (BackgroundLocatorPlugin.m:92)
8  CoreLocation                   0x190a55cb4 CLClientStopVehicleHeadingUpdates
9  CoreLocation                   0x190a54fec CLClientStopVehicleHeadingUpdates
10 CoreLocation                   0x190a3b670 CLClientInvalidate
11 CoreFoundation                 0x18aaa149c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
12 CoreFoundation                 0x18aaa06e4 __CFRunLoopDoBlocks
13 CoreFoundation                 0x18aa9aaa0 __CFRunLoopRun
14 CoreFoundation                 0x18aa9a21c CFRunLoopRunSpecific
15 GraphicsServices               0x1a2664784 GSEventRunModal
16 UIKitCore                      0x18d4daee8 -[UIApplication _run]
17 UIKitCore                      0x18d4e075c UIApplicationMain
18 Runner                         0x104f50958 main + 7 (AppDelegate.swift:7)
19 libdyld.dylib                  0x18a75a6b0 start

AppDelegate

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
        if #available(iOS 10.0, *) {
          UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
        }

        UIApplication.shared.setStatusBarHidden(false, with: UIStatusBarAnimation.none)
        // Register the plugins with the AppDelegate
        registerPlugins(self)

        BackgroundLocatorPlugin.setPluginRegistrantCallback(registerPlugins)

        // Sut Google Maps api key
        GMSServices.provideAPIKey("AIzaSyBCk1oweYbhQb2BqK3vRq1_F0mHl29YLi0")
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

func registerPlugins(_ registry: FlutterPluginRegistry) {
//    GeneratedPluginRegistrant.register(with: registry)
    CustomPluginRegistrant.register(with: registry)
}

Where CustomPluginRegistrant class looks like this

import Foundation
import firebase_core
import firebase_crashlytics
import firebase_database
import firebase_messaging
import google_maps_flutter
import geolocator
import package_info
import path_provider
import shared_preferences
import sqflite
import url_launcher
import webview_flutter
import background_fetch
import battery
import device_info
import firebase_auth
import share
import background_locator
import date_formatter_plugin
import location_permissions
import app_settings
import flutter_archive

class CustomPluginRegistrant {

    static let dateFormatterPlugin = "DateFormatterPlugin"
    static let firebaseCorePlugin = "FLTFirebaseCorePlugin"
    static let firebaseCrashlytics = "FLTFirebaseCrashlyticsPlugin"
    static let firebaseDatabasePlugin = "FLTFirebaseDatabasePlugin"
    static let firebaseMessagingPlugin = "FLTFirebaseMessagingPlugin"
    static let googleMapsPlugin = "FLTGoogleMapsPlugin"
    static let geolocatorPlugin = "GeolocatorPlugin"
    static let packageInfoPlugin = "FLTPackageInfoPlugin"
    static let pathProviderPlugin = "FLTPathProviderPlugin"
    static let sharedPreferencesPlugin = "FLTSharedPreferencesPlugin"
    static let sqflitePlugin = "SqflitePlugin"
    static let urlLauncherPlugin = "FLTUrlLauncherPlugin"
    static let webViewFlutterPlugin = "FLTWebViewFlutterPlugin"
    static let backgroundFetchPlugin = "BackgroundFetchPlugin"
    static let batteryPlugin = "FLTBatteryPlugin"
    static let deviceInfoPlugin = "FLTDeviceInfoPlugin"
    static let firebaseAuthPlugin = "FLTFirebaseAuthPlugin"
    static let sharePlugin = "FLTSharePlugin"
    static let backgroundLocatorPlugin = "BackgroundLocatorPlugin"
    static let locationPermissionsPlugin = "LocationPermissionsPlugin"
    static let appSettingsPlugin = "AppSettingsPlugin"
    static let flutterArchivePlugin = "FlutterArchivePlugin"

    static func register(with registry: FlutterPluginRegistry) {

        if (registry.hasPlugin(dateFormatterPlugin) == false) {
            DateFormatterPlugin.register(with: registry.registrar(forPlugin: dateFormatterPlugin)!)
        }
        if (registry.hasPlugin(firebaseAuthPlugin) == false) {
           FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: firebaseAuthPlugin)!)
        }
        if (registry.hasPlugin(firebaseCorePlugin) == false) {
            FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: firebaseCorePlugin)!)
        }
        if (registry.hasPlugin(firebaseCrashlytics) == false) {
            FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: firebaseCrashlytics)!)
        }
        if (registry.hasPlugin(firebaseDatabasePlugin) == false) {
            FLTFirebaseDatabasePlugin.register(with: registry.registrar(forPlugin: firebaseDatabasePlugin)!)
        }
        if (registry.hasPlugin(firebaseMessagingPlugin) == false) {
            FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: firebaseMessagingPlugin)!)
        }
        if (registry.hasPlugin(googleMapsPlugin) == false) {
            FLTGoogleMapsPlugin.register(with: registry.registrar(forPlugin: googleMapsPlugin)!)
        }
        if (registry.hasPlugin(geolocatorPlugin) == false) {
            GeolocatorPlugin.register(with: registry.registrar(forPlugin: geolocatorPlugin)!)
        }
        if (registry.hasPlugin(packageInfoPlugin) == false) {
            FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: packageInfoPlugin)!)
        }
        if (registry.hasPlugin(pathProviderPlugin) == false) {
            FLTPathProviderPlugin.register(with: registry.registrar(forPlugin: pathProviderPlugin)!)
        }
        if (registry.hasPlugin(sharedPreferencesPlugin) == false) {
            FLTSharedPreferencesPlugin.register(with: registry.registrar(forPlugin: sharedPreferencesPlugin)!)
        }
        if (registry.hasPlugin(sqflitePlugin) == false) {
            SqflitePlugin.register(with: registry.registrar(forPlugin: sqflitePlugin)!)
        }
        if (registry.hasPlugin(urlLauncherPlugin) == false) {
            FLTURLLauncherPlugin.register(with: registry.registrar(forPlugin: urlLauncherPlugin)!)
        }
        if (registry.hasPlugin(webViewFlutterPlugin) == false) {
            FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: webViewFlutterPlugin)!)
        }
        if (registry.hasPlugin(backgroundFetchPlugin) == false) {
            BackgroundFetchPlugin.register(with: registry.registrar(forPlugin: backgroundFetchPlugin)!)
        }
        if (registry.hasPlugin(batteryPlugin) == false) {
            FLTBatteryPlugin.register(with: registry.registrar(forPlugin: batteryPlugin)!)
        }
        if (registry.hasPlugin(deviceInfoPlugin) == false) {
            FLTDeviceInfoPlugin.register(with: registry.registrar(forPlugin: deviceInfoPlugin)!)
        }
        if (registry.hasPlugin(sharePlugin) == false) {
            FLTSharePlugin.register(with: registry.registrar(forPlugin: sharePlugin)!)
        }
        if (registry.hasPlugin(backgroundLocatorPlugin) == false) {
            BackgroundLocatorPlugin.register(with: registry.registrar(forPlugin: backgroundLocatorPlugin)!)
        }
        if (registry.hasPlugin(locationPermissionsPlugin) == false) {
            LocationPermissionsPlugin.register(with: registry.registrar(forPlugin: locationPermissionsPlugin)!)
        }
        if (registry.hasPlugin(appSettingsPlugin) == false) {
            AppSettingsPlugin.register(with: registry.registrar(forPlugin: appSettingsPlugin)!)
        }
    }
}

Unfortunatelly this is all I know so far. I'll update when there's more datails.

Any ideas on what might be going on here or how to prevent this from happening?

mehdok commented 3 years ago

Hi @oligazar Thank you for opening an issue and your patient;

allenbrunson commented 3 years ago

i am also seeing this bug, using version 1.5.0+1

say you just installed the app. you start it up, the background locator is running, everything is fine. then you stop the app, and wait a few hours. then you try to start it up again, and the app immediately crashes, with that same NSInternalInconsistencyException message in the crash log shown at the top there.

so you uninstall the app, re-install it, and this time it starts up just fine.

mehdok commented 3 years ago

Please try the latest beta version to see if the problem still exist.

oligazar commented 3 years ago

@mehdok unfortunately I cannot say exact conditions when the issue happens.

But I was able to catch the issue. It happened in the sendLocationEvent: method at line [_callbackChannel invokeMethod:kBCMSendLocation arguments:map]; For some reason FlutterEngine is not ready sometimes when trying to send the message down the _callbackChannel.

I used isolateId property of FlutterEngine to figure out if it's running already.

This property will be nil if the engine is not running.

So working solution for me would be :

#pragma mark LocatorPlugin Methods
- (void) sendLocationEvent: (NSDictionary<NSString*,NSNumber*>*)location {
    NSString *isolateId = [_headlessRunner isolateId];
    if (_callbackChannel == nil || isolateId == nil) {
        return;
    }

    NSDictionary *map = @{
                     kArgCallback : @([PreferencesManager getCallbackHandle:kCallbackKey]),
                     kArgLocation: location
                     };
    [_callbackChannel invokeMethod:kBCMSendLocation arguments:map];
}
Wian-TMC commented 3 years ago

Please try the latest beta version to see if the problem still exist.

still persists in the beta

rsbthebest commented 3 years ago

Am facing the same thing! Have we found any workaround for this?

mehdok commented 3 years ago

@oligazar Nice workaround, but I believe it's better to find the real problem (why FlutterEngine has stopped running?). Putting this workaround in the library makes it unrelieble.

Kurogoma4D commented 3 years ago

Same here. Just in case, here is the log when crashed my app.

(lldb) 2021-06-23 11:44:00.301574+0900 Runner[6022:3393248] 8.0.0 - [Firebase/Core][I-COR000005] No app has been configured yet.
8.0.0 - [Firebase/Analytics][I-ACS023007] Analytics v.8.0.0 started
[Firebase/Crashlytics] Version 8.0.0
8.0.0 - [Firebase/Analytics][I-ACS023008] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled (see http://goo.gl/RfcP7r)
8.0.0 - [Firebase/Analytics][I-ACS025036] App Delegate Proxy is disabled
8.0.0 - [Firebase/Analytics][I-ACS800023] No pending snapshot to activate. SDK name: app_measurement
8.0.0 - [Firebase/Analytics][I-ACS023012] Analytics collection enabled
8.0.0 - [Firebase/Analytics][I-ACS023220] Analytics screen reporting is enabled. Call +[FIRAnalytics logEventWithName:FIREventScreenView parameters:] to log a screen view event. To disable automatic screen reporting, set the flag FirebaseAutomaticScreenReportingEnabled to NO (boolean) in the Info.plist
*** Assertion failure in -[FlutterEngine sendOnChannel:message:binaryReply:], FlutterEngine.mm:799
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Sending a message before the FlutterEngine has been run.'
*** First throw call stack:
(0x19618586c 0x1ab19ec50 0x19608b000 0x19741f91c 0x104e0ec14 0x10494827c 0x104947f68 0x104948018 0x19c0b5cb4 0x19c0b4fec 0x19c09b670 0x19610149c 0x1961006e4 0x1960fae18 0x1960fa21c 0x1adcc4784 0x198b3aee8 0x198b4075c 0x102b23f94 0x195dba6b0)
libc++abi.dylib: terminating with uncaught exception of type NSException
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
    frame #0: 0x00000001c40d5414 libsystem_kernel.dylib`__pthread_kill + 8
libsystem_kernel.dylib`__pthread_kill:
->  0x1c40d5414 <+8>:  b.lo   0x1c40d5434               ; <+40>
    0x1c40d5418 <+12>: pacibsp 
    0x1c40d541c <+16>: stp    x29, x30, [sp, #-0x10]!
    0x1c40d5420 <+20>: mov    x29, sp
Target 0: (Runner) stopped.
Wian-TMC commented 3 years ago

There seems to be a different underlying issue here. When changing iOS permissions from 'Allow only while using App' to 'Always allow' when prompted, the iOS app crashes on startup again and again, even with the above workaround.

It seems to crash on iOS consistently on app startup when permissions are set to 'Always allow'.

marcusmvfa commented 3 years ago

Having the same issue, even with the workaround ... I'm using version 1.6.2+1-beta

marcusvpais commented 3 years ago

Same problem.Having the same issue too. iOS 14.6 I'm using version 1.6.2+1-beta

mehdok commented 3 years ago

248 Fix Exception occurred during the initialization of the flutter;