From 27bd5f538ecc055921e90b80599e0d75fc7083c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Sj=C3=B8gren?= Date: Tue, 26 Aug 2025 12:36:44 +0200 Subject: [PATCH] chor: wrap methodChannel.invokeMethod in isolates --- .../kotlin/dk/nota/flutter_readium/Readium.kt | 3 - .../lib/method_channel_flutter_readium.dart | 83 ++++++++++++++----- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/flutter_readium/android/src/main/kotlin/dk/nota/flutter_readium/Readium.kt b/flutter_readium/android/src/main/kotlin/dk/nota/flutter_readium/Readium.kt index 73e6f72..9595321 100644 --- a/flutter_readium/android/src/main/kotlin/dk/nota/flutter_readium/Readium.kt +++ b/flutter_readium/android/src/main/kotlin/dk/nota/flutter_readium/Readium.kt @@ -8,10 +8,7 @@ package dk.nota.flutter_readium import android.content.Context import android.util.Log -import io.flutter.plugin.common.MethodChannel -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.readium.r2.shared.ExperimentalReadiumApi import org.readium.r2.shared.publication.Publication diff --git a/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart b/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart index 7fd3485..9fc331e 100644 --- a/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart +++ b/flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'flutter_readium_platform_interface.dart'; @@ -47,15 +48,16 @@ class MethodChannelFlutterReadium extends FlutterReadiumPlatform { } @override - Future openPublication(String pubUrl) async { - final publicationString = - await methodChannel.invokeMethod('openPublication', [pubUrl]).then((dynamic result) => result); - return Publication.fromJson(json.decode(publicationString) as Map); - } + Future openPublication(String pubUrl) async => computeWithBinaryMessenger(() async { + final publicationString = await methodChannel + .invokeMethod('openPublication', [pubUrl]).then((dynamic result) => result); + return Publication.fromJson(json.decode(publicationString) as Map); + }); @override - Future closePublication(String pubIdentifier) async => - await methodChannel.invokeMethod('closePublication', [pubIdentifier]); + Future closePublication(String pubIdentifier) async => computeWithBinaryMessenger( + () async => await methodChannel.invokeMethod('closePublication', [pubIdentifier]), + ); @override Future goLeft() async => await currentReaderWidget?.goLeft(); @@ -80,38 +82,55 @@ class MethodChannelFlutterReadium extends FlutterReadiumPlatform { await currentReaderWidget?.applyDecorations(id, decorations); @override - Future ttsEnable(TTSPreferences? preferences) async => - await methodChannel.invokeMethod('ttsEnable', preferences?.toMap()); + Future ttsEnable(TTSPreferences? preferences) async => computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsEnable', preferences?.toMap()); + }); @override - Future ttsStart(Locator? fromLocator) async => - await methodChannel.invokeMethod('ttsStart', [fromLocator?.toJson()]); + Future ttsStart(Locator? fromLocator) async => computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsStart', [fromLocator?.toJson()]); + }); @override - Future ttsStop() async => await methodChannel.invokeMethod('ttsStop'); + Future ttsStop() async => computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsStop'); + }); @override - Future ttsPause() async => await methodChannel.invokeMethod('ttsPause'); + Future ttsPause() async => computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsPause'); + }); @override - Future ttsResume() async => await methodChannel.invokeMethod('ttsResume'); + Future ttsResume() async => computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsResume'); + }); @override - Future ttsNext() async => await methodChannel.invokeMethod('ttsNext'); + Future ttsNext() async => computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsNext'); + }); @override - Future ttsPrevious() async => await methodChannel.invokeMethod('ttsPrevious'); + Future ttsPrevious() async => computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsPrevious'); + }); @override Future ttsSetDecorationStyle( ReaderDecorationStyle? utteranceDecoration, ReaderDecorationStyle? rangeDecoration, ) => - methodChannel.invokeMethod('ttsSetDecorationStyle', [utteranceDecoration?.toJson(), rangeDecoration?.toJson()]); + computeWithBinaryMessenger(() async { + await methodChannel + .invokeMethod('ttsSetDecorationStyle', [utteranceDecoration?.toJson(), rangeDecoration?.toJson()]); + }); @override Future> ttsGetAvailableVoices() async { - final voicesStr = await methodChannel.invokeMethod>('ttsGetAvailableVoices'); + final voicesStr = await computeWithBinaryMessenger( + () async => await methodChannel.invokeMethod>('ttsGetAvailableVoices')); + final voices = voicesStr ?.cast() .map>((str) => json.decode(str) as Map) @@ -123,14 +142,32 @@ class MethodChannelFlutterReadium extends FlutterReadiumPlatform { @override Future ttsSetVoice(String voiceIdentifier, String? forLanguage) async { - await methodChannel.invokeMethod('ttsSetVoice', [voiceIdentifier, forLanguage]); + await computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsSetVoice', [voiceIdentifier, forLanguage]); + }); } @override - Future ttsSetPreferences(TTSPreferences preferences) => - methodChannel.invokeMethod('ttsSetPreferences', preferences.toMap()); + Future ttsSetPreferences(TTSPreferences preferences) => computeWithBinaryMessenger(() async { + await methodChannel.invokeMethod('ttsSetPreferences', preferences.toMap()); + }); @override - Future getLinkContent(final String pubIdentifier, final Link link) => - methodChannel.invokeMethod('getLinkContent', [pubIdentifier, jsonEncode(link.toJson())]); + Future getLinkContent(final String pubIdentifier, final Link link) => computeWithBinaryMessenger(() async => + await methodChannel.invokeMethod('getLinkContent', [pubIdentifier, jsonEncode(link.toJson())])); } + +// Executes a computation in an isolate using a binary message. +// Automatically handles the binary message passing when needed for +// iOS and Android. +// Use this function for calls that requires BackgroundIsolateBinaryMessenger. +Future computeWithBinaryMessenger(final FutureOr Function() computation) async => await compute( + (final token) { + if (!kIsWeb && token != null) { + BackgroundIsolateBinaryMessenger.ensureInitialized(token); + } + + return computation(); + }, + kIsWeb ? null : RootIsolateToken.instance, + );