Flutter: Eine Plattformkanalmethode kann nicht von einem anderen Isolat aufgerufen werden

Erstellt am 5. Jan. 2018  ·  133Kommentare  ·  Quelle: flutter/flutter

Wenn ich versuche, eine Plattformkanalmethode von einem benutzerdefinierten Isolat aufzurufen, stürzt die App stark ab (sowohl auf iOS als auch auf Android). Ich versuche herauszufinden, ob dies erwartet wird oder nicht.

Wenn nicht, ist es wahrscheinlich wert, das irgendwo zu erwähnen.

Wie auch immer, ich denke, dass dies möglicherweise eine starke Einschränkung sein kann. Gibt es eine Möglichkeit, Plattform-Plugins von einem sekundären Isolat aus aufzurufen?

P5 annoyance crowd gold engine framework passed first triage plugin crash new feature

Hilfreichster Kommentar

Ich habe verschiedene Anwendungsfälle, zB:

  • Laden Sie einen großen Datenblock herunter und analysieren Sie ihn (dart:convert-Methoden sind nicht asynchron) und speichern Sie ihn dann mit dem sqflite-Plugin (das Plattformbindungen verwendet) in einer SQLite-Datenbank.
  • Daten vorab abrufen und parsen, bevor sie der Benutzeroberfläche bereitgestellt werden;
  • Verschlüsseln/Entschlüsseln und Lesen/Schreiben von Daten aus/in eine Datei (Krypto-Zeug wird über einen Methodenkanal ausgeführt, um Plattform-Sicherheitsbibliotheken zu verwenden);

Im Allgemeinen kann es vorkommen, dass Sie Abhängigkeiten verwenden, die einige öffentliche Methoden deklarieren, von denen Sie nicht wirklich wissen, ob sie letztendlich plattformspezifischen Code verwenden, um ihre Aufgaben zu erledigen (ich denke zum Beispiel an Flutterfire); Die Verwendung sollte meiner Meinung nach eher ein Implementierungsdetail sein, das sich mit der Zeit ändern kann, als etwas, das in Stein gemeißelt ist.

Ihre Problemumgehung scheint im Moment in Ordnung und relativ problemlos zu implementieren, da Daten bereits so codiert sind, dass sie an den Methodenkanal übergeben werden, und aus diesem Grund auch problemlos über einen isolierten Port übertragen werden. Ich vermute jedoch, dass die Leistung suboptimal wäre .

Allerdings bin ich bei der Implementierung etwas hängen geblieben: Können Sie ein Beispiel für eine einfache Möglichkeit geben, eine statische Methode für das Hauptisolat von einem sekundären Isolat aufzurufen?

Danke

Alle 133 Kommentare

cc @mravn-google für die Sichtung

Der Engine-Code dereferenziert null und stürzt ab, wenn versucht wird, eine Plattformnachricht vom sekundären Isolat zu senden. Ich habe noch nicht genau festgestellt, wo; Ich bekomme einen Grabstein, muss aber lernen, so etwas zu interpretieren.

Als (umständliche) Problemumgehung könnte das sekundäre Isolat das primäre auffordern, die Nachricht zu senden.

@srody Darf ich fragen, was Sie mit dem sekundären Isolat erreichen möchten?

Ich habe verschiedene Anwendungsfälle, zB:

  • Laden Sie einen großen Datenblock herunter und analysieren Sie ihn (dart:convert-Methoden sind nicht asynchron) und speichern Sie ihn dann mit dem sqflite-Plugin (das Plattformbindungen verwendet) in einer SQLite-Datenbank.
  • Daten vorab abrufen und parsen, bevor sie der Benutzeroberfläche bereitgestellt werden;
  • Verschlüsseln/Entschlüsseln und Lesen/Schreiben von Daten aus/in eine Datei (Krypto-Zeug wird über einen Methodenkanal ausgeführt, um Plattform-Sicherheitsbibliotheken zu verwenden);

Im Allgemeinen kann es vorkommen, dass Sie Abhängigkeiten verwenden, die einige öffentliche Methoden deklarieren, von denen Sie nicht wirklich wissen, ob sie letztendlich plattformspezifischen Code verwenden, um ihre Aufgaben zu erledigen (ich denke zum Beispiel an Flutterfire); Die Verwendung sollte meiner Meinung nach eher ein Implementierungsdetail sein, das sich mit der Zeit ändern kann, als etwas, das in Stein gemeißelt ist.

Ihre Problemumgehung scheint im Moment in Ordnung und relativ problemlos zu implementieren, da Daten bereits so codiert sind, dass sie an den Methodenkanal übergeben werden, und aus diesem Grund auch problemlos über einen isolierten Port übertragen werden. Ich vermute jedoch, dass die Leistung suboptimal wäre .

Allerdings bin ich bei der Implementierung etwas hängen geblieben: Können Sie ein Beispiel für eine einfache Möglichkeit geben, eine statische Methode für das Hauptisolat von einem sekundären Isolat aufzurufen?

Danke

@srody Danke für die Bereitstellung der Hintergrundinformationen.

Über das Aufrufen statischer Methoden auf dem Hauptisolat: Es wird nicht direkt unterstützt und muss mithilfe von Ports implementiert werden. Dieses Paket kann diese Funktionalität bereitstellen, zB hier . Habe es aber selbst nicht probiert.

Aber wir sollten hier nach einer allgemeineren Lösung suchen, die auch funktioniert, wenn die Plattformkanalkommunikation als Teil der Implementierung eines Plugins oder einer anderen Bibliothek erfolgt.

Wir haben noch nicht ganz die Haken dafür, aber wenn wir für einen Moment davon ausgehen können, dass Sie einen bekannten Satz von Kanalnamen haben , die vom sekundären Isolat und nicht vom Hauptisolat verwendet werden sollen, sollten Sie in der Lage sein, dies generisch zu tun Rekonfigurieren Sie die Nachrichtenbehandlung der Plattform für diese Kanäle selbst und implementieren Sie transparent die notwendige Weiterleitung von binären Nachrichten und Antworten zwischen Ihren beiden Isolaten. Plugins würden den Unterschied nie erkennen.

Lösung unten skizziert.

Angenommen, Sie richten die Ports M1 und R1 auf Ihrem Hauptisolat und M2, R2 auf Ihrem sekundären Isolat ein. M für Nachricht, R für Antwort.

  • Verwenden Sie in Ihrem sekundären Isolat BinaryMessages.setMockMessageHandler für jeden Kanal, um Nachrichten für die Plattform an M1 weiterzuleiten (transparent für das Plugin, das BinaryMessage.send mit diesem Kanal verwendet). Speichern Sie Antwortrückrufe und konfigurieren Sie R2 mit einem Handler, der beim Empfang der Antwort für die Nachricht den richtigen aufruft. Konfigurieren Sie M2 mit einem Handler, der an BinaryMessages.handlePlatformMessage weiterleitet. Implementieren Sie dort den Antwort-Callback, um Antworten an R1 weiterzuleiten.
  • Symmetrisch in Ihrem Hauptisolat. Konfigurieren Sie M1 mit einem Handler, der Nachrichten für die Plattform an BinaryMessages.send weiterleitet. Legen Sie einen Antwort-Callback fest, der Antworten von der Plattform an R2 weiterleitet. Rufen Sie auch BinaryMessages.setMessageHandler für jeden der Kanäle auf, um einen Handler für eingehende Nachrichten von der Plattform einzurichten, der an M2 weiterleitet. Speichern Sie die Antwortrückrufe und konfigurieren Sie R1 mit einem Handler, der beim Empfang einer Antwort vom sekundären Isolat den richtigen aufruft.

@Hixie Ich schlage vor, zum nächsten Meilenstein zu gehen. Es scheint einen Workaround zu geben. Eine gute Lösung erfordert einige API-Designarbeiten und Iterationen.

Ich habe das gleiche Problem, gibt es Neuigkeiten zu diesem Problem?

@mravn-google Haben Sie diesbezüglich ein Update? Ich bin auf dasselbe Problem gestoßen (ich muss z. B. ein RSA 2048-Schlüsselpaar auf der Plattformseite generieren, was auf älteren Geräten eine Weile dauert, oder Daten verschlüsseln/entschlüsseln). Ich würde es lieber vermeiden, Threads oder Dienste zu erstellen, da dies meine Arbeit auf der Plattformseite verdoppeln würde, da es sowohl für Android- als auch für iOS-Plattformen implementiert werden soll. Haben wir eine einfache Möglichkeit, diese plattformspezifischen Operationen asynchron von Dart aus auszuführen? Ihre Problemumgehung scheint in Ordnung zu sein, aber es scheint, dass ich einige Probleme mit der Implementierung habe, da ich ziemlich neu bei Flutter und Dart bin (ich bin mir nicht sicher, wie ich einen Handler konfigurieren soll, der Nachrichten für ein Isolate usw. weiterleitet), sorry über das.

Ich habe das gleiche Problem.

Ich muss ein PDF auf der Android/iOS-Seite generieren, was eine Weile dauert, und die Methode compute(...) stürzt die App ab, wenn ich von dort aus einen Anruf ausführe.

Ich habe dieses Problem und jetzt stecke ich fest.

Ich möchte accessToken aus den freigegebenen Einstellungen im Hintergrundisolat lesen, obwohl die App nicht im Vordergrund ist (das Hauptisolat wird möglicherweise nicht ausgeführt, da bin ich mir nicht sicher).

Kann mir also jemand eine Lösung sagen, um persistente Daten aus dem Hintergrundisolat zu lesen.
Ich bin jetzt wirklich in Schwierigkeiten.

/cc @bkonyi Dies scheint mit etwas zu tun zu haben, an dem Sie gerade arbeiten.

Ich habe das gleiche Problem. unsere Produktbasis auf CPU-gebunden. Wir müssen die Plattformmethode isoliert verwenden. Können Sie uns bitte einen anderen Weg nennen, um dieses Problem zu lösen?

@Thomson-Tsui Ich hatte ein ähnliches Problem, und basierend auf einer Destillation der Arbeit von @bkonyi und seinem FlutterGeofencing-Beispiel gelang es mir, etwas in einem Isolat zum Laufen zu bringen, das ich mit flutter_blue und anderen Plugins verwende. Es ist ziemlich ungetestet, aber versuchen Sie es bitte.

Ich habe das gleiche Problem, ich denke, es ist wichtig

Hat jemand dieses Problem gelöst?

Ich muss Audio in unterschiedlichen Intervallen im Hintergrund abspielen. Der einfachste Weg, dies zu tun, wäre, ein separates Isolate zu haben, das das Audio verwaltet, aber ohne diese Funktion wird es einige wirklich seltsame Arbeiten mit Ports erfordern

Ich gehe davon aus, dass die nativen Bibliotheken nicht verknüpft werden, wenn Sie ein Isolat von Dart ausführen.

Wenn Sie beispielsweise Flutter-Code von Java ausführen, wird diese Methode aufgerufen:

   private native void nativeRunBundleAndSnapshotFromLibrary (
       long nativePlatformViewId,
       <strong i="7">@NonNull</strong> String [] prioritizedBundlePaths,
       <strong i="8">@Nullable</strong> String entrypointFunctionName,
       <strong i="9">@Nullable</strong> String pathToEntrypointFunction,
       <strong i="10">@NonNull</strong> AssetManager manager
   );

In dem die ganze Magie passiert)

Dies wird durch die Tatsache bestätigt, dass, wenn Sie mehrere FlutterNativeView erstellen, und in jedem Lauf:

  public void runFromBundle (FlutterRunArguments args)

dann bekommen wir ein paar "Dart-Isolate"

Auch wir stehen vor diesem Problem. Wir müssen Daten von Hintergrundisolaten (z. B. Standort, Lärm usw.) sammeln, die alle auf Plattformkanälen beruhen. @mravn-google - gibt es Neuigkeiten zu einem Update der API, das dies beheben könnte?

Es ist wirklich ein ernstes Problem in Tool- oder Medien-Apps.
Plattform-Thread ist kein einfacher Weg.

Ich hatte auch ein ähnliches Problem und bin jetzt hängen geblieben.

+1 (mein Projekt braucht auch dringend Plattformkanäle in Isolaten)

Ich muss Bilder im Hintergrund rendern. Dies muss isoliert erfolgen, da jedes Bild einige Sekunden dauert und die Benutzeroberfläche blockiert, wenn dies im Hauptthread ausgeführt wird. Ich stecke beim ersten nativen Aufruf von dart:ui.PictureRecorder fest und gehe davon aus, dass jeder einzelne grafische Aufruf (der native Funktionen verwendet) auch nicht funktioniert. Da ich viele Funktionen im Canvas verwende, wäre es mühsam, mit Callback-Ports zu arbeiten.

Eine Lösung wird sehr geschätzt.

+1 Ich habe dieses Problem

Es scheint, als hätte @mravn-google aufgehört, für Google in Aarhus zu arbeiten. Ich frage mich, ob es noch jemanden aus dem Google Flutter-Team gibt, der sich damit befasst. Meiner Meinung nach ist dies ein großer Showstopper für die ernsthafte Verwendung von Flutter zum Erstellen belastbarer und robuster Flutter-Anwendungen ....

Vielleicht kann @sethladd einen Status geben?

+1

irgendwelche Updates dazu?

+1

Der Tag, an dem Sie auf einen Fehler gestoßen sind, von dem Sie wissen, wie Sie ihn selbst lösen können, aber nur warten, ist die letzte Hoffnung, bis ich Flutter-Ingenieur werde.

Wir benötigen eine detailliertere Erklärung der Problemumgehung für neue Flutter/Dart-Benutzer. Ich habe erst vor zwei Tagen angefangen, Isolate zu verwenden, und letzte Woche Flatter/Dart, und es sieht so aus, als müsste ich den Hauptfaden für einige ernsthaft arbeitsintensive Aufgaben verwenden, was das Erlernen von Flattern zu einer schlechten Lösung macht. Ohne brauchbares Multithreading bin ich besser dran, Kotlin zu lernen und meine Apps zweimal zu erstellen. Ich hoffe, Sie können etwas zusammenstellen, das für Anfänger verständlich ist. Ich würde wirklich gerne in der Lage sein, das Lernen von Flattern gegenüber meinen Arbeitgebern zu rechtfertigen, damit ich es bei der Arbeit anwenden kann.
Wenn nicht hier, vielleicht auf StackOverflow, habe ich hier eine Frage gestellt: https://stackoverflow.com/q/57466952/6047611

+1 hoffe es wird bald behoben

+1 Mein Projekt benötigt Plattformkanäle in Isolaten.

Ich bin auf dasselbe Problem gestoßen und habe ein Paket ( Isolate Handler ) mit einer Problemumgehung erstellt, die der von @mravn-google geposteten ähnlich ist. Der Isolate Handler ermöglicht es Ihnen jedoch, Kanäle sowohl aus dem Hauptisolat als auch aus anderen Isolaten zu verwenden. Es unterstützt direkte MethodChannel -Anrufe innerhalb von Isolaten, aber derzeit werden EventChannel -Streams nicht unterstützt. Ich werde versuchen, Unterstützung für sie hinzuzufügen, wenn sich herausstellt, dass dies etwas ist, das die Leute brauchen.

Ein Nachteil der Problemumgehung ist die Notwendigkeit, den Isolate-Handler mit Kanalnamen zu versorgen. In meinem Projekt ist das kein Problem, da ich meinen nativen Code selbst schreibe, aber ansonsten müssen Sie den Quellcode jedes Plugins durchsuchen, das Sie verwenden, um sie zu finden.

Eine weitere Option, die bereits oben gepostet wurde, ist FlutterIsolate , die einen anderen Ansatz verwendet und ebenfalls einen Blick wert ist.

Hoffentlich werden die Flutter-Entwickler bald eine vollständige und angemessene Lösung für dieses Problem bereitstellen.

Bearbeiten:

Ich habe mein Problem schließlich sauberer gelöst, indem ich die Isolierung von der nativen Seite gestartet habe, anstatt einen Ansatz zu verwenden, der dem Flutter-Erstanbieter- Android-Alarm-Manager -Plugin ähnelt.

Das einzige, was ich tun musste, war, meinen nativen Code in Plugins umzuwandeln, damit sie bei der nativen Anwendung registriert werden konnten, was eine relativ problemlose Migration war. Wenn Sie Plugins von pub.dev verwenden, ist es noch einfacher, da sie nahtlos funktionieren sollten.

+1 irgendein Update hier?

+1

Interessantes was mir hier auffällt:
Mehrere Plugins scheinen mit der Annahme geschrieben worden zu sein, dass der Methodenkanalaufruf Nachrichten von Dart an einen Hintergrund-Thread weiterleitet; In diesem Zusammenhang gibt es einige mächtig verwirrte Leute, die diesem bg-Geofencing-Beispiel gefolgt sind und sich jetzt fragen, wie sie wieder zum Hauptthread zurückkehren können ...
Dieser Plugin-Code verursacht keinen "Junk", wenn sie eine kurze Aufgabe ausführen, aber sie werden standardmäßig auf dem Plattform-Thread ausgeführt, was früher "der UI-Thread" auf Android genannt wurde; Dies kann durchaus dazu führen, dass Gesten bei hoher Arbeitsbelastung verloren gehen, und ich verstehe, dass es auch alle anderen Nachrichtenkanäle blockieren würde.
Sie sind dann der Technik des Plugin-Autors ausgeliefert; Nur wenn sie ihre schwere Arbeit in einen anderen Thread im nativen Code verlagern, werden Sie Code ausführen, der auf Methodenkanäle wartet. diese Nachrichtenkanäle müssen auf dem Plattform-Thread laufen.
Ich verstehe zwar die Begründung, die der gute Chinmaygarde hier bezieht , und stimme zu, dass:

  • die meisten Plattform-API-Aufrufe sind schnell und müssen oft im Haupt-Thread erfolgen;
  • Es ist sinnvoller, Threads im nativen Code zu berücksichtigen
  • Es ist offensichtlich verwirrend für Leute, die an Android Java gewöhnt sind, den Weg zurück zum "Hauptthread" in einer Framework-Methodenüberschreibung zu finden

    • Ich kann mir nur vorstellen, wie sich das vervielfachen würde, wenn sie ihre ersten Nur-Plattform-Thread-API-Aufrufe tätigen.

Ich habe das Gefühl, dass die resultierende Architektur ein bedeutendes Anti-Pattern als Standard für Plugin-Autoren eröffnet hat; Auf den ersten Blick denke ich, dass sogar das Firebase-ml-Plugin den Plattform-Thread für seine Verarbeitung verwendet.
Ich wünschte also, es gäbe eine Art starke Warnung, die den Leuten gegeben würde, die schwere Lasten heben; vielleicht etwas in der standardmäßigen Beispiel-App, das Bildschirmfehler generiert, wenn die Nachrichtenkanalaufrufe länger als ~20 ms brauchen, um zurückzukehren, vielleicht etwas, das mehr ins Auge fällt; Ehrlich gesagt, was auch immer diese Leute überzeugt, die db-Anrufe machen &c. um schon einen Thread zu bekommen.

Oi, es gibt kein kostenloses Mittagessen, das ist sicher ...

Am Ende habe ich eine hohe Arbeitslast mit den nativen Plattform-Threads mit Java und Swift implementiert. Keine Problemumgehung gefunden.

@AndruByrne Das sind in der Tat sehr interessante Informationen und ich war mir dessen nicht bewusst, so offensichtlich es jetzt im Nachhinein scheint. Ich habe bereits ein Problem im Zusammenhang mit meinem Plugin, bei dem ein Benutzer versuchte, es als eine Möglichkeit zu verwenden, um eine schwere Hebeaufgabe im Hintergrund auszuführen, und die Benutzeroberfläche blockierte.

Ich stimme zu, dass es eine Warnung geben sollte, und ich werde eine zu meinem Plugin hinzufügen.

+1 brauche das

Unglücklicherweise ist Flutter nicht bereit für die Hauptsendezeit, da ihm etwas so sehr Grundlegendes und doch so sehr Entscheidendes fehlt. Nicht alle Flutter-Apps kommen mit einfachem async/await davon. Für das schwere Heben sind Isolate unerlässlich, und Plugins, die Plattformkanal-Methodenaufrufe von einem anderen Isolate nicht zulassen können, machen sie sinnlos und überflüssig. Xamarin hatte dies von Tag 1 an in das Framework eingebaut - sicherlich sollte dies jetzt für eine Lösung gestimmt werden.

+1 brauche das

+1

Ich muss auch eine Plattformkanalmethode von einem anderen Isolat aufrufen!

+1

+1

+1

+1 Dies ist ein Muss

+1
Ich habe ein natives SDK, das ich aufrufen muss. Es wäre schön, so etwas wie BackgroundFlutterMethodChannel zu haben.

+1

+1

Hat jemand irgendwelche Informationen darüber, was _crashs badly_ eigentlich beschreibt? Ich arbeite an einem Proof-of-Concept, das eine native Bibliothek implementiert, und ich stoße auf Watchdog-Timeouts (0x8badf00d) und andere seltsame Fehler, wenn Nachrichten über einen Methodenkanal hin und her gesendet werden.

Ich vermute, dass dies durch einige Methoden [und Ergebnisse] verursacht wird, die von anderen Threads aufgerufen werden, aber ich konnte keine Fortschritte bei der genauen Bestimmung des Problems erzielen.

+1

+1

+1

+1

+1 brauche das

+1 brauche das auch!

Ich denke, dieses Problem sollte mindestens als P2 priorisiert werden.
Die Verwendungen der Plattformkanäle, die ich mir vorstellen kann, sind

  • Um plattformspezifische APIs aufzurufen
  • Um plattformspezifische schwere Aufgaben im Hintergrund auszuführen und zu melden, wenn sie erledigt sind

Und das ist ein großer Blocker.

Das flutter_downloader -Plug-in behandelt Hintergrundisolationscode. Ich denke, es lohnt sich, es anzuschauen.

Ich hatte gehofft, dass dies in der kommenden Version behoben wird, aber der Fehler bleibt immer noch in Version 1.12!

Unser clientseitiger Code befindet sich stark auf der anderen Seite des Kanals und ist in Kotlin geschrieben, und die App bleibt bei jeder einzelnen HTTP-Verbindung fast hängen

Gooooooooooooogle?!!!!!!!!!!!!?!?!?!?

Dieses Plugin könnte hilfreich sein flutter_isolate

+1 wir brauchen das ... komm schon

+1

Das größte Problem für mich ist, dass die Kommunikation zwischen Dart und den nativen Plattformen im Haupt-Thread stattfindet – das Senden erheblicher Datenmengen ist unmöglich, ohne die Benutzeroberfläche zu verzögern oder umständlichen Code in Seitendaten zu schreiben.

+1 brauche das

Umständlichen Code in Seitendaten schreiben.

@lukaszciastko was meinst du damit, Code in Seitendaten zu schreiben. Gibt es eine Möglichkeit, die verzögerte Benutzeroberfläche zu lösen, wenn erhebliche Datenmengen gesendet werden?

@YaredTaddese

Abhängig von der Komplexität dessen, was Ihr ISOLATE tut, und der Interkommunikation zwischen Ihrer UI und ISOLATE – Es ist möglich, dieses Problem in Ihrem UI-Code zu umgehen, indem Sie strukturieren, welche Daten das ISOLATE benötigt, um seine Arbeit im Voraus zu erledigen, bevor das ISOLATE aufgerufen wird. Ich habe eine Rich-PDF-Generierung in meiner aktuellen Flutter-App - ein Benutzer kann mehrere PDFs zum Generieren auswählen.

Jeder kann 1 bis 10.000 Seiten erzeugen. Meine Benutzeroberfläche reagiert, wenn jeder Job abgeschlossen ist, wird ein TAB erstellt, der den Bericht anzeigt – der Benutzer wird benachrichtigt, wenn ein Job über einen TOAST abgeschlossen ist und die Benutzeroberfläche nicht zum Stillstand kommt – der Benutzer ist sich nicht bewusst, dass Dienste im Hintergrund beschäftigt sind Generieren von Rich-PDFs.

Beachten Sie, dass meine PDF-Generierung ein ISOLATE ist. Im ISOLATE - muss ich auch Bilder von CLOUD FIRESTORE herunterladen und in ein PDF einbetten - das alles geschieht nahtlos.

@MsXam , aber ich kann keine plattformspezifische Funktion von einem anderen Isolat aus aufrufen ... was dazu führt, dass ich die plattformspezifische Funktion im Hauptisolat aufrufe ... was zu einer verzögerten Benutzeroberfläche führt

Ich habe das gleiche Problem, ich denke, es ist sehr wichtig

@YaredTaddese

Abhängig von der Komplexität dessen, was Ihr ISOLATE tut, und der Interkommunikation zwischen Ihrer UI und ISOLATE – Es ist möglich, dieses Problem in Ihrem UI-Code zu umgehen, indem Sie strukturieren, welche Daten das ISOLATE benötigt, um seine Arbeit im Voraus zu erledigen, bevor das ISOLATE aufgerufen wird. Ich habe eine Rich-PDF-Generierung in meiner aktuellen Flutter-App - ein Benutzer kann mehrere PDFs zum Generieren auswählen.

Jeder kann 1 bis 10.000 Seiten erzeugen. Meine Benutzeroberfläche reagiert, wenn jeder Job abgeschlossen ist, wird ein TAB erstellt, der den Bericht anzeigt – der Benutzer wird benachrichtigt, wenn ein Job über einen TOAST abgeschlossen ist und die Benutzeroberfläche nicht zum Stillstand kommt – der Benutzer ist sich nicht bewusst, dass Dienste im Hintergrund beschäftigt sind Generieren von Rich-PDFs.

Beachten Sie, dass meine PDF-Generierung ein ISOLATE ist. Im ISOLATE - muss ich auch Bilder von CLOUD FIRESTORE herunterladen und in ein PDF einbetten - das alles geschieht nahtlos.

Rufen Sie einen plattformspezifischen Code auf?

Hat jemand eine Problemumgehung für das Aufrufen von Plattformkanälen von einem Isolat aus? Es widerlegt die Idee, dies vom UI-Thread aus zu tun.

Ich stecke an der selben Sache fest.

Ich rufe einen Methodenkanal mit einem Kotlin-Code und einer getStream.io-API auf, aber das ist höllisch langsam, und meine Flutter-Benutzeroberfläche friert ein. Ich wollte Berechnungen hinzufügen, aber ich habe die gleichen Fehler erhalten.

Wie kann ich das beheben?

Darüber bin ich auch gerade gestolpert. Ich prototypiere eine neue App, und dieses Problem könnte wirklich ein Showstopper für mich sein und mich veranlassen, nach anderen Nicht-Flutter-Lösungen zu suchen, was mich traurig machen wird.

Wie die Anzahl der Antworten zu diesem Problem zeigt, handelt es sich um ein GROSSES Problem, das den Hauptanwendungsfall von Isolaten im Kontext einer Flutter-App ziemlich zunichte macht.

Darüber bin ich auch gerade gestolpert. Ich prototypiere eine neue App, und dieses Problem könnte wirklich ein Showstopper für mich sein und mich veranlassen, nach anderen Nicht-Flutter-Lösungen zu suchen, was mich traurig machen wird.

Wie die Anzahl der Antworten zu diesem Problem zeigt, handelt es sich um ein GROSSES Problem, das den Hauptanwendungsfall von Isolaten im Kontext einer Flutter-App ziemlich zunichte macht.

Ja, das ist ein großes Problem, das so schnell wie möglich behoben werden muss

Ich lerne Flattern seit 10 Tagen und ich liebe es bisher, aber nach einer Weile des Experimentierens fängt man an, Probleme zu bekommen und die Nachteile zu sehen, und DIESE Ausgabe, die seit dem 5. Januar 2018 geöffnet ist, uff ...

Bitte beheben Sie diesen Fehler.

Hoffe auf baldige Unterstützung.

@jpsarda , @klaszlo8207 Halte nicht den Atem an. Und ja, es gab bis heute eine Lösung für das Problem: https://pub.dev/packages/isolate_handler. Der Programmierer musste heute den Stecker ziehen, weil eine sehr aktuelle Änderung in Flutter (nur wenige Tage alt) es unmöglich machte, dieser Route mehr zu folgen.

Sie müssen jedoch wissen, dass dies nicht wirklich Parallelität bedeutete. Es mag so ausgesehen haben und das Aussehen ist in mobilen Apps wichtig, aber der Plattformcode wird sowieso im Hauptthread ausgeführt, sodass das Delegieren einer langen Aufgabe vom Isolat an den Hauptthread nicht wirklich etwas bewirkt hat.

Es wäre schön, wenn Dart das Multithreading standardmäßig handhaben würde, wenn Sie eine asynchrone Funktion verwenden.

@spiderion Async war nie als Multithreading gedacht, genau wie bei vielen anderen Plattformen. Verstehen Sie mich nicht falsch, ich werde nicht sagen, dass die Beschränkung des Plattformkanals kein großes Problem ist, da dies der Fall ist (meine App hat bis gestern mit dem von mir erwähnten Plugin funktioniert und jetzt muss ich nach Problemumgehungen suchen), aber async/ Bei await ging es vom ersten Tag an nie um Multithreading.

@zoechi Wir würden gerne wissen, ob es ein Update zu diesem Problem gibt oder ob es einen Plan gibt, es zu beheben?
Danke schön.

@spiderion Sie können es nicht wirklich reparieren, es folgt aus der Art und Weise, wie Isolate funktionieren. Sie haben keine UI-Engine hinter sich, daher gibt es keine Plattformkanalkommunikation. Es gibt jedoch zwei Plugins, die Ihnen dabei helfen:

  • https://pub.dev/packages/flutter_isolate bietet ein Ersatzisolat, das mit Plugins kommunizieren kann , da es seine eigene UI-Unterstützung erstellt (nichts, was Sie sehen oder mit dem Sie umgehen müssen, nur technisch).

  • https://pub.dev/packages/isolate_handler , den wir gerade geändert haben, um sich auf das obige Paket zu verlassen, da die vorherige Verwendung durch eine kürzlich erfolgte Flutter-Änderung unmöglich gemacht wurde. Der Vorteil bei der Verwendung dieses Pakets anstelle von flutter_isolate selbst besteht darin, dass dies zusätzliche Handhabungsmöglichkeiten bietet, Sie können mehrere Isolate starten, diese verfolgen und Sie müssen keine eigene Kommunikation zwischen dem Isolat und dem Hauptpaket einrichten Thread (etwas, das Sie sowohl mit dem Originalbestand Isolate als auch mit FlutterIsolate manuell tun müssen), weil es abstrahiert und leicht verfügbar ist.

Ich benutze die zweite mit perfektem Erfolg. Eigentlich benutze ich es schon seit geraumer Zeit und als die Flutter Breaking Change kam, half ich seinem Programmierer, auf die neue Lösung umzusteigen, nur weil meine App auch kaputt ging. :-)

Daher halte ich es nicht für vernünftig, die Lösung vom Kern her zu erwarten, insbesondere weil die meisten Anwendungsfälle keine Plattformkanalkommunikation erfordern, sodass die unterstützende UI-Engine nicht etwas ist, das sie jedem Isolat hinzufügen möchten. Verwenden Sie einfach die vorhandenen Plugins, wenn Sie diese zusätzliche Funktionalität benötigen.

Hallo @deakjahn danke für deine schnelle Antwort.

Die Lösungen, die Sie bereitgestellt haben, scheinen sehr nützlich zu sein. Allerdings weiß ich nicht, ob es mein Problem lösen könnte. Ich habe derzeit eine Situation in der App, in der der Benutzer eine Geschichte erstellt, sagen wir ähnlich wie bei Instagram. In diesem Fall muss die App eine Liste mit Videos und Dateien auf den Firebase-Speicher hochladen, deren Upload sehr lange dauern kann (ca. 15 Minuten, wenn die Verbindung langsam ist). Nachdem die Dateien hochgeladen wurden, muss die App erstellt werden ein Dokument in der Firebase-Cloud Firestore. Das Problem, auf das ich stoße, ist, dass, wenn der Benutzer die App beendet, während die Dateien hochgeladen werden, die vollständige Aufgabe nicht vollständig ausgeführt wird.

Das ist nicht wirklich die Verantwortung des Isolats, es tut nur, was Sie ihm sagen. Firebase kann gestoppte Entladungen wieder aufnehmen, soweit ich sehen kann, obwohl ich es selbst nie benutzt habe.

Ich versuche, das getBatteryLevel-Beispiel auszuführen:
https://flutter.dev/docs/development/platform-integration/platform-channels?tab=android-channel-java-tab
und es stürzt jedes Mal ab, wenn ich versuche, es auf einem Android-Gerät auszuführen. Kann jemand bitte helfen? Ich bin mir nicht einmal sicher, wie ich den Fehler überprüfen soll, da es keine Protokolle gibt und es einfach in einer Endlosschleife weiterläuft und ich genau denselben Code verwende.

Kennzeichnung dieses Problems für P5 ??? 🥵

Bitte lesen Sie die früheren Kommentare. Es scheint sehr unwahrscheinlich , dass dies im Framework geändert wird, da es Mechanismen erfordert, die die meisten Isolate nicht benötigen, sodass es keinen Sinn macht, diesen Overhead hinzuzufügen. Für die Fälle, in denen Sie es brauchen, gibt es Pakete, um es zu lösen.

Benutzer interessieren sich nicht für den Mechanismus; wie schwer es ist oder nicht. Sie brauchen nur Ergebnisse. Dies ist möglicherweise nicht möglich. Aber wenn die Benutzer es brauchen, sollte Flutter es bereitstellen ...

Mir ist bewusst, dass die Implementierung Einschränkungen unterliegt und es viel Zeit in Anspruch nehmen kann, diese Funktion zum vorhandenen Isolat hinzuzufügen oder eine neue API mit dieser Funktion zu erstellen. Ich denke nur, dass dies eine höhere Priorität als P5 haben sollte.

Außerdem habe ich alle Ihre Kommentare gelesen und es fühlt sich an, als würden Sie nur das von Ihnen erstellte Paket bewerben.

Nein, ich war es nicht, der es erstellt hat, aber ich habe sein Eigentum vor ein paar Wochen akzeptiert, ja. Ich glaube nicht, dass das in diesem Thread geheim gehalten wurde. Darüber hinaus habe ich zwei Pakete erwähnt (eigentlich nur einmal in einem einzigen Beitrag), die beide in der Lage sind, das zu liefern, wonach Sie suchen, und ich habe keine Verbindung zu dem zweiten, außer dass ich mich auf seine bisher geleistete hervorragende Arbeit verlasse.

Wenn Sie es gelesen haben, war der einzige Grund, warum ich diese überhaupt verwendet habe, der, dass ich genau die gleiche Funktionalität benötigte, die Sie gefordert haben. Und meine Apps verlassen sich seit über einem Jahr problemlos darauf. Abgesehen davon, dass ich mit dem Innenleben von Isolaten besser vertraut bin, als es ein gelegentlicher Entwickler wäre, der sie nur verwendet (und dass ich begonnen habe, an ihrem Gegenstück in Flutter Web zu arbeiten), lässt mich denken, dass das, was ich geschrieben habe, wahr ist: diese Funktionalität wird es nicht sein in den Rahmen aufgenommen, weil es einige Argumente gegen seine Aufnahme gibt. Nicht, weil es viel Zeit in Anspruch nehmen würde, überhaupt nicht. Es würde relativ wenig Zeit in Anspruch nehmen. Ganz einfach, weil es allen Isolaten eine Backend-Komplexität hinzufügen würde, die nur wenige von ihnen tatsächlich benötigen und verwenden würden.

@deakjahn danke für diese Tipps, hilft mir die Verwendung von isolation_handler dabei, eine Methode aufzurufen und Daten an die Dart-Seite zu senden, wenn ich eine Benachrichtigung auf der nativen Seite erhalte? (Ich verwende twilio native sdks und erhalte Push-Benachrichtigungen auf der nativen Seite)

Wenn dies bedeutet, dass Sie ein Plattform-Plugin haben, das Sie verwenden, um über einen Plattformkanal zu kommunizieren, dann ja, jedes der beiden Pakete würde helfen. Wenn es mit normalem Code funktioniert, dann funktionieren diese Pakete auch mit einem Isolat.

@deakjahn Danke für die schnelle Antwort! Mein Problem ist, dass ich es nicht mit einem regulären Plattformkanal machen könnte, ich bin mir nicht sicher, aber ich denke, das Twilio SDK handhabt das Abhören von Push-Benachrichtigungen in einem separaten Isolat und nicht im Hauptthread, und ich denke, das ist mein Problem. Ich wollte wissen, ob es funktionieren würde, wenn ich die Methode jedes Mal aufrufen würde, wenn ich eine Benachrichtigung mit einem der beiden Pakete erhalte? Vielen Dank. (Ich überlege, zu nativen Apps zu wechseln, wenn dies nicht möglich ist)

Ich weiß nicht einmal, was Twilio ist (OK, ich habe es gegoogelt :-) ), also kann ich mir wirklich nicht sicher sein. Aber auf jeden Fall müssen Sie einen Plattformkanal verwenden, wenn Sie nativen Code anbinden möchten. Entweder Sie verwenden ein bereits vorhandenes Plugin, das jemand für die Verwendung dieses Twilio erstellt hat, oder Sie schreiben es selbst (in diesem Fall erstellen Sie entweder praktisch dasselbe Plugin, veröffentlichen es einfach nicht und verweisen lokal darauf), oder Sie kopieren einfach das relevante Plugin Code in Ihre eigene App (es wird keine externe Abhängigkeit sein, aber ansonsten wird es sowieso praktisch den gleichen Code haben).

Und wenn Sie einen Plattformkanal haben, gilt die vorherige Antwort: Wenn Sie den Plattformkanal und das Plugin bereits aus dem Hauptthread einer regulären Flutter-App verwenden können, können Sie dasselbe von einem Isolat mit einer der verwenden zwei Pakete.

Dieses Problem besteht also seit dem 5. Jan. 2018 und es ist immer noch nicht mit zusätzlichem Aufwand möglich. Die bereitgestellten Lösungen sind nett - aber auf dem Desktop funktioniert noch nichts. Wer also ausprobieren möchte, wie gut Flutter auf dem Desktop funktioniert, und sehen möchte, ob es für ein zukünftiges Produkt möglich ist, wird hier völlig stecken bleiben.
Fast jede App braucht Isolate, weil es immer einige riesige Berechnungen gibt, wie ist es möglich, dass bei so wichtigen API-Designfehlern nichts passiert?

Ich habe dafür einen Ausweg in meinem Plugin System Alert Window hinzugefügt

Die Lösung ist hier genannt Isolate communication .
Obwohl sich das bereitgestellte Beispiel speziell auf das Plugin für das Systemwarnfenster bezieht, kann es leicht für andere Plugins repliziert werden. Und es erfordert keine anderen ausgefallenen Plugins, damit es funktioniert!

Das kann noch recht problematisch werden. Dies bedeutet, dass es keine Möglichkeit gibt, ein Plugin direkt von einem Isolate aufzurufen, das im Hintergrund / Vordergrund ausgeführt wird, da Sie eine Nachricht an den App-Bereich senden müssten, um etwas mit einem Plugin zu tun. Wenn Ihre App also nicht läuft, gibt es keine Möglichkeit, etwas mit einem Plugin im Hintergrund zu tun.

Wenn Sie also beispielsweise etwas mit gemeinsamen Einstellungen im backgroundMessageHandler des FCM-Plugins tun möchten, wenn die App geschlossen wird, wird eine MissingPluginException ausgelöst.

Oder wenn Sie die App nicht mit einem Systemwarnfenster öffnen möchten. Das Warnfenster wird auf einem anderen Isolat ausgeführt. Sie müssten den Code also innerhalb Ihres Anwendungsbereichs ausführen. Das ist problematisch, da die App derzeit geschlossen ist.

Und es gibt wahrscheinlich viele andere Senarios, in denen dies ein großes Problem darstellt.

@michael-ottink die Szenarien, die Sie beschreiben, sind genau die, bei denen es kein Problem geben sollte. Plugins, die Code im Hintergrund ausführen, instanziieren im Allgemeinen eine ganz neue FlutterEngine mit ihrer eigenen Plugin-Registrierung, und das darin ausgeführte Isolat kann direkt über Plattformkanäle mit diesen Plugins kommunizieren.

Übrigens bedeutet das, dass, wenn Sie ein Isolat benötigen, um Plugins zu verwenden, der einfache Weg darin besteht, dieses Isolat einfach in eine FlutterEngine zu packen, und es wird diese Kräfte auf magische Weise erben. Dies ist, was das flutter_isolate-Paket für Sie auf Android und iOS tut (obwohl ich zustimme, dass es besser wäre, eine angemessene Lösung statt einer Problemumgehung zu haben. Die heute verfügbaren Problemumgehungen haben Engpässe, Overheads oder werden aus anderen Gründen möglicherweise nicht gewartet oder es fehlt die Unterstützung für alle alle Plattformen.)

@ryanheise , also macht flatter_isolate es für mich? Wie kann ich aus den Unterlagen nicht wirklich sagen. Was muss ich tun, damit es für meine FCM onBackgroundMessage funktioniert? Also muss ich in meinem onBackgroundMessageHandler ein neues Isolat erzeugen? Aber ich kann dort keine Plugins verwenden, also wie würde ich dann flatter_isolate verwenden?

Ihr Problem ist, dass firebase_messaging seit langem nicht mehr aktualisiert wurde und weit hinter den neuesten Hintergrundausführungs-APIs zurückbleibt. Wenn sie ihr Plugin aktualisierten, hätten Sie dieses Problem nicht mehr. Um es noch einmal zu verdeutlichen, das von Ihnen beschriebene Szenario ist genau das, bei dem es kein Problem geben sollte, da Hintergrundisolate bei korrekter Implementierung bereits Zugriff auf Plugins haben sollten. Das firebase_messaging-Plugin ist nicht gemäß den neuesten APIs implementiert und funktioniert daher nicht für Sie. Sie können mit diesem Projekt einen Fehlerbericht einreichen.

Dabei brauche ich dringend Hilfe. Gibt es möglicherweise eine Möglichkeit, wie ich dies tun kann. Ich habe morgen meinen ersten ernsthaften Deal und ich kann es einfach nicht herausfinden.

Tun Sie alles, um sicherzustellen, dass Plugins geladen werden. Ich weiß nicht, ob Sie die Anweisungen in der README zum Aktivieren von Hintergrundnachrichten befolgt haben, aber Sie müssen eine benutzerdefinierte Anwendungsklasse erstellen, die sich in die Initialisierung des FlutterNativeView-Hintergrunds des Plugins einklinkt. Wenn Sie dies nicht getan haben, steht Ihnen keines der Plugins zur Verfügung. Wenn das immer noch nicht funktioniert, können Sie versuchen, Ihr Projekt auf die alte Plugin-Architektur im v1-Stil herunterzustufen (und riskieren, andere Plugins zu beschädigen, die v2 erfordern).

Ich richte meine Application.kt so ein

import `in`.jvapps.system_alert_window.SystemAlertWindowPlugin
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService

import android.os.Build
import android.app.NotificationManager
import android.app.NotificationChannel

public class Application: FlutterApplication(), PluginRegistrantCallback {

   override fun onCreate() {
     super.onCreate()
     FlutterFirebaseMessagingService.setPluginRegistrant(this)
     createNotificationChannels()
     SystemAlertWindowPlugin.setPluginRegistrant(this)
   }

   override fun registerWith(registry: PluginRegistry) {
     FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
     SystemAlertWindowPlugin.registerWith(registry.registrarFor("in.jvapps.system_alert_window"))
   }

   fun createNotificationChannels() {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = "groupChannel"
        val descriptionText = "This is the group channel"
        val importance = NotificationManager.IMPORTANCE_HIGH
        val mChannel = NotificationChannel("59054", name, importance)
        mChannel.description = descriptionText
        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(mChannel)
    }
  }
}

Am besten posten Sie dies auf der Problemseite firebase_messaging, da es nichts mit diesem Problem zu tun hat.

Ich kann eine PR erstellen, die eine neue „spawnIsolate“-Methode für „SchedulerBinding“ haben wird.

Dann ist es möglich, Plattformmethoden in diesem Isolat aufzurufen.

Dies hilft Ihnen nur, wenn Sie eine Plattformmethode aufrufen und eine Antwort erhalten müssen.

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

import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:path_provider/path_provider.dart';

Future<void> _test(SendPort sendPort) async {
  final dir = await getTemporaryDirectory();
  sendPort.send(dir);
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final Completer<Object> completer = Completer<Object>();
  final RawReceivePort receivePort = RawReceivePort(completer.complete);

  final Isolate isolate = await ServicesBinding.instance.spawnIsolate(
    _test,
    receivePort.sendPort,
  );

  print(await completer.future);

  receivePort.close();
  isolate.kill();
}

Protokoll

Performing hot restart...
Syncing files to device Pixel 4...
Restarted application in 722ms.
I/flutter (11705): Directory: '/data/user/0/com.example.bug/cache'

Ich kann eine PR erstellen, die eine neue „spawnIsolate“-Methode für „SchedulerBinding“ haben wird.

Dann ist es möglich, Plattformmethoden in diesem Isolat aufzurufen.

Dies hilft Ihnen nur, wenn Sie eine Plattformmethode aufrufen und eine Antwort erhalten müssen.

SchedulerBinding.instance.spawnIsolate

Gibt es eine Möglichkeit das zu testen? Wenn es meinen Bedürfnissen entspricht etc..

@ Nagelik
Diese Datei ersetzen (Aktuell für Version 1.17.5)


Bindung.dart

```dart// Copyright 2014 The Flutter Authors. Alle Rechte vorbehalten.
// Die Verwendung dieses Quellcodes wird durch eine BSD-Lizenz geregelt, die sein kann
// gefunden in der LICENSE-Datei.

import ' dart:async ';
import ' dart:isolate ';
import ' dart:typed_data ';
importiere ' dart:ui ' als ui;

import ' Paket:flutter/foundation.dart ';

import 'asset_bundle.dart';
import 'binary_messenger.dart';
import 'system_channels.dart';

/// Hört auf Plattformnachrichten und leitet sie an [defaultBinaryMessenger] weiter.
///
/// Die [ServicesBinding] registriert auch einen [LicenseEntryCollector], der exponiert
/// die Lizenzen, die in der Datei LICENSE gefunden wurden, die im Stammverzeichnis des Assets gespeichert ist
/// Bundle und implementiert die Diensterweiterung ext.flutter.evict (siehe
/// [räumen]).
mixin ServicesBinding auf BindingBase {
@überschreiben
void initInstances() {
super.initInstances();
_instance = dies;
_defaultBinaryMessenger = createBinaryMessenger();
window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
initLizenzen();
SystemChannels.system.setMessageHandler (handleSystemMessage);
}

/// Die aktuelle [ServicesBinding], falls eine erstellt wurde.
statische ServicesBinding get instance => _instance;
statische ServicesBinding _instance;

/// Die Standardinstanz von [BinaryMessenger].
///
/// Dies wird verwendet, um Nachrichten von der Anwendung an die Plattform zu senden, und
/// verfolgt, welche Handler auf jedem Kanal so registriert wurden
/// Es kann eingehende Nachrichten an den registrierten Handler senden.
BinaryMessenger get defaultBinaryMessenger => _defaultBinaryMessenger;
BinaryMessenger _defaultBinaryMessenger;

/// Erstellt eine standardmäßige [BinaryMessenger]-Instanz, die zum Senden verwendet werden kann
/// Plattformnachrichten.
@geschützt
BinaryMessenger createBinaryMessenger() {
return const _DefaultBinaryMessenger._();
}

/// Handler für Nachrichten aufgerufen, die auf [SystemChannels.system] empfangen wurden
/// Nachrichtenkanal.
///
/// Andere Bindungen können dies überschreiben, um auf eingehende Systemmeldungen zu reagieren.
@geschützt
@mustCallSuper
ZukunfthandleSystemMessage(Objekt systemMessage) async { }

/// Fügt relevante Lizenzen zu [LicenseRegistry] hinzu.
///
/// Standardmäßig fügt die [ServicesBinding]-Implementierung von [initLicenses] hinzu
/// alle Lizenzen, die das Tool flutter während der Kompilierung gesammelt hat.
@geschützt
@mustCallSuper
void initLicenses() {
LicenseRegistry.addLicense(_addLicenses);
}

Strom_addLicenses() asynchron* {
// Wir verwenden hier Timer (anstelle von scheduleTask aus der Scheduler-Bindung)
// da die Dienstschicht die Scheduler-Bindung nicht verwenden kann (die scheduler
// die Bindung verwendet die Dienstschicht, um ihre Lebenszyklusereignisse zu verwalten). Timer
// werden sowieso von scheduleTask unter der Haube verwendet. Der einzige Unterschied ist
// dass diese nur als nächstes ausgeführt werden, anstatt relativ zu priorisiert zu werden
// die anderen Aufgaben, die möglicherweise ausgeführt werden. Verwenden von _etwas_ hier, um zu brechen
// dies in zwei Teile ist wichtig, weil das Kopieren von Isolaten eine Weile dauert
// Daten im Moment und ob wir die Daten in derselben Ereignisschleife erhalten
// Iteration, wenn wir die Daten an das nächste Isolat senden, sind wir definitiv
// wird Frames verpassen. Eine andere Lösung wäre, die Arbeit alle zu haben
// passieren in einem Isolat, und wir können irgendwann dorthin gehen, aber zuerst sind wir es
// Mal sehen, ob die isolierte Kommunikation billiger gemacht werden kann.
// Siehe: https://github.com/dart-lang/sdk/issues/31959
// https://github.com/dart-lang/sdk/issues/31960
// TODO(ianh): Entfernen Sie diese Komplexität, sobald diese Fehler behoben sind.
endgültiger VervollständigerrawLicenses = Completer();
Timer.run(() async {
rawLicenses.complete(rootBundle.loadString('LICENSE', cache: false));
});
warte auf rawLicenses.future;
endgültiger Vervollständiger> parsedLicenses = Completer>();
Timer.run(() async {
parsedLicenses.complete(compute(_parseLicenses, await rawLicenses.future, debugLabel: 'parseLicenses'));
});
warte auf parsedLicenses.future;
Ertrag* Stream.fromIterable(warten auf parsedLicenses.future);
}

// Dies wird in einem anderen Isolat ausgeführt, das oben von _addLicenses erstellt wurde.
statische Liste_parseLicenses(String rawLicenses) {
final String _licenseSeparator = '\n' + ('-' * 80) + '\n';
endgültige ListeErgebnis =[];
endgültige ListeLizenzen = rawLicenses.split (_licenseSeparator);
für (endgültige Zeichenfolgenlizenz in Lizenzen) {
final int split = lizenz.indexOf('\n\n');
if (Split >= 0) {
result.add(LicenseEntryWithLineBreaks(
lizenz.substring(0, split).split('\n'),
lizenz.substring(split + 2),
));
} anders {
result.add(LicenseEntryWithLineBreaks(const[], Lizenz));
}
}
Ergebnis zurückgeben;
}

@überschreiben
void initServiceExtensions() {
super.initServiceExtensions();

assert(() {
  registerStringServiceExtension(
    // ext.flutter.evict value=foo.png will cause foo.png to be evicted from
    // the rootBundle cache and cause the entire image cache to be cleared.
    // This is used by hot reload mode to clear out the cache of resources
    // that have changed.
    name: 'evict',
    getter: () async => '',
    setter: (String value) async {
      evict(value);
    },
  );
  return true;
}());

}

/// Wird als Antwort auf die Diensterweiterung ext.flutter.evict aufgerufen.
///
/// Dies wird vom Tool flutter während des Hot-Reloads verwendet, damit alle Bilder
/// die sich auf der Festplatte geändert haben, werden aus den Caches gelöscht.
@geschützt
@mustCallSuper
void evict(String Asset) {
rootBundle.evict(asset);
}

ZukunftspawnIsolate(
ZukunftOderentryPoint (T-Nachricht),
T-Nachricht, {
bool angehalten = falsch,
Bool-Fehler sind schwerwiegend,
SendPort onExit,
SendPort onError,
Zeichenfolge debugName,
}) {
behaupten(
_isMainIsolate,
'Kann nicht mehrere Ebenen von Isolaten erstellen',
);

final RawReceivePort messageReceiver = RawReceivePort(
  (Object receivedMessage) async {
    if (receivedMessage is SendPort) {
      receivedMessage.send(
        _IsolateStarter<T>(
          ui.PluginUtilities.getCallbackHandle(entryPoint),
          message,
        ),
      );
    } else if (receivedMessage is _Message) {
      final ByteData result = await defaultBinaryMessenger.send(
        receivedMessage.channel,
        receivedMessage.message,
      );
      receivedMessage.sendPort.send(result);
    }
  },
);
RawReceivePort onExitReceiver;
onExitReceiver = RawReceivePort(
  (Object message) {
    onExit?.send(message);

    onExitReceiver.close();
    messageReceiver.close();
  },
);

return Isolate.spawn(
  _startIsolate,
  messageReceiver.sendPort,
  paused: paused,
  errorsAreFatal: true,
  onExit: onExitReceiver.sendPort,
  onError: onError,
  debugName: debugName,
);

}
}

Zukunft_startIsolate(SendPort sendPort) asynchron {
_sendPortToMainIsolate = sendPort;
_IsolateBinding();

final Completer<_IsolateStarter> kompletter =
Completer<_IsolateStarter>();

final RawReceivePort ReceivePort = RawReceivePort(
(Objekt isolationStarter) {
assert(isolateStarter ist _IsolateStarter);
completer.complete(isolateStarter als _IsolateStarter);
},
);

sendPort.send(receivePort.sendPort);

final _IsolateStarterisolationStarter = warte auf completer.future;

ReceivePort.close();

endgültige Funktion Funktion =
ui.PluginUtilities.getCallbackFromHandle(isolateStarter.callbackHandle);

Wartefunktion (isolateStarter.message);
}

SendPort _sendPortToMainIsolate;

bool get _isMainIsolate => _sendPortToMainIsolate == null;

Klasse _IsolateStarter{
_IsolateStarter (this.callbackHandle, this.message);

final ui.CallbackHandle callbackHandle;
abschließende T-Nachricht;
}

Klasse _Message {
_Nachricht(
dieser.Kanal,
diese Nachricht,
this.sendPort,
);

letzter String-Kanal;
letzte ByteData-Nachricht;
endgültiger SendPort sendPort;
}

Klasse _IsolateBinding erweitert BindingBase um ServicesBinding {}

/// Die Standardimplementierung von [BinaryMessenger].
///
/// Dieser Messenger sendet Nachrichten von der App-Seite an die Plattform-Seite und
/// leitet eingehende Nachrichten von der Plattformseite an die entsprechende weiter
/// Handler.
Klasse _DefaultBinaryMessenger erweitert BinaryMessenger {
const _DefaultBinaryMessenger._();

// Handler für eingehende Nachrichten von Plattform-Plugins.
// Dies ist statisch, damit diese Klasse einen konstanten Konstruktor haben kann.
statische endgültige Karte_handler =
{};

// Mock-Handler, die ausgehende Nachrichten abfangen und darauf antworten.
// Dies ist statisch, damit diese Klasse einen konstanten Konstruktor haben kann.
statische endgültige Karte_mockHandlers =
{};

Zukunft_sendPlatformMessage(String-Kanal, ByteData-Nachricht) {
endgültiger VervollständigerVervollständiger = Vervollständiger();

if (_isMainIsolate) {
  // ui.window is accessed directly instead of using ServicesBinding.instance.window
  // because this method might be invoked before any binding is initialized.
  // This issue was reported in #27541. It is not ideal to statically access
  // ui.window because the Window may be dependency injected elsewhere with
  // a different instance. However, static access at this location seems to be
  // the least bad option.
  ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
    try {
      completer.complete(reply);
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'services library',
        context:
            ErrorDescription('during a platform message response callback'),
      ));
    }
  });
} else {
  RawReceivePort receivePort;
  receivePort = RawReceivePort(
    (Object message) async {
      assert(message is ByteData);
      completer.complete(message as ByteData);
      receivePort.close();
    },
  );
  _sendPortToMainIsolate.send(
    _Message(channel, message, receivePort.sendPort),
  );
}

return completer.future;

}

@überschreiben
ZukunfthandlePlatformMessage(
Saitenkanal,
ByteData-Daten,
ui.PlatformMessageResponseCallback Rückruf,
) asynchron {
ByteData-Antwort;
Versuchen {
final MessageHandler handler = _handlers[channel];
if (handler != null) {
Antwort = Warte auf Handler (Daten);
} anders {
ui.channelBuffers.push (Kanal, Daten, Rückruf);
Rückruf = null;
}
} catch (Ausnahme, Stack) {
FlutterError.reportError(FlutterErrorDetails(
Ausnahme: Ausnahme,
Stapel: Stapel,
Bibliothek: 'Dienste Bibliothek',
Kontext: ErrorDescription('während eines Rückrufs einer Plattformnachricht'),
));
} endlich {
if (Rückruf != null) {
Rückruf (Antwort);
}
}
}

@überschreiben
Zukunftsend(String-Kanal, ByteData-Nachricht) {
final MessageHandler handler = _mockHandlers[channel];
if (handler != null)
Rückgabehandler (Nachricht);
return _sendPlatformMessage (Kanal, Nachricht);
}

@überschreiben
void setMessageHandler(String-Kanal, MessageHandler-Handler) {
if (handler == null)
_handlers.remove (Kanal);
anders
_handlers[Kanal] = Handler;
ui.channelBuffers.drain(channel, (ByteData data, ui.PlatformMessageResponseCallback callback) async {
await handlePlatformMessage (Kanal, Daten, Rückruf);
});
}

@überschreiben
void setMockMessageHandler(String-Kanal, MessageHandler-Handler) {
if (handler == null)
_mockHandlers.remove (Kanal);
anders
_mockHandlers[Kanal] = Handler;
}
}
```

Ich habe Flutter 1.21, also habe ich versucht, den gesamten Code zum Kompilieren zu aktualisieren.
Jetzt gibt es noch ein Problem, weil _IsolateBinding viele Implementierungen von BindingBase-Methoden fehlen ...

Ich bekomme etw

Error: The non-abstract class '_IsolateBinding' is missing implementations for these members:
 - BindingBase with ServicesBinding.SchedulerBinding.addPersistentFrameCallback

rund 30 mal.

Am Ende druckt die Konsole

/C:/flutter/packages/flutter/lib/src/services/binding.dart:341:7: Error: 'BindingBase' doesn't implement 'SchedulerBinding' so it can't be used with 'ServicesBinding'.
 - 'BindingBase' is from 'package:flutter/src/foundation/binding.dart' ('/C:/flutter/packages/flutter/lib/src/foundation/binding.dart').
 - 'SchedulerBinding' is from 'package:flutter/src/scheduler/binding.dart' ('/C:/flutter/packages/flutter/lib/src/scheduler/binding.dart').
 - 'ServicesBinding' is from 'package:flutter/src/services/binding.dart' ('/C:/flutter/packages/flutter/lib/src/services/binding.dart').
class _IsolateBinding extends BindingBase with ServicesBinding {}

Ich bin mir nicht 100% sicher, was der Aufruf von _IsolateBinding() in _startIsolate macht, aber ohne bekomme ich den üblichen ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized. Fehler.

Ich habe versucht, dies zu beheben, aber ich denke, mein Wissen über Mixins ist noch nicht so gut.
Irgendeine Idee, wie man es repariert?


Die neue Datei sieht (bisher) so aus

// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// <strong i="24">@dart</strong> = 2.8

import 'dart:async';
import 'dart:isolate';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';

import 'asset_bundle.dart';
import 'binary_messenger.dart';
import 'restoration.dart';
import 'system_channels.dart';

/// Listens for platform messages and directs them to the [defaultBinaryMessenger].
///
/// The [ServicesBinding] also registers a [LicenseEntryCollector] that exposes
/// the licenses found in the `LICENSE` file stored at the root of the asset
/// bundle, and implements the `ext.flutter.evict` service extension (see
/// [evict]).
mixin ServicesBinding on BindingBase, SchedulerBinding {
  <strong i="25">@override</strong>
  void initInstances() {
    super.initInstances();
    _instance = this;
    _defaultBinaryMessenger = createBinaryMessenger();
    _restorationManager = createRestorationManager();
    window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
    initLicenses();
    SystemChannels.system.setMessageHandler(handleSystemMessage);
    SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
    readInitialLifecycleStateFromNativeWindow();
  }

  /// The current [ServicesBinding], if one has been created.
  static ServicesBinding get instance => _instance;
  static ServicesBinding _instance;

  /// The default instance of [BinaryMessenger].
  ///
  /// This is used to send messages from the application to the platform, and
  /// keeps track of which handlers have been registered on each channel so
  /// it may dispatch incoming messages to the registered handler.
  BinaryMessenger get defaultBinaryMessenger => _defaultBinaryMessenger;
  BinaryMessenger _defaultBinaryMessenger;

  /// Creates a default [BinaryMessenger] instance that can be used for sending
  /// platform messages.
  <strong i="26">@protected</strong>
  BinaryMessenger createBinaryMessenger() {
    return const _DefaultBinaryMessenger._();
  }

  /// Called when the operating system notifies the application of a memory
  /// pressure situation.
  ///
  /// This method exposes the `memoryPressure` notification from
  /// [SystemChannels.system].
  <strong i="27">@protected</strong>
  <strong i="28">@mustCallSuper</strong>
  void handleMemoryPressure() { }

  /// Handler called for messages received on the [SystemChannels.system]
  /// message channel.
  ///
  /// Other bindings may override this to respond to incoming system messages.
  <strong i="29">@protected</strong>
  <strong i="30">@mustCallSuper</strong>
  Future<void> handleSystemMessage(Object systemMessage) async {
    final Map<String, dynamic> message = systemMessage as Map<String, dynamic>;
    final String type = message['type'] as String;
    switch (type) {
      case 'memoryPressure':
        handleMemoryPressure();
        break;
    }
    return;
  }

  /// Adds relevant licenses to the [LicenseRegistry].
  ///
  /// By default, the [ServicesBinding]'s implementation of [initLicenses] adds
  /// all the licenses collected by the `flutter` tool during compilation.
  <strong i="31">@protected</strong>
  <strong i="32">@mustCallSuper</strong>
  void initLicenses() {
    LicenseRegistry.addLicense(_addLicenses);
  }

  Stream<LicenseEntry> _addLicenses() async* {
    // We use timers here (rather than scheduleTask from the scheduler binding)
    // because the services layer can't use the scheduler binding (the scheduler
    // binding uses the services layer to manage its lifecycle events). Timers
    // are what scheduleTask uses under the hood anyway. The only difference is
    // that these will just run next, instead of being prioritized relative to
    // the other tasks that might be running. Using _something_ here to break
    // this into two parts is important because isolates take a while to copy
    // data at the moment, and if we receive the data in the same event loop
    // iteration as we send the data to the next isolate, we are definitely
    // going to miss frames. Another solution would be to have the work all
    // happen in one isolate, and we may go there eventually, but first we are
    // going to see if isolate communication can be made cheaper.
    // See: https://github.com/dart-lang/sdk/issues/31959
    //      https://github.com/dart-lang/sdk/issues/31960
    // TODO(ianh): Remove this complexity once these bugs are fixed.
    final Completer<String> rawLicenses = Completer<String>();
    scheduleTask(() async {
      rawLicenses.complete(await rootBundle.loadString('NOTICES', cache: false));
    }, Priority.animation);
    await rawLicenses.future;
    final Completer<List<LicenseEntry>> parsedLicenses = Completer<List<LicenseEntry>>();
    scheduleTask(() async {
      parsedLicenses.complete(compute(_parseLicenses, await rawLicenses.future, debugLabel: 'parseLicenses'));
    }, Priority.animation);
    await parsedLicenses.future;
    yield* Stream<LicenseEntry>.fromIterable(await parsedLicenses.future);
  }

  // This is run in another isolate created by _addLicenses above.
  static List<LicenseEntry> _parseLicenses(String rawLicenses) {
    final String _licenseSeparator = '\n' + ('-' * 80) + '\n';
    final List<LicenseEntry> result = <LicenseEntry>[];
    final List<String> licenses = rawLicenses.split(_licenseSeparator);
    for (final String license in licenses) {
      final int split = license.indexOf('\n\n');
      if (split >= 0) {
        result.add(LicenseEntryWithLineBreaks(
          license.substring(0, split).split('\n'),
          license.substring(split + 2),
        ));
      } else {
        result.add(LicenseEntryWithLineBreaks(const <String>[], license));
      }
    }
    return result;
  }

  <strong i="33">@override</strong>
  void initServiceExtensions() {
    super.initServiceExtensions();

    assert(() {
      registerStringServiceExtension(
        // ext.flutter.evict value=foo.png will cause foo.png to be evicted from
        // the rootBundle cache and cause the entire image cache to be cleared.
        // This is used by hot reload mode to clear out the cache of resources
        // that have changed.
        name: 'evict',
        getter: () async => '',
        setter: (String value) async {
          evict(value);
        },
      );
      return true;
    }());
  }

  /// Called in response to the `ext.flutter.evict` service extension.
  ///
  /// This is used by the `flutter` tool during hot reload so that any images
  /// that have changed on disk get cleared from caches.
  <strong i="34">@protected</strong>
  <strong i="35">@mustCallSuper</strong>
  void evict(String asset) {
    rootBundle.evict(asset);
  }

  Future<Isolate> spawnIsolate<T>(
      FutureOr<void> entryPoint(T message),
      T message, {
        bool paused = false,
        bool errorsAreFatal,
        SendPort onExit,
        SendPort onError,
        String debugName,
      }) {
    assert(
    _isMainIsolate,
    'Can\'t make multiple levels of isolates',
    );

    final RawReceivePort messageReceiver = RawReceivePort(
          (Object receivedMessage) async {
        if (receivedMessage is SendPort) {
          receivedMessage.send(
            _IsolateStarter<T>(
              ui.PluginUtilities.getCallbackHandle(entryPoint),
              message,
            ),
          );
        } else if (receivedMessage is _Message) {
          final ByteData result = await defaultBinaryMessenger.send(
            receivedMessage.channel,
            receivedMessage.message,
          );
          receivedMessage.sendPort.send(result);
        }
      },
    );
    RawReceivePort onExitReceiver;
    onExitReceiver = RawReceivePort(
          (Object message) {
        onExit?.send(message);

        onExitReceiver.close();
        messageReceiver.close();
      },
    );

    return Isolate.spawn(
      _startIsolate,
      messageReceiver.sendPort,
      paused: paused,
      errorsAreFatal: true,
      onExit: onExitReceiver.sendPort,
      onError: onError,
      debugName: debugName,
    );
  }



  // App life cycle

  /// Initializes the [lifecycleState] with the [Window.initialLifecycleState]
  /// from the window.
  ///
  /// Once the [lifecycleState] is populated through any means (including this
  /// method), this method will do nothing. This is because the
  /// [Window.initialLifecycleState] may already be stale and it no longer makes
  /// sense to use the initial state at dart vm startup as the current state
  /// anymore.
  ///
  /// The latest state should be obtained by subscribing to
  /// [WidgetsBindingObserver.didChangeAppLifecycleState].
  <strong i="36">@protected</strong>
  void readInitialLifecycleStateFromNativeWindow() {
    if (lifecycleState != null) {
      return;
    }
    final AppLifecycleState state = _parseAppLifecycleMessage(window.initialLifecycleState);
    if (state != null) {
      handleAppLifecycleStateChanged(state);
    }
  }

  Future<String> _handleLifecycleMessage(String message) async {
    handleAppLifecycleStateChanged(_parseAppLifecycleMessage(message));
    return null;
  }

  static AppLifecycleState _parseAppLifecycleMessage(String message) {
    switch (message) {
      case 'AppLifecycleState.paused':
        return AppLifecycleState.paused;
      case 'AppLifecycleState.resumed':
        return AppLifecycleState.resumed;
      case 'AppLifecycleState.inactive':
        return AppLifecycleState.inactive;
      case 'AppLifecycleState.detached':
        return AppLifecycleState.detached;
    }
    return null;
  }

  /// The [RestorationManager] synchronizes the restoration data between
  /// engine and framework.
  ///
  /// See the docs for [RestorationManager] for a discussion of restoration
  /// state and how it is organized in Flutter.
  ///
  /// To use a different [RestorationManager] subclasses can override
  /// [createRestorationManager], which is called to create the instance
  /// returned by this getter.
  RestorationManager get restorationManager => _restorationManager;
  RestorationManager _restorationManager;

  /// Creates the [RestorationManager] instance available via
  /// [restorationManager].
  ///
  /// Can be overriden in subclasses to create a different [RestorationManager].
  <strong i="37">@protected</strong>
  RestorationManager createRestorationManager() {
    return RestorationManager();
  }
}

Future<void> _startIsolate<T>(SendPort sendPort) async {
  _sendPortToMainIsolate = sendPort;
  _IsolateBinding();

  final Completer<_IsolateStarter<T>> completer =
  Completer<_IsolateStarter<T>>();

  final RawReceivePort receivePort = RawReceivePort(
        (Object isolateStarter) {
      assert(isolateStarter is _IsolateStarter<T>);
      completer.complete(isolateStarter as _IsolateStarter<T>);
    },
  );

  sendPort.send(receivePort.sendPort);

  final _IsolateStarter<T> isolateStarter = await completer.future;

  receivePort.close();

  final Function function =
  ui.PluginUtilities.getCallbackFromHandle(isolateStarter.callbackHandle);

  await function(isolateStarter.message);
}

SendPort _sendPortToMainIsolate;

bool get _isMainIsolate => _sendPortToMainIsolate == null;

class _IsolateStarter<T> {
  _IsolateStarter(this.callbackHandle, this.message);

  final ui.CallbackHandle callbackHandle;
  final T message;
}

class _Message {
  _Message(
      this.channel,
      this.message,
      this.sendPort,
      );

  final String channel;
  final ByteData message;
  final SendPort sendPort;
}

//TODO not working
class _IsolateBinding extends BindingBase with ServicesBinding {}

/// The default implementation of [BinaryMessenger].
///
/// This messenger sends messages from the app-side to the platform-side and
/// dispatches incoming messages from the platform-side to the appropriate
/// handler.
class _DefaultBinaryMessenger extends BinaryMessenger {
  const _DefaultBinaryMessenger._();

  // Handlers for incoming messages from platform plugins.
  // This is static so that this class can have a const constructor.
  static final Map<String, MessageHandler> _handlers =
  <String, MessageHandler>{};

  // Mock handlers that intercept and respond to outgoing messages.
  // This is static so that this class can have a const constructor.
  static final Map<String, MessageHandler> _mockHandlers =
  <String, MessageHandler>{};

  Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
    final Completer<ByteData> completer = Completer<ByteData>();

    if (_isMainIsolate) {
      // ui.window is accessed directly instead of using ServicesBinding.instance.window
      // because this method might be invoked before any binding is initialized.
      // This issue was reported in #27541. It is not ideal to statically access
      // ui.window because the Window may be dependency injected elsewhere with
      // a different instance. However, static access at this location seems to be
      // the least bad option.
      ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
        try {
          completer.complete(reply);
        } catch (exception, stack) {
          FlutterError.reportError(FlutterErrorDetails(
            exception: exception,
            stack: stack,
            library: 'services library',
            context:
            ErrorDescription('during a platform message response callback'),
          ));
        }
      });
    } else {
      RawReceivePort receivePort;
      receivePort = RawReceivePort(
            (Object message) async {
          assert(message is ByteData);
          completer.complete(message as ByteData);
          receivePort.close();
        },
      );
      _sendPortToMainIsolate.send(
        _Message(channel, message, receivePort.sendPort),
      );
    }

    return completer.future;
  }

  <strong i="38">@override</strong>
  Future<void> handlePlatformMessage(
      String channel,
      ByteData data,
      ui.PlatformMessageResponseCallback callback,
      ) async {
    ByteData response;
    try {
      final MessageHandler handler = _handlers[channel];
      if (handler != null) {
        response = await handler(data);
      } else {
        ui.channelBuffers.push(channel, data, callback);
        callback = null;
      }
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'services library',
        context: ErrorDescription('during a platform message callback'),
      ));
    } finally {
      if (callback != null) {
        callback(response);
      }
    }
  }

  <strong i="39">@override</strong>
  Future<ByteData> send(String channel, ByteData message) {
    final MessageHandler handler = _mockHandlers[channel];
    if (handler != null)
      return handler(message);
    return _sendPlatformMessage(channel, message);
  }

  <strong i="40">@override</strong>
  void setMessageHandler(String channel, MessageHandler handler) {
    if (handler == null)
      _handlers.remove(channel);
    else
      _handlers[channel] = handler;
    ui.channelBuffers.drain(channel, (ByteData data, ui.PlatformMessageResponseCallback callback) async {
      await handlePlatformMessage(channel, data, callback);
    });
  }

  <strong i="41">@override</strong>
  void setMockMessageHandler(String channel, MessageHandler handler) {
    if (handler == null)
      _mockHandlers.remove(channel);
    else
      _mockHandlers[channel] = handler;
  }

  <strong i="42">@override</strong>
  bool checkMessageHandler(String channel, MessageHandler handler) => _handlers[channel] == handler;

  <strong i="43">@override</strong>
  bool checkMockMessageHandler(String channel, MessageHandler handler) => _mockHandlers[channel] == handler;
}

@ Nagelik
Im Moment kann ich Flutter nicht aktualisieren, um Ihnen zu helfen

Ich frage mich, warum die anderen vom Flutter-Team implementierten Flutter-Tools (wie zum Beispiel Devtools) diesen Blocker nie treffen.
Sie scheinen nur Fehler zu beheben, wenn sie von ihnen blockiert werden.

Dies ist keine schwerwiegende neue Funktion, sondern ein Framework-Blocker.
Laut diesem https://github.com/flutter/flutter/issues/18761#issuecomment -639248761 sollte es also ein P3 sein

Ich frage mich, warum die anderen vom Flutter-Team implementierten Flutter-Tools (wie zum Beispiel Devtools) diesen Blocker nie treffen.
Sie scheinen nur Fehler zu beheben, wenn sie von ihnen blockiert werden.

Um ehrlich zu sein, weil ich denke, dass die Auswirkungen dieses Problems in realen Apps überschätzt werden. Ich beobachte das Problem nur, weil es die Dinge einfacher machen würde, aber es gibt "Problemumgehungen". Und viele Apps und Plugins in der Produktion verwenden Hintergrundfunktionen.

Ich bin mir auch nicht sicher, ob einige der +1 einfach nicht ganz verstehen, worum es bei dem Problem geht. (Wie https://github.com/flutter/flutter/issues/13937#issuecomment-635683123 ). Imo fehlt die offizielle Dokumentation für Hintergrundisolationen und Headless-Laufzeit (und wie man sie verwaltet) noch ein bisschen und das Firebase-Messaging-Zeug ist ein Albtraum, aber das hat nichts mit dem Framework, der Engine 🤷‍♂️ zu tun

Hören Sie vielleicht einfach auf, das Flutter-Team zu verprügeln, es gibt erstaunliche Fortschritte, und das ist nichts, was nicht umgangen werden kann ... Und wenn es eine PR gibt, warum reichen Sie sie nicht einfach ein, damit das Flutter-Team darüber nachdenkt 🤔

Es gibt ein Problem bei der Implementierung dieses Verhaltens für Isolate.

Beispiel:

Es gibt ein Hauptisolat und einen Motor.
Wenn das Isolat etwas braucht, fragt es die Engine.
Wenn der Motor etwas braucht, fragt er das Isolieren.

Jetzt ist unsere Situation:
Es gibt 2 Isolate und einen Motor.
Wenn das Isolat etwas braucht, fragt es die Engine.
Wenn der Motor etwas braucht, was soll er tun?
Beide fragen? Dann die wessen Antwort nehmen?
Oder fragen Sie nur ein Isolat? Welche dann?

@hpoul Ich muss zustimmen, dem nicht zuzustimmen, da viele Leute hier Anfänger in der Android/IOS-Programmierung sind und ihr erstes Framework für die App-Entwicklung als Flattern gewählt haben.

Daher ist es wichtig, dass jemand dies erkennt und eine offizielle Problemumgehung mit einem Beispiel für diese Art von Problemen behebt oder vorschlägt, bei denen Sie durch das Framework blockiert und gezwungen werden, komplexe "Problemumgehungen" durchzuführen, da Anfänger offensichtlich nicht wissen, was sie lernen sollen oder sogar wo ich anfangen soll, denn es gibt keine gut erklärten Problemumgehungen in den Kommentaren hier oder irgendwo auf Stackoverflow oder anderen Blogs. Es gibt nur Hinweise auf einige Pakete, die dies möglicherweise korrekt implementiert haben oder nicht.

Leute, die viel Erfahrung in der Arbeit mit Diensten, Warteschlangen für Hintergrundaufgaben usw. haben, sowohl in Android als auch in iOS, die dies leicht umgehen können, sind nicht die Zielgruppe für Flattern.

Und was Off-Topic-Kommentare wie diesen angeht, bin ich sicher, dass es in jeder anderen Ausgabe falsche +1 gibt, was die Reihenfolge der Prioritäten der Ausgabe nicht wesentlich ändert, und diese Ausgabe bleibt immer noch an der Spitze.

Hallo @phanirithvij

flatter_isolate wurde oben verlinkt und gibt es seit Februar 2019. Es ist auch nicht komplex. Ich kopiere die README, die einfach erscheint:

FlutterIsolate ermöglicht die Erstellung eines Isolates in Flutter, das Flutter-Plugins verwenden kann. Es erstellt die notwendigen plattformspezifischen Bits (FlutterBackgroundView auf Android und FlutterEngine auf iOS), damit die Plattformkanäle innerhalb eines Isolats funktionieren können.

| | Android | iOS | Beschreibung |
| :--------------- | :----------------: | :-----------------: | :------------------------------- |
| FlutterIsolate.spawn(Einstiegspunkt,Nachricht) | :white_check_mark: | :white_check_mark: | erzeugt ein neues FlutterIsolate |
| flatternIsolate.pause() | :white_check_mark: | :white_check_mark: | pausiert ein laufendes isolieren |
| flatterIsolate.resume() | :white_check_mark: | :white_check_mark: | fortgesetzt eine angehaltene isoalte |
| flatterIsolate.kill() | :white_check_mark: | :white_check_mark: | tötet ein ein isolieren |

Ich sage einfach, da diese Methoden dieselben Namen haben wie die in der "ursprünglichen" Isolate -Klasse. Wenn Sie also bereits wissen, wie man Isolate aus der offiziellen Dokumentation verwendet, sollte es nicht schwierig sein, die Verwendung zu verstehen dies als Drop-In-Ersatz, zumindest für die oben aufgeführten Methoden.

(Hinweis: Auch ich möchte, dass dieses Problem offiziell behoben wird, aus Gründen, die ich bereits in einem früheren Kommentar genannt habe, von denen ich einige mit Ihnen teile, aber ich würde diese Problemumgehung auch nicht als „komplex“ bezeichnen.)

Als Antwort auf den Kommentar von @nikitadol erstellt flatter_isolate eine separate Engine für jedes Isolat, damit es nicht auf dasselbe Problem stößt. Natürlich ist es nicht ideal, pro Isolat eine neue Engine erstellen zu müssen, das ist einer der Gründe, warum ich sicher bin, dass das Flutter-Team einen besseren Job machen könnte. In ähnlicher Weise würde die alternative Lösung, alle Plugin-Methodenaufrufe durch das Hauptisolat zu leiten, einen Engpass schaffen, der den Zielen von Isolaten überhaupt zuwiderläuft, also bin ich sicher, dass das Flutter-Team auch das besser machen könnte.

Ich stimme denen zu, die gesagt haben, dass das Flutter-Team Probleme mit höherer Priorität haben könnte, die zuerst angegangen werden müssen, aber basierend auf dem, was wir zuvor mit der Plugin-Architektur gesehen haben, kann es auch sein, dass die Behebung dieses Problems eine größere architektonische Änderung und mehrere andere offene Probleme erfordert erfordern auch eine größere architektonische Änderung, und das Abwägen all dieser unterschiedlichen Anforderungen kann einige Zeit dauern, bis sie überhaupt dazu kommen, dieses spezielle Problem anzugehen.

@ryanheise

flutter_isolate erstellt eine separate Engine für jedes Isolat

Ich bin mir also sicher, dass das Flutter-Team das auch besser machen könnte.

Sie sagen, das Flutter-Team kann das, aber wie?
Die Fragen, die ich oben gestellt habe, bleiben gültig.
Wenn die Lösung darin besteht, eine neue Engine zu erstellen, können Sie einfach Ihr Plugin verwenden

(Anmerkung, flutter_isolate ist nicht mein Plugin, aber da ich diese Funktion auch brauchte, habe ich mich entschieden, zu dem Projekt beizutragen.)

Was ich mit "einem besseren Job" meinte, ist, dass es wahrscheinlich nicht notwendig ist, ALLE Maschinen in einer FlutterEngine hochzufahren, nur um mit Plugins zu kommunizieren, aber im Moment ist die notwendige Infrastruktur vollständig an die Engine gebunden, deshalb derzeit flutter_isolate muss eine ganze Engine hochfahren, nur um Zugriff auf die Plugins zu erhalten.

(Anmerkung, flutter_isolate ist nicht mein Plugin, aber da ich diese Funktion auch brauchte, habe ich mich entschieden, zu dem Projekt beizutragen.)

Entschuldigung, ich habe nicht bemerkt, dass dies eine Gabelung ist

flutter_isolate muss eine ganze Engine hochfahren, nur um Zugriff auf die Plugins zu erhalten.

Wenn Sie angeben, dass 'is_background_view = true' ist, beginnt die Engine nicht mit dem Rendern - was bedeutet, dass die Engine nicht vollständig startet

Die genaue API zum Ausführen von Headless unterscheidet sich für iOS und Android v1 und Android v2, aber im Wesentlichen ist dies bereits das, was flutter_isolate tut. Es gibt jedoch immer noch einen Unterschied im Overhead zwischen einem normalen Isolat und einer FlutterEngine, also wäre es nur etwas, das das Flutter-Team tun könnte, um den Overhead weiter zu senken. Es ist nicht nur ein Overhead in der Startzeit, sondern auch ein Overhead in der Speichernutzung.

Sie schlagen also vor, die Erstellung einer neuen Engine so zu optimieren, dass sie im Hintergrund arbeitet?

Dann trifft dies wahrscheinlich nicht auf dieses Problem zu

Ich schlage das nicht vor, ich weise nur darauf hin, dass der Flutter_isolate-Workaround diese Einschränkung aufgrund des Overheads hat, der nicht vermieden werden kann. Ich schlage nicht vor, dass das Flutter-Team versuchen sollte, die Problemumgehung effizienter zu gestalten, ich denke, dass es idealerweise eine architektonische Änderung an der Art und Weise geben würde, wie Plugins geladen werden, sodass es nicht notwendig wäre, eine FlutterEngine zu instanziieren.

Wenn der Motor 1 ist und die Isolate größer als 1 sind, kehren wir zu dieser Frage zurück:

https://github.com/flutter/flutter/issues/13937#issuecomment -667314232

Ich versuche nicht, tatsächlich eine Lösung für diese neue Leichtbauarchitektur zu finden, aber wenn Sie mich fragen, fallen mir zwei Varianten von Leichtbaulösungen ein:

  1. Machen Sie Methodenkanäle ein bisschen wie Server-Sockets, indem Sie sie Verbindungen von mehreren Clients akzeptieren lassen. Ein Methodenkanal auf der Plattformseite des Plugins könnte also Verbindungen von mehreren Isolaten akzeptieren und dann, sobald er verbunden ist, Nachrichten an das richtige Isolat senden.
  2. Erstellen Sie eine neue Plugin-Registrierung usw. für jedes Isolat, ABER innerhalb derselben FlutterEngine,

wenn du mich fragst

Ja, ich frage

könnten Nachrichten an das richtige Isolat senden.

Ich denke, das ist eine schlechte Lösung, da „zum richtigen Isolat“ ein relativer Begriff ist

Erstellen Sie eine neue Plugin-Registrierung usw. für jedes Isolat, ABER innerhalb derselben FlutterEngine,

Dies könnte eine gute Option sein, aber nur, wenn die Engine Plugins immer selbst registriert.

Dies passiert aber nur, wenn alle Plugins im GeneratedPluginRegistrant.
https://github.com/flutter/engine/blob/f7d241fd8a889fadf8ab3f9d57918be3d986b4be/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L330 -L344

Das Erstellen zusätzlicher Rückrufe ist kein offensichtliches Verhalten.

Ich denke, das ist eine schlechte Lösung, da „zum richtigen Isolat“ ein relativer Begriff ist

Es könnte eine schlechte Lösung sein, aber deswegen kann es nicht schlecht sein. Das "richtige Isolat" ist eindeutig, genauso wie ein Server-Socket Client-Verbindungen eindeutig handhabt.

Dies könnte eine gute Option sein, aber nur, wenn die Engine Plugins immer selbst registriert.

Was gut wäre, um sicherzustellen, dass dies immer geschieht. Derzeit ist der Mechanismus etwas flockig.

Es könnte eine schlechte Lösung sein, aber deswegen kann es nicht schlecht sein. Das "richtige Isolat" ist eindeutig, genauso wie ein Server-Socket Client-Verbindungen eindeutig handhabt.

Dann muss jedes Plugin berücksichtigen, dass es von verschiedenen Isolaten aus angesprochen werden kann - schlecht

Ja.

Lassen Sie mich zu dieser Diskussion meinen Senf beitragen ... Nachdem ich jetzt seit etwa zwei Jahren kommerziell mit Flutter gearbeitet habe, war das Wichtigste, was ich gelernt habe, dass Flutter ein UI-Toolkit ist . Diese Aussage wäre nicht überraschend - das steht sogar auf der Flutter-Hauptwebsite -, wenn die Leute dies nicht vergessen würden.

Flutter ist ein UI-Toolkit, und was es gut macht, ist UI. Wenn Sie versuchen, eine CPU-lastige Anwendung zu erstellen, die auf teurer Datenverarbeitung beruht oder komplizierte Algorithmen verwendet und große Datenmengen verbraucht, sollten Sie für diesen Zweck KEIN UI-Toolkit verwenden. Geschäftslogik sollte nicht mit der Benutzeroberfläche gemischt werden.

Natürlich können wir mit Dart viel mehr als nur UI machen, aber vergessen wir nicht den Hauptzweck dieses Toolkits.

Ich habe eine Reihe von Ansätzen ausprobiert, einschließlich der Verwendung von kopflosem Flutter - was sich als totale Katastrophe herausstellte. Nicht, weil es nicht funktioniert, sondern weil wir Probleme haben, anderen Entwicklern zu erklären, wie es funktioniert. Der damit verbundene Wartungsaufwand ist es einfach nicht wert. Ganz zu schweigen davon, eine solche Lösung zu testen. Das letzte Mal, als ich es versuchte, kam Flutter Driver überhaupt nicht damit klar.

Wenn Ihre Anwendung wirklich die Rechenleistung benötigt, haben Sie meiner Meinung nach drei Möglichkeiten:

  • Verschieben Sie Ihre Logik auf den Server;
  • Verschieben Sie Ihre Logik auf die native Plattform – Sie können dafür Kotlin Native verwenden – und verwenden Sie Ereignis-/Methodenkanäle, um nur die Ansichtsmodelle für Ihre Benutzeroberfläche bereitzustellen (ich denke, das hat OLX getan: https://tech.olx.com/fast -Prototypen-mit-Flattern-kotlin-native-d7ce5cfeb5f1);
  • Verwenden Sie Flutter nicht und suchen Sie nach einem anderen Framework (Xamarin/Blazor könnte dafür gut geeignet sein, da C# eine anspruchsvollere Multithreading-Umgebung als Dart bietet).

Alle oben genannten haben ihre Mängel, aber der Versuch, eine Bibliothek/ein Framework zu erweitern, um das zu tun, wofür es nicht wirklich entworfen wurde, ist auch nicht ideal.

Hätte der isolation_handler dieses Problem nicht gelöst?

@hasonguo Es verwendet flutter_isolate im Hintergrund, es fügt nur eine zusätzliche Ebene hinzu, um die Handhabung von Isolaten (eigentlich alle Isolate) zu vereinfachen, indem die Boilerplate für die Kommunikation hinzugefügt wird, sodass Sie sie nicht jedes Mal manuell einrichten müssen Zeit. Früher wurde das Problem auf andere Weise angegangen, aber das wurde durch Änderungen in Flutter selbst unmöglich gemacht und stattdessen auf flutter_isolate gesetzt.

Wir drehen uns im Kreis. Ja, es gibt Lösungen, eigentlich beide Plugins. Wie Ryan betonte, gibt es einen Overhead, der damit verbunden ist, die Kanalkommunikation zu ermöglichen. Höchstwahrscheinlich ist es genau dieser Overhead, der das Flutter-Team davon abgehalten hat, es automatisch zum ursprünglichen Isolate hinzuzufügen, da Sie die Unterstützung in vielen (den meisten?) Fällen nicht benötigen.

Trotzdem ist es keine so saubere Lösung, wie sie werden könnte. Wenn das Flutter-Team in der Lage wäre, Kanalkommunikation ohne Overhead, direkt und automatisch direkt aus der Box mit einem einfachen Isolate bereitzustellen, wäre die ganze Diskussion strittig. Aber auch das Gegenteil gilt: Sie streben den Wandel nicht wirklich um des Wandels willen an. Wenn es eine praktikable Lösung in einem Paket oder Plugin gibt, warum sollte man dann Zeit aufwenden und dem Kern mehr Gewicht verleihen?

Erstellen Sie eine neue Plugin-Registrierung usw. für jedes Isolat, ABER innerhalb derselben FlutterEngine,

Dies könnte eine gute Option sein, aber nur, wenn die Engine Plugins immer selbst registriert.

Dies passiert aber nur, wenn alle Plugins im GeneratedPluginRegistrant.
https://github.com/flutter/engine/blob/f7d241fd8a889fadf8ab3f9d57918be3d986b4be/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L330 -L344

Das Erstellen zusätzlicher Rückrufe ist kein offensichtliches Verhalten.

Eine weitere Verbesserung dieser zweiten Idee besteht darin, Plugins träge in das neue Isolat zu laden. Wenn ein Projekt von vielen Plugins abhängt, wird das GeneratedPluginRegistrant normalerweise recht groß sein und je nachdem, wie effizient die Initialisierungsroutine eines Plugins ist, kann dies zu einigen Overheads beitragen. Wie man das genau umsetzt, ist eine andere Geschichte. Plattformkanäle sind nicht im Besitz von Plugins, daher kann das Senden einer Nachricht über einen Kanal derzeit nicht verwendet werden, um die Instanziierung der Plattformseite eines Plugins auszulösen. Daher müssten entweder Methodenkanäle mit Plugins verknüpft werden, oder es müsste ein zusätzlicher Mechanismus vorhanden sein, den die Dart-Seite eines Plugins aufrufen könnte, um sich selbst träge zu initialisieren, bevor der erste Methodenkanal erstellt wird, und dieser Mechanismus könnte die Instanziierung auslösen der Plattformseite dieses Plugins innerhalb des Isolats.

@TahaTesser

In Ihrem Beispiel bezieht sich der Fehler nicht auf dieses Problem

E/flutter ( 7814): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Invalid argument(s): Isolate.spawn expects to be passed a static or top-level function

Hallo @nikitadol
Danke, aktualisiertes Beispiel, es löst keine Ausnahme aus, ist das richtig? (musste ich vorher nie benutzen)
Können Sie bitte ein vollständig reproduzierbares Minimalcodebeispiel bereitstellen
Danke schön

@TahaTesser


Codebeispiel

import 'package:battery/battery.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

Future<void> main() async {
  await printBatteryLevel();
  await compute(printBatteryLevel, null);
}

Future<void> printBatteryLevel([dynamic message]) async {
  WidgetsFlutterBinding.ensureInitialized();
  print(await Battery().batteryLevel);
}



Protokolle

Launching lib/main.dart on Pixel 4 in debug mode...
Running Gradle task 'assembleDebug'...
✓ Built build/app/outputs/flutter-apk/app-debug.apk.
Installing build/app/outputs/flutter-apk/app.apk...
Waiting for Pixel 4 to report its views...
Debug service listening on ws://127.0.0.1:50709/-SPs_6AmL2Q=/ws
Syncing files to device Pixel 4...
I/flutter ( 8708): 39
E/flutter ( 8708): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Exception: UI actions are only available on root isolate.
E/flutter ( 8708): #0      Window._nativeSetNeedsReportTimings (dart:ui/window.dart:1003:86)
E/flutter ( 8708): #1      Window.onReportTimings= (dart:ui/window.dart:996:29)
E/flutter ( 8708): #2      SchedulerBinding.addTimingsCallback (package:flutter/src/scheduler/binding.dart:272:14)
E/flutter ( 8708): #3      SchedulerBinding.initInstances (package:flutter/src/scheduler/binding.dart:209:7)
E/flutter ( 8708): #4      ServicesBinding.initInstances (package:flutter/src/services/binding.dart:27:11)
E/flutter ( 8708): #5      PaintingBinding.initInstances (package:flutter/src/painting/binding.dart:23:11)
E/flutter ( 8708): #6      SemanticsBinding.initInstances (package:flutter/src/semantics/binding.dart:24:11)
E/flutter ( 8708): #7      RendererBinding.initInstances (package:flutter/src/rendering/binding.dart:32:11)
E/flutter ( 8708): #8      WidgetsBinding.initInstances (package:flutter/src/widgets/binding.dart:257:11)
E/flutter ( 8708): #9      new BindingBase (package:flutter/src/foundation/binding.dart:59:5)
E/flutter ( 8708): #10     new _WidgetsFlutterBinding&BindingBase&GestureBinding (package:flutter/src/widgets/binding.dart)
E/flutter ( 8708): #11     new _WidgetsFlutterBinding&BindingBase&GestureBinding&SchedulerBinding (package:flutter/src/widgets/binding.dart)
E/flutter ( 8708): #12     new _WidgetsFlutterBinding&BindingBase&GestureBinding&SchedulerBinding&ServicesBinding (package:flutter/src/widgets/binding.dart)
E/flutter ( 8708): #13     new _WidgetsFlutterBinding&BindingBase&GestureBinding&SchedulerBinding&ServicesBinding&PaintingBinding (package:flutter/src/widgets/binding.dart)
E/flutter ( 8708): #14     new _WidgetsFlutterBinding&BindingBase&GestureBinding&SchedulerBinding&ServicesBinding&PaintingBinding&SemanticsBinding (package:flutter/src/widgets/binding.dart)
E/flutter ( 8708): #15     new _WidgetsFlutterBinding&BindingBase&GestureBinding&SchedulerBinding&ServicesBinding&PaintingBinding&SemanticsBinding&RendererBinding (package:flutter/src/widgets/binding.dart)
E/flutter ( 8708): #16     new _WidgetsFlutterBinding&BindingBase&GestureBinding&SchedulerBinding&ServicesBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding (package:flutter/src/widgets/binding.dart)
E/flutter ( 8708): #17     new WidgetsFlutterBinding (package:flutter/src/widgets/binding.dart)
E/flutter ( 8708): #18     WidgetsFlutterBinding.ensureInitialized (package:flutter/src/widgets/binding.dart:1229:7)
E/flutter ( 8708): #19     printBatteryLevel (package:bug/main.dart:11:25)
E/flutter ( 8708): #20     _IsolateConfiguration.apply (package:flutter/src/foundation/_isolates_io.dart:83:34)
E/flutter ( 8708): #21     _spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:90:65)
E/flutter ( 8708): #22     Timeline.timeSync (dart:developer/timeline.dart:163:22)
E/flutter ( 8708): #23     _spawn (package:flutter/src/foundation/_isolates_io.dart:87:35)
E/flutter ( 8708): #24     _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:304:17)
E/flutter ( 8708): #25     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
E/flutter ( 8708): 



Flatterarzt -v

[✓] Flutter (Channel stable, 1.20.4, on Mac OS X 10.15.6 19G2021, locale en-BY)
    • Flutter version 1.20.4 at /Users/nikitadold/development/flutter
    • Framework revision fba99f6cf9 (2 weeks ago), 2020-09-14 15:32:52 -0700
    • Engine revision d1bc06f032
    • Dart version 2.9.2

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
    • Android SDK at /Users/nikitadold/Library/Android/sdk
    • Platform android-30, build-tools 30.0.0
    • Java binary at: /Users/nikitadold/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/193.6626763/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 12.0.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.0.1, Build version 12A7300
    • CocoaPods version 1.9.3

[!] Android Studio (version 4.0)
    • Android Studio at /Users/nikitadold/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/193.6626763/Android Studio.app/Contents
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)

[✓] IntelliJ IDEA Ultimate Edition (version 2020.2.2)
    • IntelliJ at /Users/nikitadold/Applications/JetBrains Toolbox/IntelliJ IDEA Ultimate.app
    • Flutter plugin installed
    • Dart plugin version 202.7319.5

@TahaTesser
siehe bitte diesen Kommentar

Plattformnachrichten werden nur vom Hauptisolat unterstützt. [...]

Dieses Verhalten ist _erwartet_ und hat die Bezeichnung severe: new feature
Dieses Problem sollte als _Funktionsanfrage_ oder _Vorschlag_ betrachtet werden und nicht als _Bug_

@iapicca

In Ihrem Beispiel bezieht sich der Fehler nicht auf dieses Problem

E/flutter (23174): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message : (object is a closure - Function '<anonymous closure>':.)

@iapicca

In Ihrem Beispiel bezieht sich der Fehler nicht auf dieses Problem

E/flutter (23174): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message : (object is a closure - Function '<anonymous closure>':.)

Sie haben Recht, meins ist kein gutes Beispiel, bitte ignorieren Sie es

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen