Comments (22)
@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.
- You created
initCallback
anddisposeCallback
, the init has `initialDataMap- When plugin starts to work you call
initCallback
and pass it's saved data to flutter app- 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:
- 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?- When you call
BackgroundLocator.registerLocationUpdate
it's the point of starting plugin, So why we need separateinitCallback
? and whats the point ofinitialDataMap
? 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
- Same question about
initCallback
applies fordisposeCallback
. Whats the point of it? When user callBackgroundLocator.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.
@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.
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.
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.
If you share some code I could be more helpfull.
Static variables keep their value as long as the instance exists.
from background_locator.
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.
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.
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.
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.
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.
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.
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.
Please try the example, if I press override on the Flutter side the value is still null on the callback side
from background_locator.
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.
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.
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.
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.
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.
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.
@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.
- You created
initCallback
anddisposeCallback
, the init has `initialDataMap - When plugin starts to work you call
initCallback
and pass it's saved data to flutter app - When plugin stops you call
disposeCallback
What is not clear for me:
- 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? - When you call
BackgroundLocator.registerLocationUpdate
it's the point of starting plugin, So why we need separateinitCallback
? and whats the point ofinitialDataMap
? You don't do any process on it inside plugin, it just get saved and passed back. - Same question about
initCallback
applies fordisposeCallback
. Whats the point of it? When user callBackgroundLocator.unRegisterLocationUpdate()
isn't it the dispose?
PS: Your English is more than good :)
from background_locator.
@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.
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)
- How can I send data from location callback to controller? HOT 1
- location_callback_handler.dart not found when run release project HOT 5
- [ERROR:flutter/shell/common/shell.cc(93)] Dart Error: Dart_LookupLibrary: library 'package:untitled/location_callback_handler.dart' not found. HOT 4
- Unable to build flutter release version with background_locator lastest HOT 1
- Library Update
- Issue on release build HOT 8
- Unable to capture location in release mode (Android) HOT 4
- Unable to run on iOS
- Error java.lang.IncompatibleClassChangeError: Found interface com.google.android.gms.location.FusedLocationProviderClient HOT 1
- Error on init app flutter with carp_background_location HOT 5
- java.lang.IncompatibleClassChangeError: The method 'com.google.android.gms.tasks.Task com.google.android.gms.location.FusedLocationProviderClient.requestLocationUpdates(com.google.android.gms.location.LocationRequest, com.google.android.gms.location.LocationCallback, android.os.Looper)' was expected to be of type virtual but instead was found to be of type interface (declaration of 'rekab.app.background_locator.provider.GoogleLocationProviderClient' appears in /data/app/com.doa.lojisoft.saray-JqLCd0a12SEpAYQu6cUZjg==/base.apk:classes2.dex) HOT 2
- Service stopped suddenly in release mode android
- Data type mismatch (nullable type) and method implementation error in kotlin code just after adding the dependency HOT 1
- [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument: is a regular instance: Instance of 'LocationDto'
- What is speed measured in?
- How to stop service after a period of time?
- Upgrade Background_Locator
- Need Code or Library in kotlin only
- I so confused about "getting location updates even when the app is killed" !!!!
- Is it still maintained? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from background_locator.