简体   繁体   中英

Dart Isolate utilizing Timer periodic doesn't stop

I have the following Dart Isolate called in my Flutter app that runs and handles a task via the _checkTimer method every 7 seconds. This is handy for background tasks that I need to run every certain period of time.

import 'dart:async';
import 'dart:isolate';

class TimeTrackingDigester with ChangeNotifier {
  Isolate _isolate;
  static int _counter = 0;
  ReceivePort _receivePort;

  void startIsolate() async {
    print('**** time tracking digester = startIsolate');

    _receivePort = ReceivePort();
    _isolate = await Isolate.spawn(
      _checkTimer,
      _receivePort.sendPort,
    );
    _receivePort.listen(
      _handleMessage,
      onDone: () {
        //@TODO this never fires
        print('done!');
      },
    );
  }

  void stopIsolate() => _stop();

  static void _checkTimer(SendPort sendPort) async {
    print('**** time tracking digestoer = checkTimer');
    Timer.periodic(
      const Duration(seconds: 7),
      (Timer t) async {
        _counter++;
        print('**** time tracking digestoer = $_counter');
        
        var task = 'result from some action here';

        sendPort.send(task);
      },
    );
  }

  void _handleMessage(dynamic data) {
    print('**** time tracking digestoer = handleMessage');
    print('RECEIVED: $data');
  }

  //@TODO this is not stopping the isolate
  void _stop() {
    print('**** time tracking digestoer = stop');
    print(_isolate);

    if (_isolate != null) {
      print('* isolate is not null');
      // setState(() {
      //     _running = false;
      //
      // });
      _receivePort.close();
      _isolate.kill(priority: Isolate.immediate);
      _isolate = null;
    }
  }
}

My first problem is once the isolate is started, I call stopIsolate method to stop the Isolate yet it doesn't ever hit the close() / kill() calls because the _isolate is considered null . From what I can gather, the _isolate is always null and I'm missing a step to actually set the _isolate in the following stanza:

if (_isolate != null) {
    print('* isolate is not null');
    // setState(() {
    //     _running = false;
    //
    // });
    _receivePort.close();
    _isolate.kill(priority: Isolate.immediate);
    _isolate = null;
 }

My second issue, is if I disregard the null check, and simply call the close() / kill() methods, the following Exceptions are thrown:

NoSuchMethodError: The method 'close' was called on null. I/flutter (15558): Receiver: null I/flutter (15558): Tried calling: close()

The end result I am trying to accomplish: when I want to stop the Isolate, it fully stops the subsequent _checkTimer method, and resolve the _isolate from not being set correctly in order to trigger the null check correctly.

I tested your code, and it can successfully stop the isolate. Please make sure that you're using the same instance of TimeTrackingDigester so that _isolate will not be null .

Run this sample app.

import 'dart:async';
import 'dart:isolate';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(fontFamily: 'Roboto'),
      home: SampleIsolate(),
    );
  }
}

class SampleIsolate extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => TimeTrackingDigester(),
      child: Builder(
        builder: (context2) => Scaffold(
          body: Center(
            child: RaisedButton(
              onPressed: () {
                // Will get the TimeTrackingDigester created in "create: (_) => ..."
                final digester = Provider.of<TimeTrackingDigester>(
                  context2,
                  listen: false,
                );
                if (digester.isStarted) {
                  digester.stopIsolate();
                } else {
                  digester.startIsolate();
                }
              },
              child: const Text('Start / Stop'),
            ),
          ),
        ),
      ),
    );
  }
}

class TimeTrackingDigester with ChangeNotifier {
  Isolate _isolate;
  static int _counter = 0;
  ReceivePort _receivePort;

  bool get isStarted => _isolate != null;

  void startIsolate() async {
    print('**** time tracking digester = startIsolate');

    _receivePort = ReceivePort();
    _isolate = await Isolate.spawn(
      _checkTimer,
      _receivePort.sendPort,
    );
    _receivePort.listen(
      _handleMessage,
      onDone: () {
        //@TODO this never fires
        print('done!');
      },
    );
  }

  void stopIsolate() => _stop();

  static void _checkTimer(SendPort sendPort) async {
    print('**** time tracking digestoer = checkTimer');
    Timer.periodic(
      const Duration(seconds: 7),
      (Timer t) async {
        _counter++;
        print('**** time tracking digestoer = $_counter');

        var task = 'result from some action here';

        sendPort.send(task);
      },
    );
  }

  void _handleMessage(dynamic data) {
    print('**** time tracking digestoer = handleMessage');
    print('RECEIVED: $data');
  }

  //@TODO this is not stopping the isolate
  void _stop() {
    print('**** time tracking digestoer = stop');
    print(_isolate);

    if (_isolate != null) {
      print('* isolate is not null');
      // setState(() {
      //     _running = false;
      //
      // });
      _receivePort.close();
      _isolate.kill(priority: Isolate.immediate);
      _isolate = null;
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM