Scheduling Background Tasks in Flutter Android Apps

In the realm of Android app development, background services play a pivotal role in enabling apps to perform tasks in the background, even when the app is not actively in use. This is essential for features like notifications, data synchronization, and location updates. Flutter, being a cross-platform framework, provides mechanisms to leverage Android’s background services. In this tutorial, we’ll delve into the intricacies of scheduling background tasks in Flutter Android apps, exploring best practices and overcoming potential challenges.

What are Android background services?

An app or service is considered to be running in the background when it meets the following criteria:

  • App activities are not visible to the user.
  • The app is not running any foreground services initiated during visible app activity.

Background services can be categorized into three types:

  • Immediate: These services require immediate execution and should be completed quickly.
  • Long-Running: These services may take a significant amount of time to finish.
  • Deferrable: These services can be delayed and do not need to run immediately.

Additionally, background services can be either persistent or impersistent:

  • Persistent: These services remain scheduled even after the app is restarted or the device is rebooted.
  • Impersistent: These services are no longer scheduled once the process terminates.

The following table provides guidance on the appropriate approach for each type of background service:

Ways for Scheduling Background Tasks in the app

Android and iOS platforms employ distinct approaches for Scheduling Background Tasks . Android offers a variety of methods, including:

  • Geofencing: Triggering actions when a device enters or exits a specified geographic area.
  • Alarm Manager: Scheduling tasks at specific times or intervals.
  • JobService Class: Creating background jobs that can be scheduled and managed.
  • Firebase JobDispatcher: A reliable job scheduling library for Android.
  • WorkManager: A flexible and efficient background task scheduler.

iOS, on the other hand, provides more limited options. The system determines when an app can perform background fetches, aiming to keep the app’s functionality alive. However, there’s no guarantee that the system will ever initiate a background fetch for a particular app.

Let’s delve into each approach in detail.

1. Geofencing

Geofencing involves creating a virtual perimeter around a physical location. This allows apps to detect when a user enters or exits that area. By monitoring these location changes, you can trigger events or notifications in real time.

To implement geofencing in your Flutter app, utilize the flutter_geofence plugin. This versatile plugin provides comprehensive support for geofencing interactions on both Android and iOS platforms.

Run the following command

     flutter pub add flutter_geofence 

This will add the following dependencies to your pubspec.yaml file.

    dependencies:
      flutter_geofence: ^0.4.4

Importing to dart code.

    import 'package:flutter_geofence/Geolocation.dart';
    import 'package:flutter_geofence/geofence.dart';
Example: Using Geofence to get current location

The following command provides you Future that resolves with the current Coordinate of the user. If there is one that’s recent enough (< 60 seconds old), it’ll return to this location.

     Geofence.getCurrentLocation().then((coordinate) {
        print("Your latitude is ${coordinate.latitude} and longitude ${coordinate.longitude}");
    });

2. Alarm Manager

Alarms, such as those used in alarm clocks or calendar events, are a specific type of task that doesn’t fall under the category of background work. Alarm managers are designed to schedule these precise alarms.

When used to schedule background work, alarm managers can wake the device from a deep sleep mode, potentially impacting battery life and overall system health.

The android_alarm_manager_plus Flutter plugin provides access to Android Alarm Manager services, allowing you to execute Dart code in the background when an alarm is triggered.

Run the following command

     flutter pub add android_alarm_manager_plus 

This will add the following dependencies to your pubspec.yaml file.

     dependencies:
      android_alarm_manager_plus: ^2.0.6

Importing to the Dart code.

     import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';

Example:

Here printHello will then run after every minute, even if the main app ends. However, it will not run in the same isolate as the main application. Because it doesn’t share memory and communication between isolates must be done via message passing.

   import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
    
    static void printHello() {
      final DateTime now = DateTime.now();
      final int isolateId = Isolate.current.hashCode;
      print("[$now] Hello, world! isolate=${isolateId} function='$printHello'");
    }
    
    main() async {
      // Be sure to add this line if initialize() call happens before runApp()
      WidgetsFlutterBinding.ensureInitialized();
    
      await AndroidAlarmManager.initialize();
      runApp(...);
      final int helloAlarmID = 0;
      await AndroidAlarmManager.periodic(const Duration(minutes: 1), helloAlarmID, printHello);
    }

3. JobService Class

To schedule a background task, you should define it within a JobService. This service is invoked when the system determines that the task’s conditions are met. JobServices allow your app’s tasks to be executed regardless of whether the app is active or idle.

You can create multiple JobServices, each defining a distinct task, promoting code modularity.

To schedule a JobService, you’ll need to add a declaration to your AndroidManifest.xml file.

     undefined 

It adds permission that will allow the Job Scheduler to call your jobs and be the only one that accesses your JobService.

4. Firebase JobDispatcher

Firebase JobDispatcher was a popular library for scheduling background jobs in Android apps. It offered a JobScheduler-compatible API that functioned on Android devices with API level 9 or higher, provided Google Play Services were installed.

However, with the introduction of the Android Jetpack WorkManager, the Firebase JobDispatcher team decided to deprecate it in favor of focusing entirely on WorkManager. WorkManager provides a more comprehensive and versatile solution for background job scheduling, working with or without Google Play Services, which was a limitation of Firebase JobDispatcher.

WorkManager, a component of the Android Jetpack suite, combines the best features of both Firebase JobDispatcher and JobScheduler. It offers consistent job scheduling services back to API level 14, leveraging JobScheduler on newer devices. This unified approach simplifies background task management for developers.

5. Work Manager

WorkManager is a powerful background processing library for Android that ensures tasks are scheduled and executed reliably, even if not immediately. It allows you to enqueue background tasks, even when your app is not running and the device is rebooted.

The Flutter WorkManager plugin acts as a bridge between Flutter and the underlying background task scheduling mechanisms on Android and iOS. On Android, it wraps the WorkManager API, while on iOS, it utilizes performFetchWithCompletionHandler and BGAppRefreshTask to enable headless execution of Dart code in the background.

Installation

Run the command given below.

     flutter pub add workmanager 

This will add the following line to your pubspec.yaml file.

     dependencies:
      workmanager: ^0.5.0

To import the plugin in the main.dart file adds the line below.

    import 'package:workmanager/workmanager.dart'; 

Example:

The work manager must be initialized before registering any task.

     void callbackDispatcher() {
      Workmanager().executeTask((task, inputData) {
        print("Native called background task: $backgroundTask"); //simpleTask will be emitted here.
        return Future.value(true);
      });
    }
    
    void main() {
      Workmanager().initialize(
        callbackDispatcher, // The top level function, aka callbackDispatcher
        isInDebugMode: true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
      );
      Workmanager().registerOneOffTask("task-identifier", "simpleTask");
      runApp(MyApp());
    }

Here the callbackDispacher needs to be either a static function or a top-level function that can be accessible as a Flutter entry point.

Conclusion

By following these guidelines and leveraging the workmanager plugin, you can effectively schedule and manage background tasks in your Flutter Android app. Remember to prioritize user experience, adhere to Android’s background restrictions, and continuously test and optimize your implementation. With careful planning and execution, you can ensure that your app’s background tasks run smoothly and efficiently, enhancing the overall user experience.

Wanna Level up Your Flutter game? Then check out our ebook The Complete Guide to Flutter Developement where we teach you how to build production grade cross platform apps from scratch.Do check it out to completely Master Flutter framework from basic to advanced level.

Leave a comment