Code Monkey home page Code Monkey logo

Comments (22)

gpibarra avatar gpibarra commented on August 16, 2024 1

@gpibarra I saw your code, and honestly I don't get the point of it.

This is my understanding of your code, correct me if I'm wrong.

  1. You created initCallback and disposeCallback, the init has `initialDataMap
  2. When plugin starts to work you call initCallback and pass it's saved data to flutter app
  3. When plugin stops you call disposeCallback

When plugin start and call "initCallback" dont pass data to flutter app. Data always stays in the class facade inside isolate. Currently, sending data through the port, just for the purpose of refreshing the UI. But it is optional.

What is not clear for me:

  1. How this can help with @RomanJos problem? He just want to use some static variable inside callback, he is not trying to send anything to background service?
  2. When you call BackgroundLocator.registerLocationUpdate it's the point of starting plugin, So why we need separate initCallback? and whats the point of initialDataMap? You don't do any process on it inside plugin, it just get saved and passed back.

Both initCallback and disposeCallback, executed within the isolation process, are to handle the state of the facade class that contains variables necessary for the userand defined by the user

  1. Same question about initCallback applies for disposeCallback. Whats the point of it? When user call BackgroundLocator.unRegisterLocationUpdate() isn't it the dispose?

In the example, a simple counter is implemented, the "count" variable is always in the facade class inside isolate. It is not a static variable that is visible from the mail proccess. The only communication will always be through ports (maybe it can be shared preferences, db or some other method).
If you don't do an initCallback, the counter will continue to add indefinitely even if the user calls BackgroundLocator.unRegisterLocationUpdate(). So when calling BackgroundLocator.registerLocationUpdate() the count variable returns 0.
In this case the dispose function does nothing, but in other cases it can do.
Applied to the @RomanJos problem, when call initCalback, in initData it passes path file to save. In disposeCallback, the file is moved to another directory.
In other cases, for example consuming an api, in initCallback a JWT token is negotiated (with dataInit user and pass). This cannot be done with static variables, if static variables are set in the main process, the first time is executed isolation the statics variables are empty. And in disposeCallback send to the api that has finished capturing locations, for example.

I am unsure if the parameter should send it when the user calls BackgroundLocator.unRegisterLocationUpdate() or when he calls BackgroundLocator.registerLocationUpdate(). Definitely the dispose will be executed when calling BackgroundLocator.unRegisterLocationUpdate() but I finally decided to pass it to the start.
I still doubt about this

Making the isolation process store information in variables and have some business logic, is applied in cases where business logic cannot always be in the main process (communicated with ports) because the main process may not be running and if running the isolation process.

from background_locator.

mehdok avatar mehdok commented on August 16, 2024 1

@gpibarra Sorry for late answer. Your idea is really good and it's working. But I think this is a complicated solution for simple problem. As I said in my previous reply the solution to this problem looks very simple to me and I tested that on Roman example and it's working.

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

And how it is related to plugin or it's isolate?
You are storing the file in a static variable, stoping the plugin or removing Isolate doesn't have any effect on that specific variable.

from background_locator.

RomanJos avatar RomanJos commented on August 16, 2024

But will it not reset ? because I have two value at the same time when I launch the plugin.
If I delete and create another file, only the value from the Flutter side will be updated even though the plugin is stopped. My guess is that the plugin should completely stop and restart like the first time

I don't really know how static work for me its just to make it like public

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

If you share some code I could be more helpfull.
Static variables keep their value as long as the instance exists.

from background_locator.

RomanJos avatar RomanJos commented on August 16, 2024

This is my callback :

static void callback(LocationDto locationDto) async {
    final SendPort send = IsolateNameServer.lookupPortByName(_isolateName);
    send?.send(locationDto);

    print("get file to write");
    File file = await LogPosition.currentFile;
    print("write in ${file.path}");
    List<double> latlng = [locationDto.latitude, locationDto.longitude];
    await file.writeAsString(json.encode(latlng) + '\n', mode: FileMode.append);
  }

You can see here that I get LogPosition.currentFile from this class :

class LogPosition {
  static String _root;
  static File _currentFile;
  static Directory _currentDirectory;
  static Directory _positionHistoryDirectory;

  static Future init() async {
    await (await positionHistoryDirectory).create(recursive: true);
    await (await currentDirectory).create();
  }

  static Future<String> _getRoot() async {
    Directory docDir = await getApplicationDocumentsDirectory();
    _root = docDir.path;
    return _root;
  }

  static Future _getCurrentFile() async {
    print('getcurrentFile');
    Directory _currentDir = await currentDirectory;
    List<FileSystemEntity> _fileList = _currentDir.listSync();
    print(_fileList);
    if (_fileList.isEmpty) return false;

    String _fileName = basename(_fileList.first.path);
    _currentFile = File('${_currentDir.path}/$_fileName');
    return _currentFile;
  }

  static Future<Directory> _getCurrentDirectory() async {
    print('_getCurrentDirectory');
    _currentDirectory = Directory('${await root}/current');
    return _currentDirectory;
  }

  static Future<Directory> _getPositionHistoryDirectory() async {
    print('_getPositionHistoryDirectory');
    _positionHistoryDirectory = Directory('${await root}/history/position');
    return _positionHistoryDirectory;
  }

  static Future<File> createCurrentFile(String mode, DateTime time) async {
    print('createCurrentFile');
    String name = '${(await currentDirectory).path}/$mode^${time.toString()}';
    File _file = File(name);
    return await _file.create();
  }

  static Future save(String uuid) async {
    print('save');
    File _file = await currentFile;
    String name = '${(await positionHistoryDirectory).path}/$uuid';
    await _file.copy(name);
    await cancel();
  }

  static Future cancel() async {
    var t = await (await currentFile).delete();
    print('delete ${t.path}');
  }

  static Future<String> get root async {
    return _root ?? await _getRoot();
  }

  static Future get currentFile async {
    print(_currentFile);
    return _currentFile ?? await _getCurrentFile();
  }

  static Future<Directory> get currentDirectory async {
    return _currentDirectory ?? await _getCurrentDirectory();
  }

  static Future<Directory> get positionHistoryDirectory async {
    return _positionHistoryDirectory ?? await _getPositionHistoryDirectory();
  }

(I know its a little mess lol)

on start I init to create history/position and current directory
before starting the plugin I call createCurrentFile() which will create a file named Activity^2020-04-07 08:24:56.916155 so on restart I can get the Activity type and the date at which it was started (My app is like a activity timer basically that register the position)
the plugin then try to get LogPosition.currentFile which is null so it will list all files inside current which will return Activity^2020-04-07 08:24:56.916155 so here all is good, it write inside and stuff
when its finished I move this file inside history/position and delete it

back to the main screen when I press start It will create the file etc and change LogPosition.currentFile accordingly except the plugin which keep the old LogPosition.currentFile because its not null.

from background_locator.

gpibarra avatar gpibarra commented on August 16, 2024

There are 2 processes running at the same time, background and main app. So there are 2 instances of the LogPosition class, when calling createCurrentFile from main App the instance in background is not noticied.
However, in "get currentFile" it should be null and call to find the file by going through the directory again.
The problem is in "BackgroundLocator.unRegisterLocationUpdate();" it does not remove static variables in the backgroud process. I don't think I can help you with that, I don't quite understand how it handles the background service. Maybe @mehdok can help you.
Perhaps when creating a new file you will tell the background process (through a listening port) that the currentFile has changed.
Look at this example:

import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';

class LogPosition {
  static String _root;
  static File _currentFile;
  static Directory _currentDirectory;
  static Directory _positionHistoryDirectory;
  static const String _isolateReverseName = 'LocatorReverseIsolate';
  static ReceivePort port = ReceivePort();

  static Future init() async {
    await (await positionHistoryDirectory).create(recursive: true);
    await (await currentDirectory).create();
    if (IsolateNameServer.lookupPortByName(_isolateReverseName) != null) {
      IsolateNameServer.removePortNameMapping(_isolateReverseName);
    }
    IsolateNameServer.registerPortWithName(port.sendPort, _isolateReverseName);
    port.listen(
      (dynamic data) async {
        String name = data as String;
        print("New file created save in background $name");
        _currentFile = File(name);
      },
    );
  }

  static Future<String> _getRoot() async {
    Directory docDir = await getApplicationDocumentsDirectory();
    _root = docDir.path;
    return _root;
  }

  static Future<File> _findCurrentFile() async {
    print('findCurrentFile');
    Directory _currentDir = await currentDirectory;
    List<FileSystemEntity> _fileList = _currentDir.listSync();
    print(_fileList);
    if (_fileList.isEmpty) return null;

    String _fileName = basename(_fileList.first.path);
    return File('${_currentDir.path}/$_fileName');
  }

  static Future<Directory> _getCurrentDirectory() async {
    print('_getCurrentDirectory');
    _currentDirectory = Directory('${await root}/current');
    return _currentDirectory;
  }

  static Future<Directory> _getPositionHistoryDirectory() async {
    print('_getPositionHistoryDirectory');
    _positionHistoryDirectory = Directory('${await root}/history/position');
    return _positionHistoryDirectory;
  }

  //--------------------------------------
  // CALL FROM CALLBACK
  static Future<void> writeInCurrentFile(List<double> latlng) async {
    print("get file to write");
    File file = await LogPosition.currentFile;
    print("write in ${file.path}");
    await file.writeAsString(json.encode(latlng) + '\n', mode: FileMode.append);
  }

  //--------------------------------------
  //CALL FROM MAIN APP
  static Future<void> createCurrentFile(String mode, DateTime time) async {
    String name =
        '${(await currentDirectory).path}/$mode-${time.toString().replaceAll(" ", "_").replaceAll(":", "-")}.log';
    print('createCurrentFile $name');
    File _file = File(name);
    await _file.create();
    //Set in main process
    print("New file created save in main: $name");
    _currentFile = _file;
    //Set in background process
    final SendPort send =
        IsolateNameServer.lookupPortByName(_isolateReverseName);
    send?.send(_file.path);
    return;
  }

  static Future save(String uuid) async {
    print('save');
    File _file = await currentFile;
    String name = '${(await positionHistoryDirectory).path}/$uuid';
    //*
    await _file.copy(name);
    // */
    /*
    File _newfile = File(name + ".json");
    await _newfile.create();
    await _newfile.writeAsString('[' + '\n', mode: FileMode.append);
    List<String> lines = await _file.readAsLines();
    for (int l=0 ; l<lines.length; l++) {
      bool isLast = (l==(lines.length-1));
      await _newfile.writeAsString(lines[l] + (isLast?"":",") + '\n', mode: FileMode.append);
    }
    await _newfile.writeAsString(']' + '\n', mode: FileMode.append);
    // */
    await cancel();
  }

  static Future cancel() async {
    var t = await (await currentFile).delete();
    print('delete ${t.path}');
  }

  static Future<String> get root async {
    return _root ?? await _getRoot();
  }

  //GETTERS
  //save(main)-cancel(main)-writeInCurrentFile(background)
  static Future get currentFile async {
    if (_currentFile == null) {
      _currentFile = await _findCurrentFile();
    }
    return _currentFile;
  }

  //init(main)-_findCurrentFile(background)-createCurrentFile(main)
  static Future<Directory> get currentDirectory async {
    return _currentDirectory ?? await _getCurrentDirectory();
  }

  //init(main)-save(main)
  static Future<Directory> get positionHistoryDirectory async {
    return _positionHistoryDirectory ?? await _getPositionHistoryDirectory();
  }
}

from background_locator.

RomanJos avatar RomanJos commented on August 16, 2024

Damn thank you for the example, I re-created this class from scratch because it was more a draft than something but I'll reuse some of your improvements for sure
It currently look like this :

class Back {
  static const String currentPath = '/current.gps';
  static const String statePath = '/state.json';
  static const String histPath = '/history';
  static const String posPath = '$histPath/positions';
  static const String imgPath = '$histPath/images';

  static String current;
  static String state;
  static String root;
  static String posDir;
  static String imgDir;

  static init() async {
    root = (await getApplicationDocumentsDirectory()).path;
    posDir = '$root$posPath';
    imgDir = '$root$imgPath';
    state = '$root$statePath';
    current = '$root$currentPath';

    Directory(posDir).createSync(recursive: true);
    Directory(imgDir).createSync();
    await clean();
  }

  //State file
  static Future<Map> get lastState async {
    String lastState = await File(state).readAsString();
    if (lastState == "") return null;
    return json.decode(lastState);
  }

  static Future<void> newState(String mode, String time) async {
    await File(state).writeAsString(json.encode([mode, time]));
  }

  static Future<void> clean() async {
    //Clean don't delete the file
    //to always be sure it exist
    await File(state).writeAsString('');
    await File(current).writeAsString('');
  }

  //Finish, copy and delete position file + image
  static Future<void> finish(String uuid, File image) async {
    File(current).copy('$posDir/$uuid');
    clean();
    image?.copy('$imgDir/$uuid');
  }

  //backgroundlocator's side
  static Future<String> get posFile async {
    if (root == null) root = (await getApplicationDocumentsDirectory()).path;
    if (current == null) current = '$root$currentPath';
    return current;
  }
}

I set all the values inside init() to remove all my getters and added a img directory
for me the issue is really unRegisterLocationUpdate because if we do

BackgroundLocator.unRegisterLocationUpdate()
        .then((value) => print('stopped'));

"stopped" never gets printed so I abandonned the idea to create one file with a different name every time and instead having a json holding the Activity and DateTime, the plugin is just writing in the same file every time.
Having a init() function in the plugin's Isolate would have it possible to listen to the port and do some changes but it would be overkill I think

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

I can't see any problem with the plugin in this matter. The _currentFile belongs to your code not the plugin, it's static variable and you never clearing it. You can learn more about static in Dart static keyword.

About BackgroundLocator.unRegisterLocationUpdate() .then((value) => print('stopped'));
where are you calling it? if it's in dispose function then be sure that dispose I getting called at first.

It is more helpful If you can create a repository with your complete code so I can see where and when you are using this class and stopping the plugin.

from background_locator.

RomanJos avatar RomanJos commented on August 16, 2024

I don't think static is the problem here because the callback and my code aren't in the same process so I have two instance of my class
if _currentFile is null it create one, so the first time I open the app the callback list the directory and find the good file, but if I create another file after calling unRegisterLocationUpdate and removePortNameMapping, _currentFile is overrided by the new file only for my instance, on callback it will still get the last value

BackgroundLocator.unRegisterLocationUpdate() .then((value) => print('stopped')); Don't print even on the example

I'll fork the example

from background_locator.

RomanJos avatar RomanJos commented on August 16, 2024

Look at this main.dart https://github.com/RomanJos/background_locator/blob/ClassProblem/example/lib/main.dart
press override and from the plugin's side it would still be null

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

I don't think static is the problem here because the callback and my code aren't in the same process so I have two instance of my class

That's what static means. It will keep it's value among all process and instance.

from background_locator.

RomanJos avatar RomanJos commented on August 16, 2024

Please try the example, if I press override on the Flutter side the value is still null on the callback side

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

Ok, now I can see the problem. Just don't access TheProblem.current inside callback.
The current is static and you can access it anywhere.
Suppose you have save() function, inside this function you can access TheProblem.current directly and there is no need to pass it as argument.

In your example if you change this line the problem will be solved:

static void callback(LocationDto locationDto) async {
    final SendPort send = IsolateNameServer.lookupPortByName(_isolateName);
    send?.send('');
  }
port.listen(
          (dynamic _) async {
        print('current from callback: ${TheProblem.current}');
        setState(() {
          currentValue = TheProblem.current;
        });
      },
    );

from background_locator.

RomanJos avatar RomanJos commented on August 16, 2024

But the point of the example I gave you is that even thought TheProblem.current is static the value is not shared between the plugin and Flutter so if I only access this class inside Flutter it will work like I want but I want to use it inside callback to work in the background without Flutter so the class will have another instance inside the plugin's thread and thus will not be modifiable from Flutter.
If BackgroundLocator.unRegisterLocationUpdate() would kill this thread the instance of TheProblem from the plugins's side will be deleted and on restart of the plugin it will get this class reset

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

I'm not sure why, but the callback doesn't honor static concept. Maybe it's because it converts to a numeric value for the sake of saving handler. But I think the solution is pretty simple:

Look at this code:

static void callback(LocationDto locationDto) async {
   save(locationDto);
  }
static void save(LocationDto locationDto) {
    // get the file location
    // save it
}

All I'm saying is don't directly access your static file inside callback.
This way the problem will be solved.

from background_locator.

RomanJos avatar RomanJos commented on August 16, 2024

Oh so you mean callback need to call another function that this one will use the same instance as Flutter ?
I did this but nothing changed so far I still get two differnet value from Flutter and callback2

  static void callback(LocationDto locationDto) async {
    callback2();
  }

  static void callback2(){
    final SendPort send = IsolateNameServer.lookupPortByName(_isolateName);

    String current = TheProblem.current;
    send?.send(current);
  }

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

Oh so you mean callback need to call another function that this one will use the same instance as Flutter ?

I'm not 100% sure but I think the callback will pack everything that is inside it and will not look back at your static variable ever again, it just see the first value and pack it and use it whenever it needs it. As I said this is my assumption and it may be wrong.

I did this but nothing changed so far I still get two differnet value from Flutter and callback2

This is really strange, I did the exact same thing as I said in reply and when I pressed overrideSecond the log show me the second value.

Anyway why are you sending the current via port? you can access it anywhere. Please look at my previous reply once more.

from background_locator.

gpibarra avatar gpibarra commented on August 16, 2024

I will stay thinking about this topic.
To communicate the main process with the background process I would always use ports.
But try doing this by calling init and dispose functions. The init function also you can pass a map to pass data (on Android it was difficult, I had to use gjson).
In this way, in the example, the background process has a "state" (it is a class with the pattern facade, without using statistical variables).
I did not do PR.
What do you think? It can be useful?

https://github.com/gpibarra/background_locator/tree/callbackHandlerFeature

PS: Sorry my english

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

@gpibarra I saw your code, and honestly I don't get the point of it.

This is my understanding of your code, correct me if I'm wrong.

  1. You created initCallback and disposeCallback, the init has `initialDataMap
  2. When plugin starts to work you call initCallback and pass it's saved data to flutter app
  3. When plugin stops you call disposeCallback

What is not clear for me:

  1. How this can help with @RomanJos problem? He just want to use some static variable inside callback, he is not trying to send anything to background service?
  2. When you call BackgroundLocator.registerLocationUpdate it's the point of starting plugin, So why we need separate initCallback? and whats the point of initialDataMap? You don't do any process on it inside plugin, it just get saved and passed back.
  3. Same question about initCallback applies for disposeCallback. Whats the point of it? When user call BackgroundLocator.unRegisterLocationUpdate() isn't it the dispose?

PS: Your English is more than good :)

from background_locator.

mehdok avatar mehdok commented on August 16, 2024

@gpibarra Due to similar reported problems, I think this issue is getting more serious, please create a PR from your branch and I will dig it deeply ;)

from background_locator.

francipvb avatar francipvb commented on August 16, 2024

Hello @mehdok,

The plugins seems to be disposed but the isolate continues to run the callback, so the cleanup does not work in iOS.

I don't know UIKit to look at this by myself.

from background_locator.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.