Геолокатор, заданное состояние, утечка памяти

#flutter

#трепетать

Вопрос:

Я работаю над геолокацией с пакетом геолокации 7.6.2. У меня проблема с утечкой памяти при закрытии потока. Виджет выглядит так:

 class _GeolocatorActiveState extends Statelt;GeolocatorActivegt; {   StreamSubscriptionlt;Positiongt;? _currentPosition;   double distanceToday = 0.0;  double distanceTotal = 0.0;   bool gpsActive = true;   @override  void initState() {  getCurrentPosition();  super.initState();  }   @override  Widget build(BuildContext context) {  return Scaffold(  // Some stuff  ),  ),  backgroundColor: Colors.deepOrange  ),  body: Column(  children: [  // some stuff here  TextButton(onPressed: () {  setState(() {  gpsActive = false;  stopStream();  });  Navigator.pushNamed(context, '/');},  child: Text(  'Finish',  )  ),  // Some stuff there  ],  ),  );  }   void getCurrentPosition () {  final positionStream = GeolocatorPlatform.instance.getPositionStream();  if(gpsActive == true){  _currentPosition = positionStream.listen((position) {  setState(() {  long = position.longitude.toString();  lat = position.latitude.toString();  });  });  }  }   void stopStream () {  _currentPosition = null;  _currentPosition?.cancel();  super.dispose();  } }  

Теперь дело в том, что, когда я нажимаю кнопку «Готово», я хочу закрыть и удалить этот виджет. Я попробовал с Navigator.pushNamedAndRemoveUntil и подумал, что это вызывает утечку памяти, но нет. У меня была функция stopStream внутри getCurrentPosition как оператор else, но она тоже не работала. Как я могу заставить приложение закрыть поток, прежде чем оно закроет этот виджет? Или я что-то упускаю?

Ошибка выглядит так:

 E/flutter ( 4655): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: setState() called after dispose(): _GeolocatorActiveState#6b088(lifecycle state: defunct) E/flutter ( 4655): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. E/flutter ( 4655): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree. E/flutter ( 4655): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().  

Ответ №1:

Так что здесь две вещи не так:

  1. Вы вызываете super.dispose вне dispose метода, что вам не следует делать.
  2. Сначала вы аннулируете _currentComposition поток, а затем пытаетесь его отменить, но слишком поздно, потому что вы уже потеряли доступ к этому потоку. Вам следует сменить порядок.

Кстати, я думаю , что вы можете легко поместить все методы удаления потока внутрь dispose , а не закрывать их на кнопке «Не onTap перезванивать».

Вот пример вашего кода, который я изменил, обратите внимание на переопределенный dispose метод:

 class _GeolocatorActiveState extends Statelt;GeolocatorActivegt; {   StreamSubscriptionlt;Positiongt;? _currentPosition;   double distanceToday = 0.0;  double distanceTotal = 0.0;   bool gpsActive = true;   @override  void initState() {  super.initState();  getCurrentPosition();  }   @override  void dispose() {  _currentPosition?.cancel();  _currentPosition = null;  super.dispose();  }   @override  Widget build(BuildContext context) {  return Scaffold(  // Some stuff  ),  ),  backgroundColor: Colors.deepOrange  ),  body: Column(  children: [  // some stuff here  TextButton(onPressed: () {  setState(() {  gpsActive = false;  });  Navigator.pushNamed(context, '/');},  child: Text(  'Finish',  )  ),  // Some stuff there  ],  ),  );  }   void getCurrentPosition () {  final positionStream = GeolocatorPlatform.instance.getPositionStream();  if(gpsActive == true){  _currentPosition = positionStream.listen((position) {  setState(() {  long = position.longitude.toString();  lat = position.latitude.toString();  });  });  }  } }  

Ответ №2:

Вы можете попробовать то, что он предлагает:

Другое решение состоит в том, чтобы проверить свойство «смонтировано» этого объекта перед вызовом setState (), чтобы убедиться, что объект все еще находится в дереве.

Подобный этому:

 if (mounted) {  setState(() {  long = position.longitude.toString();  lat = position.latitude.toString();  });  }  

Комментарии:

1. Уже примерил как функцию, так и кнопку, вместе и по отдельности. Все та же утечка.