rmawatson/flutter_isolate

Need help maintaining only one Isolate executing

Closed this issue · 3 comments

I am using this package to get the palette from an Image when the user goes to the WallpaperScreen.

Code:
void computePalette(List<Object> args) async {
  var image = args[0] as Uint8List;
  var url = args[1] as String;
  var port = args[2] as SendPort;
  var img = Image.memory(image);
  var palette = await PaletteGenerator.fromImageProvider(
    img.image,
    size: Size(300, 240),
    maximumColorCount: 6,
  );
  List<int> colors = [];
  palette.colors.forEach((element) => colors.add(element.value));
  port.send([colors, url]);
}

class PaletteController extends GetxController {
  FlutterIsolate? isolate;

  void getPalette(WallpaperModel wall) async {
    print("getting palette");
    if (isolate != null) {
      isolate!.kill();
      print("isolate killed mid compute");
    }
    var cache = DefaultCacheManager();
    File file = await cache.getSingleFile(wall.url);
    var port = ReceivePort();
    isolate = await FlutterIsolate.spawn(
      computePalette,
      [file.readAsBytesSync(), wall.url, port.sendPort],
    );
    port.listen((msg) {
      List<Object> data = msg;
      addColors(data, wall);
      print("added palette for ${wall.name}");
      isolate!.kill();
      isolate = null;
      port.close();
    });
  }
}

The Problem: My function seems to work when it is called once in 3 seconds. But when I call getPalette() rapidly (5 times in 3) seconds I get the following error in the console:

Error:
I/flutter (14144): getting palette for Soap
I/flutter (14144): getting palette for Green Impact
I/flutter (14144): isolate killed mid compute
I/flutter (14144): getting palette for Deep Dusky Canyon
I/flutter (14144): isolate killed mid compute
I/flutter (14144): added palette for Green Impact

E/MethodChannel#com.rmawatson.flutterisolate/control(14144): Failed to handle method call
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): java.lang.NullPointerException: Attempt to read from field 'io.flutter.embedding.engine.FlutterEngine com.rmawatson.flutterisolate.IsolateHolder.engine' on a null object reference
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at com.rmawatson.flutterisolate.FlutterIsolatePlugin.onMethodCall(FlutterIsolatePlugin.java:211)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:818)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at android.os.MessageQueue.next(MessageQueue.java:336)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at android.os.Looper.loop(Looper.java:174)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at android.app.ActivityThread.main(ActivityThread.java:7356)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
E/MethodChannel#com.rmawatson.flutterisolate/control(14144): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
E/flutter (14144): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: PlatformException(error, Attempt to read from field 'io.flutter.embedding.engine.FlutterEngine com.rmawatson.flutterisolate.IsolateHolder.engine' on a null object reference, null, java.lang.NullPointerException: Attempt to read from field 'io.flutter.embedding.engine.FlutterEngine com.rmawatson.flutterisolate.IsolateHolder.engine' on a null object reference
E/flutter (14144): 	at com.rmawatson.flutterisolate.FlutterIsolatePlugin.onMethodCall(FlutterIsolatePlugin.java:211)
E/flutter (14144): 	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/flutter (14144): 	at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
E/flutter (14144): 	at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:818)
E/flutter (14144): 	at android.os.MessageQueue.nativePollOnce(Native Method)
E/flutter (14144): 	at android.os.MessageQueue.next(MessageQueue.java:336)
E/flutter (14144): 	at android.os.Looper.loop(Looper.java:174)
E/flutter (14144): 	at android.app.ActivityThread.main(ActivityThread.java:7356)
E/flutter (14144): 	at java.lang.reflect.Method.invoke(Native Method)
E/flutter (14144): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
E/flutter (14144): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)
E/flutter (14144): )
E/flutter (14144): #0      StandardMethodCodec.decodeEnvelope
E/flutter (14144): #1      MethodChannel._invokeMethod
E/flutter (14144): <asynchronous suspension>
E/flutter (14144):

As you can see from the 1st 6 lines of the error, line 5 says it killed the Isolate for Green Impact but line 6 says that the palette successfully computed for Green Impact (The names are of the images). I think the isolated are not getting killed correctly.

Is there any way I can achieve only 1 Isolate running at a time? It would be nice if flutterIsolate.kill() would let me know if the Isolate was killed or not.

After researching a bit. I tried to implement a single Isolate instead of trying to kill and create new Isolates each time. It was a bit hard to set up 2-way communication but now there are no errors. I just send the image to the Isolate via a port. If the isolate is already running, I set a while loop to poll a boolean variable which tells if the Isolate is free or not, if it is free then send the next Image in the pipeline. No more errors or performance issues now.

@ShreeyansB could you please share the solution code?

@ShreeyansB could you please share the solution code?

Check Here for the isolate code.

Also I am polling bool isIsolateComputing here (line 85) to check its value so that I wait for the previous palette computation to complete before computing a new palette otherwise I face lag.