flutter_background_fetch icon indicating copy to clipboard operation
flutter_background_fetch copied to clipboard

[BUG] http get inside backgroundFetchHeadlessTask not working

Open lambertofredricknbb opened this issue 3 years ago • 17 comments

Your Environment

  • Plugin version: background_fetch: ^1.1.0
  • Platform: Android
  • OS version: Android 12
  • Device manufacturer / model: PIXEL 3A XL / sdk_gphone64_arm64
  • Flutter info (flutter info, flutter doctor): [✓] Flutter (Channel stable, 2.10.3, on macOS 12.3.1 21E258 darwin-arm, locale en-ID) [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc1) [✓] Xcode - develop for iOS and macOS (Xcode 13.3) [✓] Chrome - develop for the web [✓] Android Studio (version 2021.1) [✓] VS Code (version 1.66.0) [✓] Connected device (3 available) [✓] HTTP Host Availability

• No issues found!

  • Plugin config
  1. main.dart
void backgroundFetchHeadlessTask(HeadlessTask task) async {
  String taskId = task.taskId;
  bool isTimeout = task.timeout;
  if (isTimeout) {
    // This task has exceeded its allowed running-time.
    // You must stop what you're doing and immediately .finish(taskId)
    print("[BackgroundFetch] Headless task timed-out: $taskId");
    BackgroundFetch.finish(taskId);
    return;
  }
  print('[BackgroundFetch] Headless event received.');

  // Do your work here...
  String uid;
  String typeid;
  final prefs = await loginLogout.getPref();
  uid = prefs.getString('uid');
  typeid = prefs.getString('typeid');

  String jsonString;
  List<dynamic> dataRespons = [];
  String baseUrl =
      "https://www.mi-one.id/rest_api/ApiMobileNotification/data_notif/${typeid}/${uid}";

  final response = await http.get(Uri.parse(baseUrl));

  if (response.statusCode == 200) {
    final content = jsonDecode(response.body);
    jsonString = content.toString();
    dataRespons = content;
    await FlutterLocalNotificationsPlugin().cancelAll();
    for (var _dataRespons in dataRespons) {
      await NotificationService.display(
          "MI One", "${_dataRespons['project']} - ${_dataRespons['task']}");
    }
    print(jsonString);
  } else {
    print('Failed 1');
  }

  BackgroundFetch.finish(taskId);
}

void main() async {
  WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
  // Register to receive BackgroundFetch events after app is terminated.
  // Requires {stopOnTerminate: false, enableHeadless: true}
  BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
  
}
  1. v_dashboard.dart
Future<void> initPlatformState() async {
    // Configure BackgroundFetch.
    int status = await BackgroundFetch.configure(
        BackgroundFetchConfig(
            minimumFetchInterval: 15,
            forceAlarmManager: true,
            stopOnTerminate: false,
            enableHeadless: true,
            requiresBatteryNotLow: false,
            requiresCharging: false,
            requiresStorageNotLow: false,
            requiresDeviceIdle: false,
            startOnBoot: true,
            requiredNetworkType: NetworkType.NONE), (String taskId) async {
      // <-- Event handler
      // This is the fetch-event callback.
      print("[BackgroundFetch] Event received $taskId");

      String jsonString;
      List<dynamic> dataRespons = [];
      String baseUrl =
          "https://www.mi-one.id/rest_api/ApiMobileNotification/data_notif/${typeid}/${uid}";

      final response = await http.get(Uri.parse(baseUrl));

      if (response.statusCode == 200) {
        final content = jsonDecode(response.body);
        jsonString = content.toString();
        dataRespons = content;
        await FlutterLocalNotificationsPlugin().cancelAll();
        for (var _dataRespons in dataRespons) {
          await NotificationService.display(
              "MI One", "${_dataRespons['project']} - ${_dataRespons['task']}");
        }
        print(jsonString);
      } else {
        print('Failed 2');
      }

      // IMPORTANT:  You must signal completion of your task or the OS can punish your app
      // for taking too long in the background.
      BackgroundFetch.finish(taskId);
    }, (String taskId) async {
      // <-- Task timeout handler.
      // This task has exceeded its allowed running-time.  You must stop what you're doing and immediately .finish(taskId)
      print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
      BackgroundFetch.finish(taskId);
    });
    print('[BackgroundFetch] configure success: $status');
    setState(() {
      _status = status;
    });

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;
  }

Additional context As you can see, in backgroundFetchHeadlessTask I call http get then distribute it to local notification. I use this but http get not running if I terminate the App. But, if i call local notification directly without distribution from http get it's running on background.

lambertofredricknbb avatar Apr 07 '22 12:04 lambertofredricknbb

The plug-in's responsibility ends once your headlessTask is executed.

It is not involved with your http request.

christocracy avatar Apr 07 '22 17:04 christocracy

The plug-in's responsibility ends once your headlessTask is executed.

It is not involved with your http request.

How to involve it?

lambertofredricknbb avatar Apr 08 '22 16:04 lambertofredricknbb

Involve what?

christocracy avatar Apr 08 '22 16:04 christocracy

Involve what?

my http request. If I just put local notification in backgroundFetchHeadlessTask, it will show every 15 minutes in background. But If I put http get and send it to local notification in backgroundFetchHeadlessTask, notification will not show.

lambertofredricknbb avatar Apr 08 '22 16:04 lambertofredricknbb

The plugin causes your supplied background_fetch callback to be run.

It's not involved with any code you implement in your callback. There's nothing the plugin does to make your http request work or not.

christocracy avatar Apr 08 '22 16:04 christocracy

Is the background fetch headless task work in release mode in ios? If yes then how?

neeluagrawal04 avatar May 25 '22 17:05 neeluagrawal04

Is the background fetch headless task work in release mode in ios?

"Headless" is an Android-only concept. There's no such thing as headless for ios.

christocracy avatar May 25 '22 17:05 christocracy

How the background fetch triggers in ios release build or on production like in debug we test via Debug> Simulate background fetch. So in release build is this background fetch triggers automatically?

neeluagrawal04 avatar May 25 '22 17:05 neeluagrawal04

See the README to learn how to simulate events for your app launched via XCode to your device plugged into USB.

It doesn't matter if your app is launched in release.

christocracy avatar May 25 '22 17:05 christocracy

I have uploaded final build to internal tester and launching the app from there on start of app the background fetch should trigger to call the api and some operations in background but found that its not working. So need to confirm the same like when it will triggers the event in release build? Did u tried it in release mode like above for background fetch and doing some heavy operations via it?

neeluagrawal04 avatar May 25 '22 17:05 neeluagrawal04

It can take days before Apple's machine-learning algorithm decides to start firing regular fetch events on a newly installed app.

Apple fires fetch events based upon how often / how long the user interacts with the app. The best way to start seeing fetch events fire is to periodically open the app throughout the day, as a real user would.

Also, if the user terminates the app, iOS stops firing fetch events (there is no such thing as stopOnTerminate: false for iOS.

Did u tried it in release mode like above for background fetch and doing some heavy operations via it?

Yes. All the time.

christocracy avatar May 25 '22 17:05 christocracy

If simulated fetch events fire with simulated events in XCode, that's all you need to know that everything is configured correctly. Everything else is up to iOS.

christocracy avatar May 25 '22 17:05 christocracy

My app is dependent on the background fetch event on which i am doing some background operations which is needed to trigger at app launch and without this my app wont work. So is there any other way to run background task on app launch in flutter for ios?

neeluagrawal04 avatar May 25 '22 17:05 neeluagrawal04

So is there any other way to run background task on app launch in flutter for ios?

No. You are at the mercy of iOS and how often / how long the user interacts with your app. If your user doesn't open the app for a couple of days, iOS stops firing events completely.

christocracy avatar May 25 '22 17:05 christocracy

I have added the event on app open. So you mean to say it wont trigger when that event is fired from app on release build on ios? and it depends on user interaction and at any time it can be triggered?

neeluagrawal04 avatar May 25 '22 17:05 neeluagrawal04

If you want to learn more about this iOS API, watch this video

christocracy avatar May 25 '22 17:05 christocracy

So you mean to say it wont trigger when that event is fired from app on release build on ios?

I don't know why you are confused about release. It doesn't matter. The only thing you need to do is this:

// Configure BackgroundFetch.
    BackgroundFetch.configure(BackgroundFetchConfig(
        minimumFetchInterval: 15        
    ), (String taskId) async {  // <-- Event handler
      // This is the fetch-event callback.
      print("[BackgroundFetch] Event received $taskId");
      await performMyCustomWorkFunction();      
      BackgroundFetch.finish(taskId);
    }, (String taskId) async {  // <-- Task timeout handler.
      // This task has exceeded its allowed running-time.  You must stop what you're doing and immediately .finish(taskId)
      print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
      BackgroundFetch.finish(taskId);
    });

That's it. When iOS decides to fire an event, your callback provided to .configure will be called, regardless of release mode.

If you want to confirm everything is working correctly, boot your app in XCode (in debug or release, doesn't matter) and simulate an event.

Next, you install on a device and wait for iOS to fire events.

christocracy avatar May 25 '22 17:05 christocracy

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Apr 18 '24 01:04 github-actions[bot]

This issue was closed because it has been inactive for 14 days since being marked as stale.

github-actions[bot] avatar May 02 '24 01:05 github-actions[bot]