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

We cannot maintain the class objects, when application was terminated. #48

Closed
saravananmnm opened this issue Sep 15, 2020 · 24 comments
Closed
Labels
question from customer Question from the customer on how to use the packages waiting for customer response Waiting on customer response

Comments

@saravananmnm
Copy link

saravananmnm commented Sep 15, 2020

[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: NoSuchMethodError: The method 'getAllOrders' was called on null.
E/flutter ( 2544): Receiver: null
E/flutter ( 2544): Tried calling: getAllOrders("STY10")
E/flutter ( 2544): #      Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
E/flutter ( 2544): #      backgroundFetchHeadlessTask (package:courier_alliance/src/service/backgroundservice.dart:25:40)
E/flutter ( 2544): <asynchronous suspension>
E/flutter ( 2544): #      _headlessCallbackDispatcher.<anonymous closure> (package:background_fetch/background_fetch.dart:619:15)
E/flutter ( 2544): #      MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:432:55)
E/flutter ( 2544): #      MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:385:33)
E/flutter ( 2544): #      _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binding.dart:267:33)
E/flutter ( 2544): #      _invoke3.<anonymous closure> (dart:ui/hooks.dart:282:15)
E/flutter ( 2544): #      _rootRun (dart:async/zone.dart:1190:13)
E/flutter ( 2544): #      _CustomZone.run (dart:async/zone.dart:1093:19)
E/flutter ( 2544): #      _CustomZone.runGuarded (dart:async/zone.dart:997:7)
E/flutter ( 2544): #     _invoke3 (dart:ui/hooks.dart:281:10)
E/flutter ( 2544): #     _dispatchPlatformMessage (dart:ui/hooks.dart:156:5)
@saravananmnm saravananmnm changed the title We cannot maintain the class objects, when application is terminated. We cannot maintain the class objects, when application was terminated. Sep 15, 2020
@vanlooverenkoen
Copy link
Collaborator

vanlooverenkoen commented Sep 15, 2020

Can you provide me a reproducible example? You are calling a method getAllOrders on a null object? Where is this object coming from? Why is it null?

Did you init the container in the background as well?

@vanlooverenkoen vanlooverenkoen added waiting for customer response Waiting on customer response more info required We don't have enough info to get started with this issue. labels Sep 15, 2020
@saravananmnm
Copy link
Author

saravananmnm commented Sep 15, 2020

class BackgroundService {
  CollectionDeliveryRepository repository;
  SaveOrdersDetailsDAO saveOrdersDetailsDAO;
  OrdersMasterDAO ordersMasterDAO;

  BackgroundService(
      this.repository, this.saveOrdersDetailsDAO, this.ordersMasterDAO) {
    configure();
  }

i have injected from class constructor through kiwi .

void backgroundFetchHeadlessTask(String taskId) async {
  CollectionDeliveryRepository repository;
  SaveOrdersDetailsDAO saveOrdersDetailsDAO;
  OrdersMasterDAO ordersMasterDAO;
  print("[BackgroundFetch onTerminate] Headless event received: $taskId");
  DateTime timestamp = DateTime.now();
  // Read fetch_events from SharedPreferences
  // Add new event.
  if (await Utils.onCheckNetworkConnection()) {
    var orders = await ordersMasterDAO.getAllOrders(StringConstant.POD);
    var syncData = await saveOrdersDetailsDAO.getAllOrderDetails(orders);
    var savedOrders = await getSavedOrderDetails(repository, syncData, orders);
    pushAllSavedOrders(
        savedOrders, repository, ordersMasterDAO, saveOrdersDetailsDAO);
  }
  BackgroundFetch.finish(taskId);
}

but inside top-level function is not getting those objects.

@saravananmnm
Copy link
Author

container.registerSingleton((c) => BackgroundService(container<CollectionDeliveryRepository>(),container<SaveOrdersDetailsDAO>(),container<OrdersMasterDAO>()));

@saravananmnm
Copy link
Author

any solutions about this issue?

@vanlooverenkoen
Copy link
Collaborator

vanlooverenkoen commented Oct 24, 2020

Oh I'm sorry I completely forgot. Today I have time to investigate.

@vanlooverenkoen vanlooverenkoen removed the waiting for customer response Waiting on customer response label Oct 24, 2020
@vanlooverenkoen
Copy link
Collaborator

@saravananmnm can you provide me with a small working example? That I can run that has this specific issue? Meanwhile I am looking at https://pub.dev/packages/background_fetch how it works and trying to create a working example for myself

@vanlooverenkoen vanlooverenkoen added the waiting for customer response Waiting on customer response label Oct 24, 2020
@saravananmnm
Copy link
Author

saravananmnm commented Oct 24, 2020

2020-10-24 14:06:53.466 29236-29266/com.fluttersamples E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: KiwiError:
    
    
    Failed to resolve `BGReposotory`:
    
    The type `BGReposotory` was not registered
    
    Make sure `BGReposotory` is added to your KiwiContainer and rerun build_runner build
    (If you are using the kiwi_generator)
    
    When using Flutter, most of the time a hot restart is required to setup the KiwiContainer again.
    
    
    
    #     KiwiContainer.resolve (package:kiwi/src/kiwi_container.dart:94:7)
    #     KiwiContainer.call (package:kiwi/src/kiwi_container.dart:126:31)
    #     backgroundHeadlessTask (package:fluttersamples/backgroundservices.dart:14:25)
    #     _headlessCallbackDispatcher.<anonymous closure> (package:background_fetch/background_fetch.dart:619:15)
    #     MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:432:55)
    #     MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:385:33)
    #     _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binding.dart:267:33)
    #     _invoke3.<anonymous closure> (dart:ui/hooks.dart:282:15)
    #     _rootRun (dart:async/zone.dart:1190:13)
    #     _CustomZone.run (dart:async/zone.dart:1093:19)
    #     _CustomZone.runGuarded (dart:async/zone.dart:997:7)
    #     _invoke3 (dart:ui/hooks.dart:281:10)
    #    _dispatchPlatformMessage (dart:ui/hooks.dart:156:5)
2020-10-24 14:07:27.986 29236-29236/com.fluttersamples D/TSBackgroundFetch: - Background Fetch event received
2020-10-24 14:07:27.986 29236-29236/com.fluttersamples D/TSBackgroundFetch: 💀 [HeadlessTask flutter_background_fetch]
2020-10-24 14:07:27.991 29236-29266/com.fluttersamples E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: KiwiError:

@saravananmnm
Copy link
Author

saravananmnm commented Oct 24, 2020

import 'dart:async';

import 'package:background_fetch/background_fetch.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttersamples/repository/backgroundrepository.dart';
import 'package:geolocator/geolocator.dart';
import 'package:kiwi/kiwi.dart';

/// This "Headless Task" is run when app is terminated.
Future<void> backgroundHeadlessTask(String taskId) async {
  BGReposotory reposotory;
  var container = new KiwiContainer();
  reposotory = container<BGReposotory>();
  DateTime timestamp = DateTime.now();
  print("[BackgroundFetch onTerminate] Headless event received: $timestamp");
  var position = await Geolocator.getCurrentPosition(
      forceAndroidLocationManager: true,
      desiredAccuracy: LocationAccuracy.high);
  print(position.toString());
  int res= await reposotory.updateDriverStatus(position);
  if (res != null && res == 1)
    print('success');
  else
    print('failure');
  BackgroundFetch.finish(taskId);
}

class MyBGApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyBGApp> {
  bool _enabled = true;
  int _status = 0;
  List<String> _events = [];
  BGReposotory reposotory;

  _MyAppState() {
    var container = new KiwiContainer();
    reposotory = container<BGReposotory>();
  }

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    // Configure BackgroundFetch.
    BackgroundFetch.configure(
            BackgroundFetchConfig(
              minimumFetchInterval: 1,
              forceAlarmManager: true,
              stopOnTerminate: false,
              startOnBoot: true,
              enableHeadless: true,
              requiresBatteryNotLow: false,
              requiresCharging: false,
              requiresStorageNotLow: false,
              requiresDeviceIdle: false,
              requiredNetworkType: NetworkType.ANY,
            ),
            _onBackgroundFetch)
        .then((int status) {
      print('[BackgroundFetch] configure success: $status');
      setState(() {
        _status = status;
      });
    }).catchError((e) {
      print('[BackgroundFetch] configure ERROR: $e');
      setState(() {
        _status = e;
      });
    });

    // Schedule a "one-shot" custom-task in 10000ms.
    // These are fairly reliable on Android (particularly with forceAlarmManager) but not iOS,
    // where device must be powered (and delay will be throttled by the OS).
    BackgroundFetch.scheduleTask(TaskConfig(
        taskId: "Saro.Background",
        delay: 1000,
        periodic: true,
        forceAlarmManager: false,
        stopOnTerminate: false,
        enableHeadless: true));

    // Optionally query the current BackgroundFetch status.
    int status = await BackgroundFetch.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;
  }

  void _onBackgroundFetch(String taskId) async {
    DateTime timestamp = new DateTime.now();
    // This is the fetch-event callback.
    print("[BackgroundFetch onForeground] Event received: $taskId $timestamp");
    var position = await Geolocator.getCurrentPosition(
        forceAndroidLocationManager: true,
        desiredAccuracy: LocationAccuracy.high);
   int res= await reposotory.updateDriverStatus(position);
    if (res != null && res == 1)
      print('success');
    else
      print('failure');
    // IMPORTANT:  You must signal completion of your fetch task or the OS can punish your app
    // for taking too long in the background.
    BackgroundFetch.finish(taskId);
  }

  void _onClickEnable(enabled) {
    setState(() {
      _enabled = enabled;
    });
    if (enabled) {
      BackgroundFetch.start().then((int status) {
        print('[BackgroundFetch] start success: $status');
      }).catchError((e) {
        print('[BackgroundFetch] start FAILURE: $e');
      });
    } else {
      BackgroundFetch.stop().then((int status) {
        print('[BackgroundFetch] stop success: $status');
      });
    }
  }

  void _onClickStatus() async {
    int status = await BackgroundFetch.status;
    print('[BackgroundFetch] status: $status');
    setState(() {
      _status = status;
    });
  }

  void _onClickClear() async {
    setState(() {
      _events = [];
    });
  }

  @override
  Widget build(BuildContext context) {
    const EMPTY_TEXT = Center(
        child: Text(
            'Waiting for fetch events.  Simulate one.\n [Android] \$ ./scripts/simulate-fetch\n [iOS] XCode->Debug->Simulate Background Fetch'));

    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
            title: const Text('BackgroundFetch Example',
                style: TextStyle(color: Colors.black)),
            backgroundColor: Colors.amberAccent,
            brightness: Brightness.light,
            actions: <Widget>[
              Switch(value: _enabled, onChanged: _onClickEnable),
            ]),
        body: (_events.isEmpty)
            ? EMPTY_TEXT
            : Container(
                child: new ListView.builder(
                    itemCount: _events.length,
                    itemBuilder: (BuildContext context, int index) {
                      List<String> event = _events[index].split("@");
                      return InputDecorator(
                          decoration: InputDecoration(
                              contentPadding: EdgeInsets.only(
                                  left: 5.0, top: 5.0, bottom: 5.0),
                              labelStyle:
                                  TextStyle(color: Colors.blue, fontSize: 20.0),
                              labelText: "[${event[0].toString()}]"),
                          child: new Text(event[1],
                              style: TextStyle(
                                  color: Colors.black, fontSize: 16.0)));
                    }),
              ),
        bottomNavigationBar: BottomAppBar(
            child: Container(
                padding: EdgeInsets.only(left: 5.0, right: 5.0),
                child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      RaisedButton(
                          onPressed: _onClickStatus,
                          child: Text('Status: $_status')),
                      RaisedButton(
                          onPressed: _onClickClear, child: Text('Clear'))
                    ]))),
      ),
    );
  }
}
```

@vanlooverenkoen
Copy link
Collaborator

vanlooverenkoen commented Oct 24, 2020

@saravananmnm I tried the following and everything was working for me.

I assume this problem is only present on Andriod? Because on iOS it is not possible to fetch when the app is terminated

EXAMPLE
import 'dart:math';

import 'package:background_fetch/background_fetch.dart';
import 'package:flutter/material.dart';
import 'package:kiwi/kiwi.dart';
import 'package:pp_local_notifications/local_notifications.dart';

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// MAIN DART FILE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await setupDependencyTree();
  BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
  runApp(MyApp());
  Future.delayed(Duration(seconds: 10)).then((value) {
    delayedPrint();
  });
}

backgroundFetchHeadlessTask(String taskId) {
  sendNotification(
    title: "backgroundFetchHeadlessTask",
    content: "${KiwiContainer().resolve<RepoA>().id}\n"
        "${KiwiContainer().resolve<RepoB>().id}\n"
        "${KiwiContainer().resolve<RepoC>().id}\n"
        "${KiwiContainer().resolve<RepoD>().id}\n"
        "${KiwiContainer().resolve<RepoABC>().id}\n",
  );
  print(KiwiContainer().resolve<RepoA>().id);
  print(KiwiContainer().resolve<RepoB>().id);
  print(KiwiContainer().resolve<RepoC>().id);
  print(KiwiContainer().resolve<RepoD>().id);
  print(KiwiContainer().resolve<RepoABC>().id);

  BackgroundFetch.finish(taskId);
}

delayedPrint() {
  sendNotification(
    title: "delayedPrint",
    content: "${KiwiContainer().resolve<RepoA>().id}\n"
        "${KiwiContainer().resolve<RepoB>().id}\n"
        "${KiwiContainer().resolve<RepoC>().id}\n"
        "${KiwiContainer().resolve<RepoD>().id}\n"
        "${KiwiContainer().resolve<RepoABC>().id}\n",
  );
  print(KiwiContainer().resolve<RepoA>().id);
  print(KiwiContainer().resolve<RepoB>().id);
  print(KiwiContainer().resolve<RepoC>().id);
  print(KiwiContainer().resolve<RepoD>().id);
  print(KiwiContainer().resolve<RepoABC>().id);
}

Future<void> sendNotification({@required String title, @required String content}) async {
  const AndroidNotificationChannel channel = const AndroidNotificationChannel(
    id: 'default_notification',
    name: 'Default',
    description: 'Grant this app the ability to show notifications',
    importance: AndroidNotificationChannelImportance.HIGH,
  );

  await LocalNotifications.createAndroidNotificationChannel(channel: channel);

  LocalNotifications.createNotification(
    title: title,
    ticker: content,
    content: content,
    id: Random().nextInt(54468),
    androidSettings: AndroidSettings(channel: channel),
  );
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// INJECTOR
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
abstract class Injector {
  @Register.singleton(RepoA)
  @Register.singleton(RepoB)
  @Register.singleton(RepoC)
  @Register.singleton(RepoD)
  @Register.singleton(RepoABC)
  void register();
}

Future<void> setupDependencyTree() async => _$Injector().register();

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// REPOSITORIES
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class RepoA {
  String id;

  RepoA() {
    id = Random().nextInt(545654465).toString();
  }

  String getStringFromA() => 'A';
}

class RepoB {
  String id;

  RepoB() {
    id = Random().nextInt(545654465).toString();
  }

  String getStringFromB() => 'B';
}

class RepoC {
  String id;

  RepoC() {
    id = Random().nextInt(545654465).toString();
  }

  String getStringFromC() => 'A';
}

class RepoD {
  String id;

  RepoD() {
    id = Random().nextInt(545654465).toString();
  }

  String getStringFromD() => 'A';
}

class RepoABC {
  final RepoA _repoA;
  final RepoB _repoB;
  final RepoC _repoC;
  String id;

  RepoABC(this._repoA, this._repoB, this._repoC){
    id = Random().nextInt(545654465).toString();
  }

  String getString() => '${_repoA.getStringFromA()}${_repoB.getStringFromB()}${_repoC.getStringFromC()}';
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// MY APP
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomeScreen(),
    );
  }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// Home Screen
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int _status = 0;
  List<DateTime> _events = [];

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Column(
        children: [
          Text(KiwiContainer().resolve<RepoA>().id),
          Text(KiwiContainer().resolve<RepoB>().id),
          Text(KiwiContainer().resolve<RepoC>().id),
          Text(KiwiContainer().resolve<RepoD>().id),
          Text(KiwiContainer().resolve<RepoABC>().id),
          Expanded(
            child: Container(
              color: Colors.grey.shade300,
              child: ListView.builder(
                itemCount: _events.length,
                itemBuilder: (BuildContext context, int index) {
                  DateTime timestamp = _events[index];
                  return InputDecorator(
                    decoration: InputDecoration(
                        contentPadding: EdgeInsets.only(left: 10.0, top: 10.0, bottom: 0.0),
                        labelStyle: TextStyle(color: Colors.amberAccent, fontSize: 20.0),
                        labelText: "[background fetch event]"),
                    child: Text(
                      timestamp.toString(),
                      style: TextStyle(color: Colors.white, fontSize: 16.0),
                    ),
                  );
                },
              ),
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: onAddTask,
      ),
    );
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    // Configure BackgroundFetch.
    BackgroundFetch.configure(
        BackgroundFetchConfig(
          stopOnTerminate: false,
          enableHeadless: true,
          minimumFetchInterval: 1,
        ), (String taskId) async {
      // This is the fetch-event callback.
      print("[BackgroundFetch] Event received $taskId");
      setState(() {
        _events.insert(0, new DateTime.now());
      });
      // 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);
    }).then((int status) {
      print('[BackgroundFetch] configure success: $status');
      setState(() {
        _status = status;
      });
    }).catchError((e) {
      print('[BackgroundFetch] configure ERROR: $e');
      setState(() {
        _status = e;
      });
    });

    // Optionally query the current BackgroundFetch status.
    int status = await BackgroundFetch.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;
  }

  Future<void> onAddTask() async {
    sendNotification(
      title: "onAddTask",
      content: "${KiwiContainer().resolve<RepoA>().id}\n"
          "${KiwiContainer().resolve<RepoB>().id}\n"
          "${KiwiContainer().resolve<RepoC>().id}\n"
          "${KiwiContainer().resolve<RepoD>().id}\n"
          "${KiwiContainer().resolve<RepoABC>().id}\n",
    );
    BackgroundFetch.scheduleTask(TaskConfig(
      taskId: 'com.example.customtask',
      delay: 60 * 1000,
      enableHeadless: true,
      stopOnTerminate: false,
      periodic: true,
    ));
  }
}

@vanlooverenkoen vanlooverenkoen removed the waiting for customer response Waiting on customer response label Oct 24, 2020
@saravananmnm
Copy link
Author

saravananmnm commented Oct 24, 2020

C:\Saravanan\Flutter_Sdk\flutter\bin\flutter.bat doctor --verbose
[√] Flutter (Channel stable, 1.20.4, on Microsoft Windows [Version 10.0.18362.1082], locale en-IN)
    • Flutter version 1.20.4 at C:\Saravanan\Flutter_Sdk\flutter
    • Framework revision fba99f6cf9 (6 weeks ago), 2020-09-14 15:32:52 -0700
    • Engine revision d1bc06f032
    • Dart version 2.9.2

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
    • Android SDK at C:\Users\Admin\AppData\Local\Android\sdk
    • Platform android-30, build-tools 30.0.0
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Android Studio (version 4.0)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin version 50.0.1
    • Dart plugin version 193.7547
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] Connected device (1 available)
    • Android SDK built for x86 (mobile) • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)

• No issues found!
Process finished with exit code 0

@vanlooverenkoen
Copy link
Collaborator

vanlooverenkoen commented Oct 24, 2020

import 'dart:async';

import 'package:background_fetch/background_fetch.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttersamples/repository/backgroundrepository.dart';
import 'package:geolocator/geolocator.dart';
import 'package:kiwi/kiwi.dart';

/// This "Headless Task" is run when app is terminated.
Future<void> backgroundHeadlessTask(String taskId) async {
  BGReposotory reposotory;
  var container = new KiwiContainer();
  reposotory = container<BGReposotory>();
  DateTime timestamp = DateTime.now();
  print("[BackgroundFetch onTerminate] Headless event received: $timestamp");
  var position = await Geolocator.getCurrentPosition(
      forceAndroidLocationManager: true,
      desiredAccuracy: LocationAccuracy.high);
  print(position.toString());
  int res= await reposotory.updateDriverStatus(position);
  if (res != null && res == 1)
    print('success');
  else
    print('failure');
  BackgroundFetch.finish(taskId);
}

class MyBGApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyBGApp> {
  bool _enabled = true;
  int _status = 0;
  List<String> _events = [];
  BGReposotory reposotory;

  _MyAppState() {
    var container = new KiwiContainer();
    reposotory = container<BGReposotory>();
  }

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    // Configure BackgroundFetch.
    BackgroundFetch.configure(
            BackgroundFetchConfig(
              minimumFetchInterval: 1,
              forceAlarmManager: true,
              stopOnTerminate: false,
              startOnBoot: true,
              enableHeadless: true,
              requiresBatteryNotLow: false,
              requiresCharging: false,
              requiresStorageNotLow: false,
              requiresDeviceIdle: false,
              requiredNetworkType: NetworkType.ANY,
            ),
            _onBackgroundFetch)
        .then((int status) {
      print('[BackgroundFetch] configure success: $status');
      setState(() {
        _status = status;
      });
    }).catchError((e) {
      print('[BackgroundFetch] configure ERROR: $e');
      setState(() {
        _status = e;
      });
    });

    // Schedule a "one-shot" custom-task in 10000ms.
    // These are fairly reliable on Android (particularly with forceAlarmManager) but not iOS,
    // where device must be powered (and delay will be throttled by the OS).
    BackgroundFetch.scheduleTask(TaskConfig(
        taskId: "Saro.Background",
        delay: 1000,
        periodic: true,
        forceAlarmManager: false,
        stopOnTerminate: false,
        enableHeadless: true));

    // Optionally query the current BackgroundFetch status.
    int status = await BackgroundFetch.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;
  }

  void _onBackgroundFetch(String taskId) async {
    DateTime timestamp = new DateTime.now();
    // This is the fetch-event callback.
    print("[BackgroundFetch onForeground] Event received: $taskId $timestamp");
    var position = await Geolocator.getCurrentPosition(
        forceAndroidLocationManager: true,
        desiredAccuracy: LocationAccuracy.high);
   int res= await reposotory.updateDriverStatus(position);
    if (res != null && res == 1)
      print('success');
    else
      print('failure');
    // IMPORTANT:  You must signal completion of your fetch task or the OS can punish your app
    // for taking too long in the background.
    BackgroundFetch.finish(taskId);
  }

  void _onClickEnable(enabled) {
    setState(() {
      _enabled = enabled;
    });
    if (enabled) {
      BackgroundFetch.start().then((int status) {
        print('[BackgroundFetch] start success: $status');
      }).catchError((e) {
        print('[BackgroundFetch] start FAILURE: $e');
      });
    } else {
      BackgroundFetch.stop().then((int status) {
        print('[BackgroundFetch] stop success: $status');
      });
    }
  }

  void _onClickStatus() async {
    int status = await BackgroundFetch.status;
    print('[BackgroundFetch] status: $status');
    setState(() {
      _status = status;
    });
  }

  void _onClickClear() async {
    setState(() {
      _events = [];
    });
  }

  @override
  Widget build(BuildContext context) {
    const EMPTY_TEXT = Center(
        child: Text(
            'Waiting for fetch events.  Simulate one.\n [Android] \$ ./scripts/simulate-fetch\n [iOS] XCode->Debug->Simulate Background Fetch'));

    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
            title: const Text('BackgroundFetch Example',
                style: TextStyle(color: Colors.black)),
            backgroundColor: Colors.amberAccent,
            brightness: Brightness.light,
            actions: <Widget>[
              Switch(value: _enabled, onChanged: _onClickEnable),
            ]),
        body: (_events.isEmpty)
            ? EMPTY_TEXT
            : Container(
                child: new ListView.builder(
                    itemCount: _events.length,
                    itemBuilder: (BuildContext context, int index) {
                      List<String> event = _events[index].split("@");
                      return InputDecorator(
                          decoration: InputDecoration(
                              contentPadding: EdgeInsets.only(
                                  left: 5.0, top: 5.0, bottom: 5.0),
                              labelStyle:
                                  TextStyle(color: Colors.blue, fontSize: 20.0),
                              labelText: "[${event[0].toString()}]"),
                          child: new Text(event[1],
                              style: TextStyle(
                                  color: Colors.black, fontSize: 16.0)));
                    }),
              ),
        bottomNavigationBar: BottomAppBar(
            child: Container(
                padding: EdgeInsets.only(left: 5.0, right: 5.0),
                child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      RaisedButton(
                          onPressed: _onClickStatus,
                          child: Text('Status: $_status')),
                      RaisedButton(
                          onPressed: _onClickClear, child: Text('Clear'))
                    ]))),
      ),
    );
  }
}

@saravananmnm Your example does not include the main function. And I don't see a KiwiContainer().register(BGRepository)

I need an example that I can copy and past in my editor run it and it should work just like that. So remove the geolocation as well that is not related to this problem. So create a minimal example

@vanlooverenkoen vanlooverenkoen added the waiting for customer response Waiting on customer response label Oct 24, 2020
@saravananmnm
Copy link
Author

saravananmnm commented Oct 24, 2020

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  diConfig();
  BackgroundFetch.registerHeadlessTask(backgroundHeadlessTask);
  runApp(MyMainApp());
}

class MyMainApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Colors.blue,
      ),
      home: MyBGApp(),
    );
  }
}

@vanlooverenkoen
Copy link
Collaborator

diConfig is not yet available

@saravananmnm
Copy link
Author

saravananmnm commented Oct 24, 2020

import 'dart:io';

import 'package:fluttersamples/apiinterface/apiinterface.dart';
import 'package:fluttersamples/constants/constants.dart';
import 'package:fluttersamples/dto/driverstatusdto.dart';
import 'package:fluttersamples/dto/logindto.dart';
import 'package:fluttersamples/repository/backgroundrepository.dart';
import 'package:http/http.dart';
import 'package:http/io_client.dart';
import 'package:jaguar_retrofit/jaguar_retrofit.dart';
import 'package:jaguar_serializer/jaguar_serializer.dart';
import 'package:kiwi/kiwi.dart';

final KiwiContainer container = new KiwiContainer();

diConfig() {
  jaguarRetrofit();
}

jaguarRetrofit() {
  container.registerSingleton((c) => globalClient = IOClient());
  container.registerSingleton((c) => JsonRepo()
    ..add(new LoginModelSerializer())
    ..add(new DriverStatusModelSerializer()));

  container.registerSingleton((c) => ApiInterface(
          Route(Constants.DEV_URL).withClient(
        RestyIOClient(
          HttpClient()
            ..connectionTimeout = const Duration(minutes: 5)
            ..idleTimeout = const Duration(seconds: 60)
            ..userAgent = 'CA Driver',
        )..timeout = Duration(minutes: 5),
      )
            ..before((req) async {
              req.header('timezone', "Asia/Kolkata");
              print(req.getUrl);
              print(req.getHeaders);
              if (req is RouteWithBody) {
                print(req.getBody());
              }
            })
            ..after((res) {
              print(res.request.url);
              print(res.statusCode);
              print(res.body);
            }))
        ..jsonConverter = c<JsonRepo>());

  container.registerSingleton((c) => BGReposotory(container<ApiInterface>()));
}


class RestyIOClient extends IOClient {
  Duration timeout;

  RestyIOClient([inner]) : super(inner);

  @override
  Future<IOStreamedResponse> send(BaseRequest request) async {
    return await timeout != null
        ? super.send(request).timeout(timeout)
        : super.send(request);
  }
}

@vanlooverenkoen
Copy link
Collaborator

Alright thanks. I just got the same error in my example as well after taking a look at your full example. I am first taking a deeper look into headless dart code. How it exactly works.

@vanlooverenkoen vanlooverenkoen removed the waiting for customer response Waiting on customer response label Oct 24, 2020
@saravananmnm
Copy link
Author

Alright @vanlooverenkoen fine. Let me know your solutions.

@vanlooverenkoen
Copy link
Collaborator

@saravananmnm 1 more question are you having this problem only on Android? Because iOS won't run when the app is terminated? Or on both platforms?

@saravananmnm
Copy link
Author

@vanlooverenkoen Am faced this issue from both plarforms.

@vanlooverenkoen
Copy link
Collaborator

And the app is completely terminated? Removed from recents. Or is the app just in the background?

@saravananmnm
Copy link
Author

And the app is completely terminated? Removed from recents. Or is the app just in the background?

Yes @vanlooverenkoen. App is terminated from recent activities.

@vanlooverenkoen
Copy link
Collaborator

transistorsoft/flutter_background_fetch#32 (comment)

How is it possible that you can run a headless implemation on iOS? That is not supported by iOS itself.

@vanlooverenkoen
Copy link
Collaborator

vanlooverenkoen commented Oct 24, 2020

Regarding the Android problem this is because android will release these objects. Only the callback can bu used to trigger dart code. The sollution that works is to call your diConfig() in you too level function.

This wil offcourse result in new objects but. But the kiwi container wil have all the objects you registered. As far as I can see there is no way to keep these the objects in the KiwiContainer. So calling diConfig() again is the way to go.

@vanlooverenkoen vanlooverenkoen added waiting for customer response Waiting on customer response question from customer Question from the customer on how to use the packages and removed more info required We don't have enough info to get started with this issue. labels Oct 24, 2020
@saravananmnm
Copy link
Author

Thanks for the solution @vanlooverenkoen.It's save my day.

@vanlooverenkoen
Copy link
Collaborator

@saravananmnm no problem. Sorry it took so long.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question from customer Question from the customer on how to use the packages waiting for customer response Waiting on customer response
Projects
None yet
Development

No branches or pull requests

2 participants