Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

infinite repeat backgroundCallback when First time only install app for any device android #54

Open
AhkamKhallaf opened this issue Jun 22, 2023 · 29 comments

Comments

@AhkamKhallaf
Copy link

AhkamKhallaf commented Jun 22, 2023

when BackgroundLocationTrackerManager.handleBackgroundUpdated is called ,
i call api request to save this new location to server , but this request is repeated many times ,
how cal fix this bug

@pragma('vm:entry-point')
void backgroundCallback() {
  BackgroundLocationTrackerManager.handleBackgroundUpdated(
      (BackgroundLocationUpdateData data) async {
    await providerContainer.read(currentLocationApiProvider).saveUserLocation(data);
  });
}

final Provider<CurrentLocationApi> currentLocationApiProvider = Provider<CurrentLocationApi>(
        (ProviderRef<CurrentLocationApi> ref) => CurrentLocationApi(ref.read));

class CurrentLocationApi {
  final Reader read;

  CurrentLocationApi(this.read);

  Preferences get preferences => read(preferencesProvider);

  ApiCore get apiCore => read(apiCoreProvider);

  Future<void> saveUserLocation(
      BackgroundLocationUpdateData data,
      ) async {
    await preferences.init();
    apiCore.init();
    try {
      await read(userApiProvider).saveUserLocation(data);
    } catch (onError) {
      debugPrint('$onError,,onError');
    }
  }
}

i use river pod as state management @vanlooverenkoen

@vanlooverenkoen
Copy link
Contributor

What do you exactly mean? The api call will be triggered every time the location updates so this is to be expected

@vanlooverenkoen
Copy link
Contributor

On Android/iOS?

@AhkamKhallaf
Copy link
Author

AhkamKhallaf commented Jun 22, 2023

On Android/iOS

android
such as OPPO A31 ANDROID , SAMSUNG 172 ANDROID

@dev07060
Copy link

dev07060 commented Jul 25, 2023

The trackingInterval option actually does not work properly In my emulator which is built with A30, A33
and also on Samsung Galaxy S10(SM G977N) too. if the tracking altitude is not required in your project, geoloactor + flutter_background_service could be a good alternative option it has Stream event and you can adjust it's interval by rxDart throttleTime.

@ikbendewilliam
Copy link
Contributor

@AhkamKhalaaf Do you do WidgetsFlutterBinding.ensureInitialized(); before the await BackgroundLocationTrackerManager.initialize?

@AhkamKhallaf
Copy link
Author

@AhkamKhalaaf Do you do WidgetsFlutterBinding.ensureInitialized(); before the await BackgroundLocationTrackerManager.initialize?

yes , i do

@ikbendewilliam
Copy link
Contributor

Have you tried running the example project? Do you also encounter it then?

@AhkamKhallaf
Copy link
Author

Have you tried running the example project? Do you also encounter it then?

yes , it's also, there is the same problem

@vanlooverenkoen
Copy link
Contributor

When exactly do you have this issue?

  • Only when the app is installed for the first time?
  • With every clean install?
  • Everytime
  • Only when the device in restarted?

@AhkamKhallaf
Copy link
Author

  • With every clean install?

With every clean install

@vanlooverenkoen
Copy link
Contributor

Just for my understanding:

  • clean install
  • open app
  • start tracking
  • ISSUE HAPPENS
  • quit app
  • stop tracking
  • open app
  • start tracking
  • everything works

Are these steps correct?

@AhkamKhallaf
Copy link
Author

AhkamKhallaf commented Sep 11, 2023

  • With every clean install?

With every clean install

i note , now , it does not work with the example project, and not with my project

 WidgetsFlutterBinding.ensureInitialized();
  /// Background Location initialize
  await BackgroundLocationTrackerManager.initialize(
    backgroundCallback,
    config: const BackgroundLocationTrackerConfig(
      androidConfig: AndroidConfig(
        distanceFilterMeters: 100,
        trackingInterval: Duration(minutes: 5),
        enableCancelTrackingAction: false,
      ),
      iOSConfig: IOSConfig(
        activityType: ActivityType.FITNESS,
        distanceFilterMeters: 100,
        restartAfterKill: true,
      ),
    ),
  );

  // this my config

  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  final ProviderContainer providerContainer = ProviderContainer();
  if (flavorConfig == null) {
    await dotenv.load();
  }

  final Preferences preferences = providerContainer.read(preferencesProvider);
  await preferences.init();
  await Future.wait(<Future<void>>[
    loadImage(splashBackgroundAssets),
    loadImage(splashImageAssets),
  ]);

  await EasyLocalization.ensureInitialized();

  await providerContainer.read(packageInfoHelperProvider).getPackageVersion();

  FlavorConfig.set(
    flavorConfig: preferences.flavorConfig ?? flavorConfig ?? FlavorConfig.production(),
    read: providerContainer.read,
  );

  await Firebase.initializeApp();

  ///Initialize events
  EventsHelper().setEvents(
    events: <Events>[providerContainer.read(firebaseEventsProvider)],
  );

  providerContainer.read(apiCoreProvider).init();

  /// Disable logs in release mode.
  if (kReleaseMode) debugPrint = (String? message, {int? wrapWidth}) {};

  /// disable red screen of death in production.
  if (!isIntegrationTesting) {
    ErrorWidget.builder = (FlutterErrorDetails details) => const SizedBox.shrink();

    ///Enable & initialize crashlytics when we don't run integration testing.
    Crashlytics().enableCrashlytics();
  }

  await SystemChrome.setPreferredOrientations(
    <DeviceOrientation>[DeviceOrientation.portraitUp],
  );
  await providerContainer.read(appStateProvider).fetchAppToken();

  ///Initialize dynamic link
  DynamicLink().getInitialLink();

  final List<Translation> translations = await providerContainer
      .read(translationHelperProvider)
      .getTranslations(forceRefresh: false, keys: <String>["customTranslations"]);

  /// init flutter_bug_logger
  Logger.init(FlavorConfig.isStage());


  runApp(
    showDeviceReview
        ? DevicePreview(
            builder: (BuildContext context) => UncontrolledProviderScope(
              container: providerContainer,
              child: RestartWidget(
                translations: translations,
                showDeviceReview: showDeviceReview,
              ),
            ),
          )
        : UncontrolledProviderScope(
            container: providerContainer,
            child: RestartWidget(
              translations: translations,
              showDeviceReview: showDeviceReview,
            ),
          ),
  );

other code which i run inside main

@AhkamKhallaf
Copy link
Author

AhkamKhallaf commented Sep 11, 2023

my background meth when location changed , now i just print , it repeat many times

@AhkamKhallaf
Copy link
Author

AhkamKhallaf commented Sep 11, 2023

Just for my understanding:

  • clean install
  • open app
  • start tracking
  • ISSUE HAPPENS
  • quit app
  • stop tracking
  • open app
  • start tracking
  • everything works

Are these steps correct?

yes, but the tracking is enabled always

@vanlooverenkoen
Copy link
Contributor

@dev07060 can you confirm you have exactly the same issue in your project?

And is everything working in our example project?

@ikbendewilliam
Copy link
Contributor

Do you immediately start the tracking or is it after a button press or something (just to make sure you first ask permissions)? For me your config works fine in our example project 🤔

@AhkamKhallaf
Copy link
Author

Do you immediately start the tracking or is it after a button press or something (just to make sure you first ask permissions)? For me your config works fine in our example project 🤔

in my project, i depend on silent notification to start track , and i ask user for permissions for location && tracking

@vanlooverenkoen
Copy link
Contributor

Because everything works in the example project, I think in order to help you further we should need a minimal reproducible project. that only contains

  • the bug
  • minimal screens
  • minimal logic
  • no setup

Can you provide us with that.

@AhkamKhallaf
Copy link
Author

AhkamKhallaf commented Sep 11, 2023

Because everything works in the example project, I think in order to help you further we should need a minimal reproducible project. that only contains

  • the bug
  • minimal screens
  • minimal logic
  • no setup

Can you provide us with that.

i use this package to get updated location with fore&&back ground , there's not any ui for that ,
i depends on silent notification to start &&end tracking ,
i use Flutter (Channel stable, 3.10.6) , Dart SDK version: 3.0.6
i'll put copy of my main method

Future main({
FlavorConfig? flavorConfig,
bool isIntegrationTesting = false,
bool showDeviceReview = false,
}) async {
WidgetsFlutterBinding.ensureInitialized();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

if (flavorConfig == null) {
await dotenv.load();
}

final Preferences preferences = providerContainer.read(preferencesProvider);
await preferences.init();
await Future.wait(<Future>[
loadImage(splashBackgroundAssets),
loadImage(splashImageAssets),
]);

await EasyLocalization.ensureInitialized();

await providerContainer.read(packageInfoHelperProvider).getPackageVersion();

FlavorConfig.set(
flavorConfig: preferences.flavorConfig ?? flavorConfig ?? FlavorConfig.production(),
read: providerContainer.read,
);

await Firebase.initializeApp();

///Initialize events
EventsHelper().setEvents(
events: [providerContainer.read(firebaseEventsProvider)],
);

providerContainer.read(apiCoreProvider).init();

/// Disable logs in release mode.
if (kReleaseMode) debugPrint = (String? message, {int? wrapWidth}) {};

/// disable red screen of death in production.
if (!isIntegrationTesting) {
ErrorWidget.builder = (FlutterErrorDetails details) => const SizedBox.shrink();

///Enable & initialize crashlytics when we don't run integration testing.
Crashlytics().enableCrashlytics();

}

await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp],
);
await providerContainer.read(appStateProvider).fetchAppToken();

///Initialize dynamic link
DynamicLink().getInitialLink();

final List translations = await providerContainer
.read(translationHelperProvider)
.getTranslations(forceRefresh: false, keys: ["customTranslations"]);

/// init flutter_bug_logger
Logger.init(FlavorConfig.isStage());

/// Background Location initialize
await BackgroundLocationTrackerManager.initialize(
backgroundCallback,
config: const BackgroundLocationTrackerConfig(
androidConfig: AndroidConfig(
distanceFilterMeters: 100,
trackingInterval: Duration(minutes: 5),
enableCancelTrackingAction: false,
),
iOSConfig: IOSConfig(
activityType: ActivityType.FITNESS,
distanceFilterMeters: 100,
restartAfterKill: true,
),
),
);,,

my pubspec.yaml
name: ozee_sp
description: An eCommerce mobile app for Ozee project, built with Flutter.

The following line prevents the package from being accidentally published to

pub.dev using pub publish. This is preferred for private packages.

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

The following defines the version and build number for your application.

A version number is three numbers separated by dots, like 1.2.43

followed by an optional build number separated by a +.

Both the version and the builder number may be overridden in flutter

build by specifying --build-name and --build-number, respectively.

In Android, build-name is used as versionName while build-number used as versionCode.

Read more about Android versioning at https://developer.android.com/studio/publish/versioning

In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.

Read more about iOS versioning at

https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html

version: 1.0.4+22

environment:
sdk: '>=2.12.0 <3.0.0'

dependencies:
auto_size_text: ^3.0.0-nullsafety.0
bot_toast: ^4.0.1
cached_network_image: ^3.0.0
carousel_slider: ^4.0.0
collection: ^1.15.0
cookie_jar: ^3.0.1
dio: ^4.0.4
dio_cookie_manager: ^2.0.0
easy_localization: ^3.0.0
encrypt: ^5.0.0
firebase_analytics: ^10.0.4
flutter:
sdk: flutter
flutter_bug_logger: ^1.0.0
flutter_cache_manager: ^3.3.0
flutter_dotenv: ^5.0.2
flutter_gen: ^5.1.0+1
flutter_html: ^3.0.0-alpha.5
flutter_localizations:
sdk: flutter
flutter_rating_bar: ^4.0.0
flutter_riverpod: ^1.0.2
geocoding: ^2.0.0
google_fonts: ^4.0.4
image_picker: ^1.0.2
in_app_review: ^2.0.2
line_awesome_flutter: ^2.0.0
location: ^5.0.1
package_info: ^2.0.2
percent_indicator: ^4.2.2
app_settings: ^5.0.0
background_location_tracker: ^1.4.0
recapet_events:
git:
url: https://gitlab.zarcony.com/ibrahim.gamal/recapet_events.git
ref: 0.0.1
recapet_firebase:
git:
url: https://gitlab.zarcony.com/ibrahim.gamal/recapet_firebase.git
ref: dev
recapet_widgets:
git:
url: https://gitlab.zarcony.com/ibrahim.gamal/recapet_widgets.git
ref: dev
shared_preferences: ^2.0.6
url_launcher: ^6.1.2
infinite_scroll_pagination: ^3.1.0
connectivity_plus: ^4.0.1
intl: ^0.18.0
dev_dependencies:
build_runner: ^2.3.2
device_preview: ^1.1.0
#To get unused files => flutter pub run dart_code_metrics:metrics check-unused-files lib
dart_code_metrics: ^5.0.1
flutter_gen_runner: ^5.0.3

#To generate app icons => flutter pub run flutter_launcher_icons
flutter_launcher_icons: ^0.13.1

flutter_native_splash: ^2.2.16
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
lint: ^2.1.2

to use flutter gen => flutter packages pub run build_runner build

to delete conflicts => flutter packages pub run build_runner build --delete-conflicting-outputs

flutter_gen:
output: lib/src/gen/
integrations:
flutter_svg: true

For information on the generic Dart part of this file, see the

following page: https://dart.dev/tools/pub/pubspec

flutter_native_splash:
background_image: 'assets/images/splash_background.png'
android: true
ios: true
web: false
android_gravity: center
ios_content_mode: center
flutter_icons:
android: false
ios: true
image_path: "assets/launcher/ozee_sp_logo.png"
adaptive_icon_background: "#000000"

The following section is specific to Flutter.

flutter:

The following line ensures that the Material Icons font is

included with your application, so that you can use the icons in

the material Icons class.

uses-material-design: true

To add assets to your application, add an assets section, like this:

assets:
- .env
- assets/images/
- assets/images/1.0x/
- assets/images/2.0x/
- assets/images/3.0x/
- assets/images/4.0x/
- assets/launcher/
- assets/i18n/
- lib/src/widgets/timezone-0.9.0/lib/data/

, my build gradle android
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

def keyStoreProperties = new Properties()
def keyStorePropertiesFile = rootProject.file('key.properties')
if (keyStorePropertiesFile.exists()) {
keyStoreProperties.load(new FileInputStream(keyStorePropertiesFile))
}

def debugKeystoreProperties = new Properties()
def debugKeystorePropertiesFile = rootProject.file('debug-key.properties')
if (debugKeystorePropertiesFile.exists()) {
debugKeystoreProperties.load(new FileInputStream(debugKeystorePropertiesFile))
}

android {
compileSdkVersion 33

sourceSets {
    main.java.srcDirs += 'src/main/kotlin'
}

lintOptions {
    disable 'InvalidPackage'
}

defaultConfig {
    // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    applicationId "sp.ozeesalon.com"
    minSdkVersion 23
    targetSdkVersion 33
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}

signingConfigs {
    debug {
        keyAlias debugKeystoreProperties['keyAlias']
        keyPassword debugKeystoreProperties['keyPassword']
        storeFile file(debugKeystoreProperties['storeFile'])
        storePassword debugKeystoreProperties['storePassword']
    }
    release {
        keyAlias keyStoreProperties['keyAlias']
        keyPassword keyStoreProperties['keyPassword']
        storeFile file(keyStoreProperties['storeFile'])
        storePassword keyStoreProperties['storePassword']
    }
}

buildTypes {
    release {
        // TODO: Add your own signing config for the release build.
        // Signing with the debug keys for now, so `flutter run --release` works.
         signingConfig signingConfigs.release
    }
}

}

flutter {
source '../..'
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
}

apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.gms.google-services'
,
i hope that be useful

@AhkamKhallaf
Copy link
Author

@vanlooverenkoen there is a simple template
https://github.com/AhkamKhalaaf/example_location/

@AhkamKhallaf
Copy link
Author

Fatal Exception: java.lang.RuntimeException

Unable to create service com.icapps.background_location_tracker.service.LocationUpdatesService: android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service sp.ozeesalon.com/com.icapps.background_location_tracker.service.LocationUpdatesService

Caused by android.app.ForegroundServiceStartNotAllowedException

Service.startForeground() not allowed due to mAllowStartForeground false: service sp.ozeesalon.com/com.icapps.background_location_tracker.service.LocationUpdatesService

android.app.Service.startForeground (Service.java:862)

arrow_right

com.icapps.background_location_tracker.utils.NotificationUtil.startForeground (NotificationUtil.kt:110)

com.icapps.background_location_tracker.service.LocationUpdatesService.startTracking (LocationUpdatesService.kt:176)

com.icapps.background_location_tracker.service.LocationUpdatesService.onCreate (LocationUpdatesService.kt:77)

android.app.ActivityThread.handleCreateService (ActivityThread.java:4651)

@AhkamKhallaf AhkamKhallaf changed the title code that i run it when location is updated , it's repeated many times through one second infinite repeat backgroundCallback when First time only install app for any device android Sep 12, 2023
@AhkamKhallaf
Copy link
Author

AhkamKhallaf commented Sep 12, 2023

The trackingInterval option actually does not work properly In my emulator which is built with A30, A33 and also on Samsung Galaxy S10(SM G977N) too. if the tracking altitude is not required in your project, geoloactor + flutter_background_service could be a good alternative option it has Stream event and you can adjust it's interval by rxDart throttleTime.

oh, it's so important for me , now track interval does not work well for any android device, handleBackgroundUpdated is called for less than one second , when first time run app, but when kill it and run again, it work s fine , any help about this issue @dev07060

@AhkamKhallaf
Copy link
Author

Do you immediately start the tracking or is it after a button press or something (just to make sure you first ask permissions)? For me your config works fine in our example project 🤔

yes, i immediately start tracking , inside initState homeScreen , there is a problem with this
@ikbendewilliam

@AhkamKhallaf
Copy link
Author

Because everything works in the example project, I think in order to help you further we should need a minimal reproducible project. that only contains

  • the bug
  • minimal screens
  • minimal logic
  • no setup

Can you provide us with that.

i try example project but not work with my environment , can you tell me your com.google.gms:google-services or any settings can effect @vanlooverenkoen

@ikbendewilliam
Copy link
Contributor

@AhkamKhalaaf I've checked out your example, thank you for providing this to us.
First I have to say that I don't encounter your issue that the backgroundCallback is repeated. It's only called after the set time interval.
That being said, there are a few issues I see in your code.

In the handleBackgroundUpdated you do (data) async => { This means that you actually return a Set from the method and not execute the content of your method.

More importantly, you don't await the Repo().update(data). Meaning that when you do an async call inside this, the background will be disposed as it won't wait for the process to complete. This is the main issue I think in your code.

You start tracking before you have permission, this is not good. You should first ask the user for permission and then start tracking. I would update your init and checkLocation as follows:

  void initState() {
    super.initState();
    checkLocation();
  }
...
  Future<void> checkLocation() async {
...
    _permissionGranted = await location.hasPermission();
    if (_permissionGranted == PermissionStatus.denied) {
      _permissionGranted = await location.requestPermission();
      if (_permissionGranted != PermissionStatus.granted) {
        return;
      }
    }
    await startTRack();
    _getTrackingStatus();
    _startLocationsUpdatesStream();
    debugPrint('${await BackgroundLocationTrackerManager.isTracking()},,isTrackingisTracking');

With these fixes, it works fine for me on my Android device. Can you fix these issues and see if this resolves your issue?

@AhkamKhallaf
Copy link
Author

> @AhkamKhalaaf I've checked out your example, thank you for providing this to us. First I have to say that I don't encounter your issue that the backgroundCallback is repeated. It's only called after the set time interval. That being said, there are a few issues I see in your code.

In the handleBackgroundUpdated you do (data) async => { This means that you actually return a Set from the method and not execute the content of your method.

More importantly, you don't await the Repo().update(data). Meaning that when you do an async call inside this, the background will be disposed as it won't wait for the process to complete. This is the main issue I think in your code.

You start tracking before you have permission, this is not good. You should first ask the user for permission and then start tracking. I would update your init and checkLocation as follows:

  void initState() {
    super.initState();
    checkLocation();
  }
...
  Future<void> checkLocation() async {
...
    _permissionGranted = await location.hasPermission();
    if (_permissionGranted == PermissionStatus.denied) {
      _permissionGranted = await location.requestPermission();
      if (_permissionGranted != PermissionStatus.granted) {
        return;
      }
    }
    await startTRack();
    _getTrackingStatus();
    _startLocationsUpdatesStream();
    debugPrint('${await BackgroundLocationTrackerManager.isTracking()},,isTrackingisTracking');

With these fixes, it works fine for me on my Android device. Can you fix these issues and see if this resolves your issue?

@ikbendewilliam thank you very much for your response,

i follow all your notes and push them to repo
, but the issue is still , handleBackgroundUpdated is repeated every second , as you show the location is the same

Uploading Screenshot_20230914_113907.png…

@AhkamKhallaf
Copy link
Author

i use in
android/build.gradle classpath 'com.android.tools.build:gradle:7.1.2'
in android/gradle/wrapper/gradle-wrapper.properties distributionUrl=https://services.gradle.org/distributions/gradle-7.4-all.zip
what about you ?@ikbendewilliam

@ikbendewilliam
Copy link
Contributor

Hi @AhkamKhalaaf the gradle version depends on your local installation and shouldn't matter unless you encounter issues with this. I don't fully know what the issue can be in your case as it is working on our end. Have you tried different devices? And does it work on iOS?

@AhkamKhallaf
Copy link
Author

Hi @AhkamKhalaaf the gradle version depends on your local installation and shouldn't matter unless you encounter issues with this. I don't fully know what the issue can be in your case as it is working on our end. Have you tried different devices? And does it work on iOS?

yes , it works with ios platform , yes i try with diffirent emulators and read device

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants