Flutter: Tidak dapat memanggil metode saluran platform dari isolat lain

Dibuat pada 5 Jan 2018  ·  133Komentar  ·  Sumber: flutter/flutter

Ketika saya mencoba untuk memanggil metode saluran platform dari isolat yang dibuat khusus, aplikasi macet parah (baik di iOS dan Android). Saya mencoba mencari tahu apakah ini diharapkan atau tidak.

Jika tidak, mungkin perlu disebutkan di suatu tempat.

Bagaimanapun saya pikir ini berpotensi menjadi batasan yang kuat. Apakah ada cara untuk dapat memanggil plugin platform dari isolat sekunder?

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

Komentar yang paling membantu

Saya memiliki berbagai kasus penggunaan, misalnya:

  • Unduh dan urai sebagian besar data (metode dart:convert tidak asinkron), lalu simpan ke sqlite db menggunakan plugin sqflite (yang menggunakan binding platform);
  • Mengambil dan mengurai data sebelum menyajikannya ke ui;
  • Enkripsi/dekripsi dan baca/tulis data dari/ke file (hal kripto dilakukan melalui saluran metode untuk menggunakan lib keamanan platform);

Secara umum dapat terjadi bahwa Anda menggunakan dependensi yang mendeklarasikan beberapa metode publik yang Anda tidak benar-benar tahu apakah pada akhirnya akan menggunakan kode khusus platform untuk menyelesaikan hal-hal mereka (misalnya saya sedang memikirkan flutterfire); menggunakannya imho harus lebih merupakan detail implementasi yang dapat berubah seiring waktu alih-alih sesuatu yang ditulis di atas batu.

Solusi Anda tampaknya baik-baik saja untuk saat ini dan relatif tidak menyakitkan untuk diterapkan, karena data sudah dikodekan dengan cara untuk diteruskan ke saluran metode, dan untuk alasan ini akan dengan mudah juga melalui port yang terisolasi, namun dugaan saya adalah bahwa kinerjanya akan menjadi suboptimal .

Namun saya agak macet selama implementasi: dapatkah Anda memberikan contoh cara mudah untuk memanggil metode statis pada isolat utama dari isolat sekunder?

Terima kasih

Semua 133 komentar

cc @mravn-google untuk triase

Kode mesin dereferences null dan crash ketika mencoba untuk mengirim pesan platform dari isolat sekunder. Saya belum menunjukkan dengan tepat di mana; Saya mendapatkan batu nisan, tetapi perlu belajar bagaimana menafsirkan hal seperti itu.

Sebagai solusi (canggung), isolat sekunder dapat meminta isolat utama untuk mengirim pesan.

@sroddy Bolehkah saya bertanya apa yang ingin Anda capai dengan isolat sekunder?

Saya memiliki berbagai kasus penggunaan, misalnya:

  • Unduh dan urai sebagian besar data (metode dart:convert tidak asinkron), lalu simpan ke sqlite db menggunakan plugin sqflite (yang menggunakan binding platform);
  • Mengambil dan mengurai data sebelum menyajikannya ke ui;
  • Enkripsi/dekripsi dan baca/tulis data dari/ke file (hal kripto dilakukan melalui saluran metode untuk menggunakan lib keamanan platform);

Secara umum dapat terjadi bahwa Anda menggunakan dependensi yang mendeklarasikan beberapa metode publik yang Anda tidak benar-benar tahu apakah pada akhirnya akan menggunakan kode khusus platform untuk menyelesaikan hal-hal mereka (misalnya saya sedang memikirkan flutterfire); menggunakannya imho harus lebih merupakan detail implementasi yang dapat berubah seiring waktu alih-alih sesuatu yang ditulis di atas batu.

Solusi Anda tampaknya baik-baik saja untuk saat ini dan relatif tidak menyakitkan untuk diterapkan, karena data sudah dikodekan dengan cara untuk diteruskan ke saluran metode, dan untuk alasan ini akan dengan mudah juga melalui port yang terisolasi, namun dugaan saya adalah bahwa kinerjanya akan menjadi suboptimal .

Namun saya agak macet selama implementasi: dapatkah Anda memberikan contoh cara mudah untuk memanggil metode statis pada isolat utama dari isolat sekunder?

Terima kasih

@sroddy Terima kasih telah memberikan informasi latar belakang.

Tentang menjalankan metode statis pada isolat utama: tidak didukung secara langsung dan harus diimplementasikan menggunakan port. Paket ini mungkin menyediakan fungsionalitas itu, misalnya di sini . Padahal saya sendiri belum mencobanya.

Tetapi kita harus mencari solusi yang lebih umum di sini, solusi yang juga berfungsi ketika komunikasi saluran platform dilakukan sebagai bagian dari implementasi plugin atau pustaka lainnya.

Kami belum cukup memahami hal ini, tetapi jika kami dapat berasumsi sejenak bahwa Anda memiliki serangkaian nama saluran yang diketahui yang akan digunakan dari isolat sekunder dan bukan isolat utama, Anda seharusnya dapat secara umum mengonfigurasi ulang pesan platform yang menangani diri Anda sendiri untuk saluran tersebut, secara transparan menerapkan penerusan pesan biner dan balasan yang diperlukan antara dua isolat Anda. Plugin tidak akan pernah tahu bedanya.

Solusi digambarkan di bawah ini.

Misalkan Anda mengatur port M1 dan R1 pada isolat utama Anda dan M2, R2 pada isolat sekunder Anda. M untuk pesan, R untuk balasan.

  • Di isolat sekunder Anda, gunakan BinaryMessages.setMockMessageHandler untuk setiap saluran guna meneruskan pesan untuk platform ke M1 (secara transparan ke plugin yang menggunakan BinaryMessage.send dengan saluran itu). Simpan panggilan balik balasan dan konfigurasikan R2 dengan penangan yang memanggil yang benar saat menerima balasan untuk pesan tersebut. Konfigurasikan M2 dengan handler yang meneruskan ke BinaryMessages.handlePlatformMessage . Terapkan panggilan balik balasan di sana untuk meneruskan balasan ke R1.
  • Secara simetris di isolat utama Anda. Konfigurasikan M1 dengan handler yang meneruskan pesan untuk platform ke BinaryMessages.send . Setel panggilan balik balasan yang meneruskan balasan dari platform ke R2. Panggil juga BinaryMessages.setMessageHandler untuk setiap saluran untuk mengatur penangan pesan masuk dari platform yang meneruskan ke M2. Simpan panggilan balik balasan dan konfigurasikan R1 dengan penangan yang memanggil yang benar saat menerima balasan dari isolat sekunder.

@Hixie Saya sarankan pindah ke tonggak berikutnya. Solusi tampaknya ada. Solusi yang baik akan membutuhkan beberapa pekerjaan desain dan iterasi API.

Saya mendapat masalah yang sama, apakah ada berita tentang masalah ini?

@mravn-google Apakah Anda memiliki pembaruan tentang ini? Saya mengalami masalah yang sama (saya perlu menghasilkan misalnya pasangan kunci RSA 2048 di sisi platform yang membutuhkan waktu beberapa saat pada perangkat yang lebih lama atau data crypt/decrypt). Saya lebih suka menghindari membuat utas atau layanan karena itu akan menggandakan pekerjaan saya di sisi platform karena akan diimplementasikan untuk platform Android dan iOS. Apakah kami memiliki cara mudah untuk menjalankan operasi khusus platform tersebut secara asinkron dari Dart? Solusi Anda tampaknya baik-baik saja, namun tampaknya saya memiliki beberapa masalah dengan penerapannya karena saya cukup baru di Flutter dan Dart (Saya tidak yakin bagaimana mengonfigurasi penangan yang meneruskan pesan untuk Isolate dan sebagainya), maaf tentang itu.

Saya mendapat masalah yang sama.

Saya perlu membuat PDF di sisi Android/iOS yang memakan waktu cukup lama dan metode compute(...) membuat aplikasi mogok jika saya menjalankan panggilan dari sana.

Saya mendapatkan masalah ini, dan sekarang saya terjebak.

Saya ingin membaca accessToken, dari preferensi bersama, di isolat latar belakang, meskipun aplikasi tidak di latar depan (isolasi utama mungkin tidak berjalan, saya tidak begitu yakin).

Jadi, dapatkah seseorang memberi tahu saya solusi untuk membaca data yang bertahan dari isolat latar belakang.
Aku benar-benar dalam masalah sekarang.

/cc @bkonyi Sepertinya ini terkait dengan sesuatu yang sedang Anda kerjakan.

Saya mendapat masalah yang sama. basis produk kami pada CPU-terikat. kita perlu menggunakan metode platform secara terpisah. Bisakah Anda memberi tahu kami cara lain untuk menyelesaikan masalah ini?

@Thomson-Tsui Saya memiliki masalah yang sama, dan berdasarkan distilasi karya @bkonyi dan sampel FlutterGeofencing-nya, saya berhasil membuat sesuatu berfungsi dalam isolat yang saya gunakan dengan flutter_blue dan plugin lainnya. Ini cukup belum teruji tetapi silakan mencobanya.

Saya mendapat masalah yang sama, saya pikir itu penting

Apakah ada yang memecahkan masalah ini?

Saya perlu memutar audio pada interval yang berbeda-beda di latar belakang. Cara paling sederhana untuk melakukannya adalah dengan memiliki Isolate terpisah yang mengelola audio, tetapi tanpa fitur ini akan membutuhkan beberapa pekerjaan yang sangat aneh dengan port

Saya memiliki asumsi bahwa ketika Anda menjalankan isolat dari dart, perpustakaan asli tidak menautkan.

Misalnya, saat menjalankan kode flutter dari Java, metode ini disebut:

   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
   );

Di mana semua keajaiban terjadi)

Ini diverifikasi oleh fakta bahwa jika Anda membuat beberapa FlutterNativeView, dan di setiap proses:

  public void runFromBundle (FlutterRunArguments args)

lalu kita mendapatkan beberapa "isolasi panah"

Kami juga menghadapi masalah ini. Kita perlu mengumpulkan data dari isolat latar belakang (misalnya lokasi, kebisingan, dll.) yang semuanya bergantung pada saluran platform. @mravn-google -- adakah berita tentang pembaruan API yang dapat mengatasi hal ini?

Ini benar-benar masalah serius dalam aplikasi alat atau media.
Utas platform bukanlah cara yang mudah.

Saya juga menghadapi masalah serupa dan macet sekarang.

+1 (proyek saya juga sangat membutuhkan saluran platform dalam isolasi)

Saya perlu membuat gambar di latar belakang. Itu harus dilakukan secara terpisah karena setiap gambar membutuhkan waktu beberapa detik dan memblokir ui jika dilakukan di utas utama. Saya terjebak pada panggilan asli pertama dart:ui.PictureRecorder dan menganggap setiap panggilan grafis (yang menggunakan fungsi asli) juga tidak akan berfungsi. Karena saya menggunakan banyak fungsi di kanvas, akan merepotkan untuk bekerja dengan port panggilan balik.

Sebuah solusi sangat dihargai.

+1 Saya mendapatkan masalah ini

Sepertinya @mravn-google telah berhenti bekerja untuk Google di Aarhus. Saya ingin tahu apakah ada orang lain dari tim Google Flutter yang menyelidiki ini. IMO, ini adalah show stopper utama untuk penggunaan Flutter yang serius untuk membangun aplikasi Flutter yang tangguh dan tangguh....

Mungkin @sethladd bisa kasih status?

+1

ada update tentang ini?

+1

Pada hari Anda bertemu bug yang Anda tahu cara mengatasinya sendiri tetapi hanya menunggu adalah harapan terakhir sampai saya menjadi insinyur flutter.

Kami membutuhkan penjelasan yang lebih rinci tentang solusi untuk pengguna flutter/dart baru. Saya baru mulai menggunakan isolat dua hari yang lalu, dan flutter/dart minggu lalu, dan sepertinya saya harus menggunakan utas utama untuk beberapa tugas padat karya yang serius, yang menjadikan pembelajaran flutter sebagai solusi yang buruk. Tanpa multithreading yang dapat digunakan, saya lebih baik mempelajari Kotlin dan membuat aplikasi dua kali. Saya harap Anda semua bisa mendapatkan sesuatu yang dapat dimengerti oleh pemula, saya benar-benar ingin dapat membenarkan pembelajaran flutter kepada majikan saya sehingga saya dapat menggunakannya di tempat kerja.
Jika tidak di sini, mungkin di StackOverflow, saya telah memposting pertanyaan di sini: https://stackoverflow.com/q/57466952/6047611

+1 semoga cepet beres

+1 Proyek saya membutuhkan saluran platform secara terpisah.

Saya mengalami masalah yang sama dan membuat paket ( Isolate Handler ) menggunakan solusi yang mirip dengan yang diposting oleh @mravn-google. Namun, Isolate Handler memungkinkan Anda untuk menggunakan saluran baik dari isolat utama bersama dengan isolat lainnya. Ini mendukung panggilan langsung MethodChannel dari dalam isolat, tetapi saat ini aliran EventChannel tidak didukung. Saya akan mempertimbangkan untuk menambahkan dukungan bagi mereka jika itu ternyata menjadi sesuatu yang dibutuhkan orang.

Satu kelemahan dari solusi ini adalah kebutuhan untuk menyediakan handler isolat dengan nama saluran. Ini bukan masalah dalam proyek saya karena saya menulis kode asli saya sendiri, tetapi jika tidak, Anda harus melihat melalui kode sumber plugin apa pun yang Anda gunakan untuk menemukannya.

Opsi lain yang sudah diposting di atas adalah FlutterIsolate , yang menggunakan pendekatan berbeda dan juga layak untuk dilihat.

Semoga pengembang Flutter akan segera memberikan solusi lengkap dan tepat untuk masalah ini.

Sunting:

Saya akhirnya memecahkan masalah saya dengan lebih bersih dengan memulai isolat dari sisi asli alih-alih menggunakan pendekatan yang mirip dengan plugin Pengelola Alarm Android pihak pertama Flutter.

Satu-satunya hal yang harus saya lakukan adalah mengubah kode asli saya menjadi plugin sehingga mereka dapat didaftarkan dengan aplikasi asli, yang merupakan migrasi yang relatif tidak menyakitkan. Jika Anda menggunakan plugin dari pub.dev, itu bahkan lebih sederhana karena mereka seharusnya bekerja dengan mulus.

+1 ada pembaruan di sini?

+1

Hal menarik yang saya perhatikan di sini:
Beberapa plugin tampaknya ditulis dengan asumsi bahwa panggilan saluran metode meneruskan pesan dari Dart ke utas latar belakang; terkait, ada beberapa orang yang sangat bingung yang mengikuti contoh geofencing bg itu dan sekarang bertanya-tanya bagaimana cara kembali ke utas utama ...
Kode plugin itu tidak akan menyebabkan "jank" jika mereka melakukan tugas singkat, tetapi mereka secara default berjalan di utas platform , yang dulu disebut "utas UI" di android; ini benar-benar dapat menyebabkan gerakan dijatuhkan selama beban kerja tinggi, dan saya mengerti itu akan memblokir semua saluran pesan lainnya juga.
Anda kemudian berada di bawah kekuasaan rekayasa pembuat plugin; hanya jika mereka memindahkan pekerjaan berat mereka ke utas lain dalam kode asli, Anda akan menjalankan kode yang menunggu di saluran metode; saluran pesan tersebut harus berjalan di utas platform.
Sementara saya memahami alasan yang baik chinmaygarde berhubungan di sini , menyetujui bahwa:

  • sebagian besar panggilan API platform cepat dan sering kali perlu dilakukan di utas utama;
  • lebih masuk akal untuk memperhitungkan utas dalam kode asli
  • menemukan jalan kembali ke "utas utama" dalam penggantian metode kerangka kerja jelas membingungkan orang yang terbiasa dengan Android Java

    • Saya hanya bisa membayangkan bagaimana itu akan berlipat ganda ketika melakukan panggilan API platform-thread-only pertama mereka.

Saya merasa bahwa arsitektur yang dihasilkan membuka anti-pola yang signifikan sebagai default untuk pembuat plugin; pada pandangan pertama, saya pikir bahkan plugin firebase ml menggunakan utas platform untuk pemrosesannya.
Jadi saya berharap ada semacam peringatan keras yang akan diberikan kepada orang-orang yang melakukan angkat berat; mungkin sesuatu di aplikasi contoh default yang menghasilkan kesalahan di layar jika panggilan saluran pesan membutuhkan waktu lebih lama dari ~20 mdtk untuk kembali, mungkin sesuatu yang lebih di-muka semua orang; jujur, apa pun yang meyakinkan orang-orang itu membuat panggilan db & c. untuk pergi mendapatkan utas sudah.

Oi, tidak ada makan siang gratis, itu pasti...

Saya akhirnya menerapkan beban kerja yang berat dengan utas platform asli menggunakan Java dan Swift. Tidak menemukan solusi.

@AndruByrne Ini memang informasi yang sangat menarik dan saya tidak menyadarinya, sejelas yang sekarang terlihat di belakang. Saya sudah memiliki satu masalah terkait plugin saya di mana pengguna mencoba menggunakannya sebagai cara untuk melakukan tugas angkat berat di latar belakang dan itu mengunci UI.

Saya setuju bahwa harus ada peringatan dan saya akan menambahkannya ke plugin saya.

+1 butuh ini

Sayangnya - Flutter belum siap untuk primetime karena kekurangan sesuatu yang sangat mendasar namun sangat kritis. Tidak semua aplikasi Flutter bisa lolos dengan async/menunggu sederhana. Untuk angkat berat Isolat sangat penting dan plugin yang tidak dapat mengizinkan panggilan metode saluran platform dari Isolate lain membuatnya tidak berguna dan berlebihan. Xamarin telah membangun ini ke dalam kerangka kerja sejak hari 1 - tentunya ini harus dipilih untuk solusi sekarang.

+1 butuh ini

+1

Saya perlu memanggil metode saluran platform dari isolat lain juga!

+1

+1

+1

+1 Ini harus dimiliki

+1
Saya memiliki SDK asli yang harus saya panggil. Akan menyenangkan untuk memiliki sesuatu seperti BackgroundFlutterMethodChannel .

+1

+1

Adakah yang punya informasi tentang apa yang sebenarnya dijelaskan _crashes parah_? Saya sedang mengerjakan bukti konsep yang mengimplementasikan perpustakaan asli dan saya mengalami waktu tunggu pengawas (0x8badf00d) dan kesalahan aneh lainnya saat mengirim pesan bolak-balik melalui saluran metode.

Saya menduga ini disebabkan oleh beberapa metode [dan hasil] yang dipanggil dari utas lain, tetapi saya belum dapat membuat kemajuan apa pun dalam menunjukkan dengan tepat masalah yang sebenarnya.

+1

+1

+1

+1

+1 butuh ini

+1 membutuhkan ini juga!

Saya pikir masalah ini harus diprioritaskan setidaknya P2.
Penggunaan saluran platform yang dapat saya pikirkan adalah

  • Untuk memanggil API khusus platform
  • Untuk menjalankan tugas berat khusus platform di latar belakang dan laporkan saat selesai

Dan ini adalah pemblokir utama.

Plugin flutter_downloader menangani kode isolasi latar belakang. Saya pikir itu layak untuk dilihat.

saya berharap ini diselesaikan di versi yang akan datang, tetapi bug masih ada di versi 1.12!

kode sisi klien kami banyak di sisi lain saluran dan ditulis dalam kotlin dan aplikasi hampir macet untuk setiap koneksi http tunggal

Gooooooooooooooo?!!!!!!!!!!!!?!?!?!?

Plugin ini mungkin membantu flutter_isolate

+1 kita membutuhkan ini ... ayolah

+1

Masalah terbesar bagi saya adalah fakta bahwa komunikasi antara Dart dan platform asli terjadi di utas utama - mengirim potongan data yang signifikan tidak mungkin tanpa tertinggal UI atau menulis kode rumit ke data halaman.

+1 butuh ini

menulis kode rumit ke data halaman.

@lukaszciastko apa yang Anda maksud dengan menulis kode ke data halaman. Apakah ada cara untuk mengatasi UI yang tertinggal saat mengirim potongan data yang signifikan??

@YaredTaddese

Bergantung pada kerumitan dari apa yang dilakukan ISOLATE Anda dan komunikasi antar UI dan ISOLATE - Anda dapat mengatasi masalah ini dalam kode UI Anda dengan menyusun data apa yang diperlukan ISOLATE untuk melakukan tugasnya terlebih dahulu sebelum menjalankan ISOLATE. Saya memiliki pembuatan PDF yang kaya di aplikasi Flutter saya saat ini - pengguna dapat memilih beberapa PDF untuk dibuat.

Masing-masing dapat menghasilkan dari 1 hingga 10.000 halaman. UI saya responsif, ketika setiap pekerjaan selesai, TAB dibuat dengan menampilkan laporan - pengguna mendapat pemberitahuan saat pekerjaan selesai melalui TOAST dan UI tidak berhenti - pengguna tidak menyadari bahwa ada layanan di latar belakang yang sibuk menghasilkan PDF yang kaya.

Catatan, generasi PDF saya adalah ISOLATE. Di ISOLATE -, saya juga perlu mengunduh gambar dari CLOUD FIRESTORE dan menyematkannya dalam PDF - ini semua terjadi dengan mulus.

@MsXam tapi saya tidak bisa memanggil fungsi khusus platform dari isolat lain...yang membuat saya memanggil fungsi khusus platform di isolat utama...yang mengarah ke UI yang tertinggal

Saya mendapat masalah yang sama, saya pikir itu sangat penting

@YaredTaddese

Bergantung pada kerumitan dari apa yang dilakukan ISOLATE Anda dan komunikasi antar UI dan ISOLATE - Anda dapat mengatasi masalah ini dalam kode UI Anda dengan menyusun data apa yang diperlukan ISOLATE untuk melakukan tugasnya terlebih dahulu sebelum menjalankan ISOLATE. Saya memiliki pembuatan PDF yang kaya di aplikasi Flutter saya saat ini - pengguna dapat memilih beberapa PDF untuk dibuat.

Masing-masing dapat menghasilkan dari 1 hingga 10.000 halaman. UI saya responsif, ketika setiap pekerjaan selesai, TAB dibuat dengan menampilkan laporan - pengguna mendapat pemberitahuan saat pekerjaan selesai melalui TOAST dan UI tidak berhenti - pengguna tidak menyadari bahwa ada layanan di latar belakang yang sibuk menghasilkan PDF yang kaya.

Catatan, generasi PDF saya adalah ISOLATE. Di ISOLATE -, saya juga perlu mengunduh gambar dari CLOUD FIRESTORE dan menyematkannya dalam PDF - ini semua terjadi dengan mulus.

Apakah Anda memanggil kode khusus platform?

Adakah yang punya solusi untuk memanggil saluran platform dari isolat ?itu mengalahkan ide untuk melakukannya dari utas UI.

Saya terjebak pada hal yang sama.

Saya memanggil saluran metode dengan kode kotlin, dan getStream.io API, tetapi itu sangat lambat, dan UI Flutter saya membeku, saya ingin menambahkan komputasi ke dalamnya, tetapi saya mendapatkan kesalahan yang sama.

Bagaimana saya bisa memperbaikinya?

Saya baru saja melewati ini juga. Saya membuat prototipe aplikasi baru, dan sebenarnya masalah ini mungkin menjadi penghalang bagi saya dan menyebabkan saya mencari solusi non-Flutter lainnya, yang akan membuat saya sedih.

Seperti yang ditunjukkan oleh volume respons terhadap masalah ini, ini adalah masalah UTAMA dan cukup banyak mengalahkan kasus penggunaan utama isolat dalam konteks aplikasi Flutter.

Saya baru saja melewati ini juga. Saya membuat prototipe aplikasi baru, dan sebenarnya masalah ini mungkin menjadi penghalang bagi saya dan menyebabkan saya mencari solusi non-Flutter lainnya, yang akan membuat saya sedih.

Seperti yang ditunjukkan oleh volume respons terhadap masalah ini, ini adalah masalah UTAMA dan cukup banyak mengalahkan kasus penggunaan utama isolat dalam konteks aplikasi Flutter.

ya ini masalah besar, perlu diperbaiki secepatnya

Saya telah belajar flutter selama 10 hari, dan saya menyukainya sejauh ini, tetapi setelah beberapa saat bereksperimen, Anda mulai mengalami masalah, dan melihat kerugiannya, dan masalah INI, dibuka sejak 5 Januari 2018, oof...

Tolong perbaiki bug ini.

Berharap untuk segera mendukung.

@jpsarda , @klaszlo8207 Jangan menahan nafas. Dan ya, ada * solusi untuk masalah tersebut, hingga hari ini: https://pub.dev/packages/isolate_handler. Pemrogram harus menghentikannya hari ini karena perubahan yang sangat baru di Flutter (hanya beberapa hari) membuat tidak mungkin untuk mengikuti rute itu lagi.

Anda harus tahu, bagaimanapun, bahwa ini tidak benar-benar berarti paralelisme. Itu mungkin tampak seperti itu dan terlihat penting di aplikasi seluler tetapi kode platform berjalan di utas utama, jadi mendelegasikan tugas panjang kembali dari isolat ke utas utama tidak benar-benar menyelesaikan apa pun.

Akan menyenangkan jika dart menangani multithreading secara default setiap kali Anda menggunakan fungsi async.

@spiderion Async tidak pernah dimaksudkan untuk menjadi multithreading, seperti di banyak platform lainnya. Jangan salah paham, saya tidak akan mengatakan batasan saluran platform bukan masalah besar karena itu (aplikasi saya telah bekerja dengan plugin yang saya sebutkan sampai kemarin dan sekarang saya harus mencari solusi), tetapi async/ menunggu tidak pernah tentang multithreading sejak hari pertama.

@zoechi Kami ingin tahu apakah ada pembaruan tentang masalah ini atau apakah ada rencana untuk memperbaikinya?
Terima kasih.

@spiderion Mereka tidak dapat memperbaikinya, ini mengikuti cara kerja isolat. Mereka tidak memiliki mesin UI di belakang mereka, oleh karena itu tidak ada komunikasi saluran platform. Namun, ada dua plugin untuk membantu Anda:

  • https://pub.dev/packages/flutter_isolate menyediakan isolat pengganti yang dapat berkomunikasi dengan plugin karena membuat dukungan UI sendiri (tidak ada yang Anda lihat atau harus tangani, hanya secara teknis),

  • https://pub.dev/packages/isolate_handler yang baru saja kita modifikasi untuk mengandalkan paket di atas karena cara yang digunakan sebelumnya dibuat tidak mungkin oleh perubahan Flutter baru-baru ini. Keuntungan menggunakan paket ini daripada flutter_isolate itu sendiri adalah bahwa ini menambah kemampuan penanganan, Anda dapat memulai beberapa isolat, melacaknya dan Anda tidak perlu mengatur komunikasi Anda sendiri antara isolat dan utama utas (sesuatu yang harus Anda lakukan secara manual dengan stok asli Isolate dan FlutterIsolate ) karena sudah diabstraksikan dan tersedia.

Saya menggunakan yang kedua dengan sukses sempurna. Sebenarnya, saya telah menggunakannya cukup lama dan ketika perubahan Flutter melanggar datang, saya membantu programmer untuk beralih ke solusi baru hanya karena aplikasi saya rusak juga. :-)

Jadi, saya rasa tidak masuk akal untuk mengharapkan solusi dari inti, terutama karena sebagian besar kasus penggunaan tidak memerlukan komunikasi saluran platform, jadi mesin UI pendukung bukanlah sesuatu yang ingin mereka tambahkan ke setiap isolat. Cukup gunakan plugin yang ada saat Anda membutuhkan fungsionalitas tambahan itu.

Hai @deakjahn terima kasih atas jawaban cepat Anda.

Solusi yang Anda berikan terlihat sangat berguna. Namun, saya tidak tahu apakah itu bisa menyelesaikan masalah saya. Saat ini saya memiliki situasi di aplikasi di mana pengguna membuat cerita, katakanlah mirip dengan Instagram. Aplikasi, dalam hal ini, perlu mengunggah di penyimpanan firebase daftar video dan file yang mungkin membutuhkan waktu sangat lama untuk diunggah (sekitar 15 menit jika koneksi lambat) kemudian setelah file diunggah, aplikasi perlu membuat sebuah dokumen di cloud firebase, Firestore. Masalah yang saya temui adalah jika pengguna mematikan aplikasi saat file sedang diunggah maka tugas lengkapnya tidak sepenuhnya dijalankan.

Itu bukan tanggung jawab isolat, itu hanya melakukan apa yang Anda perintahkan. Firebase dapat melanjutkan pembongkaran yang dihentikan, sejauh yang saya lihat, meskipun saya tidak pernah menggunakannya sendiri.

Saya mencoba menjalankan contoh getBatteryLevel:
https://flutter.dev/docs/development/platform-integration/platform-channels?tab=android-channel-java-tab
dan crash setiap kali saya mencoba menjalankannya di perangkat android. Dapatkah seseorang tolong bantu? Saya bahkan tidak yakin bagaimana cara memeriksa kesalahan, karena tidak ada log dan itu terus berjalan dalam loop tak terbatas dan saya menggunakan kode yang sama persis.

Memberi label masalah ini untuk P5 ??? 🥵.

Silakan, periksa komentar sebelumnya. Tampaknya sangat tidak mungkin bahwa ini akan diubah dalam kerangka kerja karena memerlukan mekanisme yang tidak diperlukan sebagian besar isolat, jadi tidak ada gunanya menambahkan overhead itu. Untuk kasus-kasus ketika Anda membutuhkannya, ada paket untuk menyelesaikannya.

Pengguna tidak peduli dengan mekanismenya; seberapa sulit atau tidak. Mereka hanya butuh hasil. Ini mungkin tidak mungkin dilakukan. Tetapi jika pengguna membutuhkannya, Flutter harus menyediakannya...

Saya memahami bahwa implementasi memiliki keterbatasan dan mungkin membutuhkan banyak waktu untuk menambahkan fitur ini ke isolat yang ada atau membuat API baru dengan fitur ini. Saya hanya berpikir ini harus memiliki prioritas lebih tinggi daripada P5.

Juga, saya membaca semua komentar Anda dan bagi saya rasanya seperti Anda hanya mempromosikan paket yang Anda buat.

Bukan, bukan saya yang membuatnya, tapi saya memang menerima kepemilikannya beberapa minggu lalu, ya. Saya tidak berpikir itu dirahasiakan di utas ini. Selain itu, saya menyebutkan dua paket (dalam satu posting, sebenarnya, hanya sekali) yang keduanya dapat memberikan apa yang Anda cari dan saya tidak memiliki afiliasi dengan yang kedua selain mengandalkan pekerjaan luar biasa yang dilakukan sejauh ini.

Jika Anda membacanya, satu-satunya alasan saya menggunakan ini sendiri adalah karena saya membutuhkan fungsi yang sama seperti yang Anda minta. Dan aplikasi saya telah mengandalkannya selama lebih dari satu tahun sekarang, tanpa masalah. Selain itu, menjadi lebih akrab dengan cara kerja isolat daripada pengembang sesekali yang hanya menggunakannya (ditambah bahwa saya mulai bekerja pada rekan mereka di Flutter Web), membuat saya berpikir bahwa apa yang saya tulis itu benar: fungsi ini tidak akan dimasukkan dalam kerangka kerja karena ada beberapa argumen yang menentang penyertaannya. Bukan karena itu akan memakan banyak waktu, tidak sama sekali. Ini akan memakan waktu yang relatif sedikit. Hanya karena itu akan menambah kompleksitas backend ke semua isolat daripada hanya beberapa dari mereka yang benar-benar perlu dan digunakan.

@deakjahn terima kasih atas tip ini, akankah menggunakan isolate_handler membantu saya memanggil metode dan mengirim data ke sisi panah setiap kali saya mendapat pemberitahuan di sisi asli? (Saya menggunakan twilio sdks asli dan menerima pemberitahuan push di sisi asli)

Jika ini berarti Anda memiliki plugin patform yang Anda gunakan untuk berkomunikasi melalui saluran platform, maka ya, salah satu dari dua paket tersebut akan membantu. Jika bekerja dari kode biasa, maka paket-paket ini akan membuatnya bekerja dari sebuah isolat juga.

@deakjahn Terima kasih atas respon cepatnya! Masalah saya adalah saya tidak bisa melakukannya dengan saluran platform biasa, saya tidak yakin tapi saya pikir twilio sdk menangani mendengarkan pemberitahuan push di isolat terpisah dan bukan di utas utama, dan saya pikir itu masalah saya. Saya ingin tahu apakah itu akan berhasil jika saya memanggil metode ini setiap kali saya mendapat pemberitahuan menggunakan salah satu dari 2 paket ?terima kasih banyak. (Saya sedang mempertimbangkan untuk beralih ke aplikasi asli jika ini tidak memungkinkan)

Saya bahkan tidak tahu apa itu Twilio (OK, saya googling :-) ), jadi saya benar-benar tidak yakin. Tetapi bagaimanapun juga, jika Anda ingin menghubungkan kode asli apa pun, Anda harus menggunakan saluran platform. Entah Anda menggunakan plugin yang sudah ada yang dibuat seseorang untuk menggunakan Twilio ini atau Anda sendiri yang menulisnya (dalam hal ini Anda dapat membuat plugin yang hampir sama, hanya saja tidak mempublikasikannya dan merujuknya secara lokal), atau Anda cukup menyalin plugin yang relevan kode ke dalam aplikasi Anda sendiri (itu tidak akan menjadi ketergantungan eksternal tetapi selain itu, itu akan memiliki kode yang hampir sama).

Dan, jika Anda memiliki saluran platform, maka jawaban sebelumnya berlaku: jika Anda sudah dapat menggunakan saluran platform dan plugin dari utas utama aplikasi Flutter biasa, maka Anda dapat menggunakan yang sama dari isolat menggunakan salah satu dari dua paket.

Jadi masalah ini ada Sejak 5. Jan 2018 dan masih tidak mungkin dengan usaha ekstra. Solusi yang diberikan bagus - tetapi belum ada yang berfungsi di desktop. Jadi bagi siapa saja yang ingin mencoba seberapa baik flutter bekerja di desktop dan melihat apakah mungkin untuk produk masa depan akan benar-benar macet di sini.
Hampir semua Aplikasi membutuhkan isolasi, karena selalu ada beberapa perhitungan besar, bagaimana mungkin tidak ada yang terjadi pada kelemahan desain Api yang begitu penting?

Saya telah menambahkan jalan keluar untuk ini, di plugin saya System Alert Window

Solusinya disebutkan di sini Isolasi komunikasi .
Meskipun contoh yang diberikan berbicara khusus untuk plugin jendela peringatan sistem, itu dapat dengan mudah direplikasi untuk plugin lain. Dan itu tidak memerlukan plugin mewah lainnya untuk membuatnya berfungsi!

Ini masih bisa sangat bermasalah. Ini berarti bahwa tidak ada cara untuk memanggil plugin secara langsung dari isolat yang berjalan di latar belakang/latar depan, karena Anda harus mengirim pesan ke lingkup aplikasi untuk melakukan apa pun dengan plugin. Jadi, jika aplikasi Anda tidak berjalan, tidak ada cara untuk melakukan apa pun dengan plugin di latar belakang.

Jadi misalnya jika Anda ingin melakukan sesuatu dengan preferensi bersama di backgroundMessageHandler dari plugin FCM saat aplikasi ditutup, itu akan memunculkan MissingPluginException.

Atau jika Anda tidak ingin membuka aplikasi dengan jendela peringatan sistem. Jendela peringatan berjalan pada isolat yang berbeda. Jadi, Anda harus menjalankan kode dalam lingkup aplikasi Anda. Ini bermasalah, karena aplikasi saat ini ditutup.

Dan mungkin ada banyak skenario lain di mana ini adalah masalah besar.

@michael-ottink skenario yang Anda gambarkan adalah skenario yang seharusnya tidak menjadi masalah. Plugin yang mengeksekusi kode di latar belakang umumnya membuat instance FlutterEngine baru dengan registri pluginnya sendiri dan isolat yang berjalan di dalamnya akan dapat berkomunikasi langsung melalui saluran platform ke plugin tersebut.

Kebetulan, itu berarti bahwa jika Anda membutuhkan isolat untuk menggunakan plugin, cara mudahnya adalah dengan membungkus isolat itu dalam FlutterEngine dan itu akan secara ajaib mewarisi kekuatan itu. Inilah yang dilakukan paket flutter_isolate untuk Anda di Android dan iOS (walaupun saya setuju akan lebih baik untuk memiliki perbaikan yang tepat daripada solusi. Solusi yang tersedia saat ini memiliki kemacetan, overhead atau mungkin kurang terpelihara atau kurang dukungan di seluruh semua platform.)

@ryanheise jadi flutter_isolate melakukannya untuk saya? Bagaimana saya benar-benar tidak tahu dari dokumen. Apa yang harus saya lakukan agar berfungsi untuk FCM saya diBackgroundMessage? Jadi saya perlu menelurkan isolat baru di onBackgroundMessageHandler saya? Tapi saya tidak bisa menggunakan plugin di sana, jadi bagaimana saya menggunakan flutter_isolate?

Masalah Anda adalah firebase_messaging sudah lama tidak diperbarui dan tertinggal jauh di belakang API eksekusi latar belakang terbaru. Jika mereka memperbarui plugin mereka, Anda tidak akan lagi mengalami masalah ini. Hanya untuk memperjelas lagi, jenis skenario yang Anda gambarkan adalah skenario yang seharusnya tidak menjadi masalah karena isolat latar belakang seharusnya sudah memiliki akses ke plugin jika diterapkan dengan benar. plugin firebase_messaging tidak diimplementasikan sesuai dengan API terbaru dan itulah sebabnya plugin ini tidak berfungsi untuk Anda. Anda dapat mengirimkan laporan bug dengan proyek itu.

Saya sangat membutuhkan bantuan untuk ini. Apakah mungkin ada cara saya bisa melakukan ini. Saya memiliki dealine serius pertama saya besok dan saya tidak bisa mengetahuinya.

Lakukan segalanya untuk memastikan bahwa plugin sedang dimuat. Saya tidak tahu apakah Anda telah mengikuti instruksi di README tentang cara mengaktifkan pesan latar belakang, tetapi Anda perlu membuat kelas Aplikasi khusus yang terhubung ke inisialisasi latar belakang plugin FlutterNativeView. Jika Anda belum melakukannya, tidak ada plugin yang tersedia untuk Anda gunakan. Jika itu masih tidak berhasil, Anda dapat mencoba menurunkan versi proyek Anda ke arsitektur plugin gaya v1 yang lama (dan berisiko merusak plugin lain yang memerlukan v2).

Saya mengatur Application.kt saya seperti ini

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)
    }
  }
}

Sebaiknya posting ini di halaman masalah firebase_messaging karena tidak terkait dengan masalah ini.

Saya dapat membuat PR, yang akan memiliki metode 'spawnIsolate' baru untuk 'SchedulerBinding'.

Maka dimungkinkan untuk memanggil metode platform dalam isolat ini.

Ini hanya akan membantu Anda jika Anda perlu memanggil metode platform dan mendapatkan respons.

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();
}

Catatan

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

Saya dapat membuat PR, yang akan memiliki metode 'spawnIsolate' baru untuk 'SchedulerBinding'.

Maka dimungkinkan untuk memanggil metode platform dalam isolat ini.

Ini hanya akan membantu Anda jika Anda perlu memanggil metode platform dan mendapatkan respons.

SchedulerBinding.instance.spawnIsolate

Adakah kesempatan untuk menguji ini? Jika itu sesuai dengan kebutuhan saya dll..

@Nailik
Ganti file ini (Aktual untuk versi 1.17.5)


mengikat.dart

```dart// Hak Cipta 2014 The Flutter Authors. Seluruh hak cipta.
// Penggunaan kode sumber ini diatur oleh lisensi gaya BSD yang dapat
// ditemukan di file LISENSI.

impor ' dart:async ';
impor ' dart:isolasi ';
impor ' dart:typed_data ';
impor ' dart:ui ' sebagai ui;

import ' package:flutter/foundation.dart ';

impor 'asset_bundle.dart';
impor 'binary_messenger.dart';
impor 'system_channels.dart';

/// Mendengarkan pesan platform dan mengarahkannya ke [defaultBinaryMessenger].
///
/// [ServicesBinding] juga mendaftarkan [LicenseEntryCollector] yang mengekspos
/// lisensi yang ditemukan di file LICENSE yang disimpan di root aset
/// bundel, dan mengimplementasikan ekstensi layanan ext.flutter.evict (lihat
/// [mengusir]).
mixin ServicesBinding di BindingBase {
@mengesampingkan
void initInstances() {
super.initInstances();
_instance = ini;
_defaultBinaryMessenger = createBinaryMessenger();
window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
initLisensi();
SystemChannels.system.setMessageHandler(handleSystemMessage);
}

/// [ServicesBinding] saat ini, jika sudah dibuat.
static ServicesBinding dapatkan instance => _instance;
_instance ServicesBinding statis;

/// Contoh default [BinaryMessenger].
///
/// Ini digunakan untuk mengirim pesan dari aplikasi ke platform, dan
/// melacak penangan mana yang telah terdaftar di setiap saluran jadi
/// mungkin mengirimkan pesan masuk ke handler terdaftar.
BinaryMessenger dapatkan defaultBinaryMessenger => _defaultBinaryMessenger;
BinaryMessenger _defaultBinaryMessenger;

/// Membuat instance [BinaryMessenger] default yang dapat digunakan untuk mengirim
/// pesan platform.
@terlindung
BinaryMessenger createBinaryMessenger() {
kembali const _DefaultBinaryMessenger._();
}

/// Handler memanggil pesan yang diterima di [SystemChannels.system]
/// saluran pesan.
///
/// Binding lain dapat menimpa ini untuk menanggapi pesan sistem yang masuk.
@terlindung
@mustCallSuper
Masa depanhandleSystemMessage(Object systemMessage) asinkron {}

/// Menambahkan lisensi yang relevan ke [LicenseRegistry].
///
/// Secara default, implementasi [ServicesBinding] dari [initLicenses] menambahkan
/// semua lisensi yang dikumpulkan oleh alat flutter selama kompilasi.
@terlindung
@mustCallSuper
batal initLicenses() {
LicenseRegistry.addLicense(_addLicenses);
}

Sungai kecil_addLicenses() asinkron* {
// Kami menggunakan pengatur waktu di sini (bukan scheduleTask dari pengikatan penjadwal)
// karena lapisan layanan tidak dapat menggunakan pengikatan penjadwal (penjadwal
// pengikatan menggunakan lapisan layanan untuk mengelola peristiwa siklus hidupnya). pengatur waktu
// adalah apa yang digunakan scheduleTask di bawah tenda. Satu-satunya perbedaan adalah
// bahwa ini hanya akan berjalan berikutnya, bukannya diprioritaskan relatif terhadap
// tugas lain yang mungkin sedang berjalan. Menggunakan _sesuatu_ di sini untuk menghancurkan
// ini menjadi dua bagian penting karena isolat membutuhkan waktu untuk menyalin
// data saat ini, dan jika kita menerima data dalam loop peristiwa yang sama
// iterasi saat kami mengirim data ke isolat berikutnya, kami pasti
// akan melewatkan frame. Solusi lain adalah memiliki pekerjaan semua
// terjadi dalam satu isolat, dan kita mungkin akan pergi ke sana pada akhirnya, tetapi pertama-tama kita
// akan melihat apakah komunikasi yang terisolasi dapat dibuat lebih murah.
// Lihat: https://github.com/dart-lang/sdk/issues/31959
// https://github.com/dart-lang/sdk/issues/31960
// TODO(ianh): Hapus kerumitan ini setelah bug ini diperbaiki.
Penyempurna akhirrawLicenses = Pelengkap();
Timer.run(() asinkron {
rawLicenses.complete(rootBundle.loadString('LICENSE', cache: false));
});
menunggu rawLicenses.future;
Penyempurna akhir> parsedLicenses = Pelengkap>();
Timer.run(() asinkron {
parsedLicenses.complete(compute(_parseLicenses, menunggu rawLicenses.future, debugLabel: 'parseLicenses'));
});
menunggu parsingLicenses.future;
hasil* Aliran.fromIterable(menunggu parsedLicenses.future);
}

// Ini dijalankan di isolat lain yang dibuat oleh _addLicenses di atas.
Daftar statis_parseLicenses(String rawLicenses) {
string terakhir _licenseSeparator = '\n' + ('-' * 80) + '\n';
daftar akhirhasil =[];
daftar akhirlisensi = rawLicenses.split(_licenseSeparator);
for (lisensi String terakhir dalam lisensi) {
final int split = license.indexOf('\n\n');
if (bagi >= 0) {
result.add(LicenseEntryWithLineBreaks(
license.substring(0, split).split('\n'),
lisensi.substring(split + 2),
));
} kalau tidak {
result.add(LicenseEntryWithLineBreaks(const[], lisensi));
}
}
kembali hasil;
}

@mengesampingkan
batal 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;
}());

}

/// Dipanggil sebagai tanggapan atas ekstensi layanan ext.flutter.evict .
///
/// Ini digunakan oleh alat flutter selama hot reload sehingga gambar apapun
/// yang telah berubah pada disk dibersihkan dari cache.
@terlindung
@mustCallSuper
void evict(String aset) {
rootBundle.evict(aset);
}

Masa depanbertelurIsolate(
Masa DepanAtautitik masuk(Pesan T),
T pesan, {
bool dijeda = salah,
bool errorAreFatal,
SendPort saat Keluar,
SendPort onError,
String nama debug,
}) {
menegaskan(
_isMainIsolate,
'Tidak dapat membuat beberapa level isolat',
);

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,
);

}
}

Masa depan_startIsolate(SendPort sendPort) asinkron {
_sendPortToMainIsolate = sendPort;
_IsolateBinding();

Pelengkap akhir<_IsolateStarter> pelengkap =
Pelengkap<_IsolateStarter>();

akhir RawReceivePort menerimaPort = RawReceivePort(
(Objek isolatStarter) {
tegaskan (isolateStarter adalah _IsolateStarter);
completer.complete(isolateStarter sebagai _IsolateStarter);
},
);

sendPort.send(receivePort.sendPort);

akhir _IsolateStartermengisolasiStarter = menunggu completer.future;

menerimaPort.close();

fungsi Fungsi akhir =
ui.PluginUtilities.getCallbackFromHandle(isolateStarter.callbackHandle);

fungsi menunggu (isolateStarter.message);
}

SendPort _sendPortToMainIsolate;

bool dapatkan _isMainIsolate => _sendPortToMainIsolate == null;

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

ui terakhir.CallbackHandle callbackHandle;
pesan T terakhir;
}

kelas _Pesan {
_Pesan(
ini. saluran,
pesan ini,
this.sendPort,
);

saluran String terakhir;
pesan ByteData terakhir;
SendPort akhir sendPort;
}

class _IsolateBinding memperluas BindingBase dengan ServicesBinding {}

/// Implementasi default [BinaryMessenger].
///
/// Messenger ini mengirim pesan dari sisi aplikasi ke sisi platform dan
/// mengirimkan pesan masuk dari sisi platform ke yang sesuai
/// penangan.
class _DefaultBinaryMessenger memperluas BinaryMessenger {
const _DefaultBinaryMessenger._();

// Penangan untuk pesan masuk dari plugin platform.
// Ini statis sehingga kelas ini dapat memiliki konstruktor const.
Peta akhir statis_penangan =
{};

// Pengendali tiruan yang mencegat dan menanggapi pesan keluar.
// Ini statis sehingga kelas ini dapat memiliki konstruktor const.
Peta akhir statis_mockHandler =
{};

Masa depan_sendPlatformMessage(Saluran string, pesan ByteData) {
Penyempurna akhirpenyempurna = penyempurna();

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;

}

@mengesampingkan
Masa depanmenanganiPesan Platform(
saluran tali,
data ByteData,
ui.PlatformMessageResponseCallback panggilan balik,
) tidak sinkron {
Respons ByteData;
mencoba {
terakhir MessageHandler handler = _handlers[channel];
if (penangan != null) {
respon = menunggu penangan(data);
} kalau tidak {
ui.channelBuffers.push(saluran, data, panggilan balik);
panggilan balik = nol;
}
} tangkap (pengecualian, tumpukan) {
FlutterError.reportError(Rincian FlutterError(
pengecualian: pengecualian,
tumpukan: tumpukan,
perpustakaan: 'perpustakaan layanan',
konteks: ErrorDescription('selama panggilan balik pesan platform'),
));
} akhirnya {
if (panggilan balik != null) {
panggilan balik (tanggapan);
}
}
}

@mengesampingkan
Masa depansend(Saluran string, pesan ByteData) {
pawang MessageHandler terakhir = _mockHandlers[saluran];
jika (penangan != null)
penangan kembali (pesan);
kembalikan _sendPlatformMessage(saluran, pesan);
}

@mengesampingkan
void setMessageHandler(Saluran string, penangan MessageHandler) {
jika (penangan == null)
_handlers.remove(saluran);
kalau tidak
_handlers[channel] = penangan;
ui.channelBuffers.drain(saluran, (data ByteData, ui.PlatformMessageResponseCallback callback) async {
menunggu handlePlatformMessage(saluran, data, panggilan balik);
});
}

@mengesampingkan
void setMockMessageHandler(Saluran string, penangan MessageHandler) {
jika (penangan == null)
_mockHandlers.remove(saluran);
kalau tidak
_mockHandlers[saluran] = penangan;
}
}
```

Saya memiliki flutter 1.21 jadi saya mencoba memperbarui semua kode untuk dikompilasi.
Sekarang ada satu Masalah tersisa karena _IsolateBinding kehilangan banyak implementasi Metode BindingBase...

Saya mendapatkan suka

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

sekitar 30 kali.

Pada akhirnya konsol mencetak

/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 {}

Saya tidak 100% yakin apa yang dilakukan oleh panggilan _IsolateBinding() di _startIsolate tetapi tanpa saya mendapatkan kesalahan ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized. yang umum.

Saya mencoba untuk memperbaikinya tetapi saya pikir pengetahuan saya tentang mixin belum begitu baik.
Ada ide bagaimana cara memperbaikinya?


File baru terlihat seperti ini (sejauh ini)

// 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;
}

@Nailik
Saat ini, saya tidak dapat memperbarui flutter untuk membantu Anda

Saya bertanya-tanya mengapa alat flutter lain yang diterapkan oleh tim flutter (seperti katakanlah devtools) tidak pernah mengenai pemblokir ini.
Mereka tampaknya memperbaiki bug hanya jika mereka diblokir oleh mereka.

Ini bukan fitur baru yang parah, ini adalah pemblokir kerangka kerja.
Jadi menurut ini https://github.com/flutter/flutter/issues/18761#issuecomment -639248761 itu harus P3

Saya bertanya-tanya mengapa alat flutter lain yang diterapkan oleh tim flutter (seperti katakanlah devtools) tidak pernah mengenai pemblokir ini.
Mereka tampaknya memperbaiki bug hanya jika mereka diblokir oleh mereka.

Sejujurnya, karena saya pikir dampak dari masalah ini di aplikasi dunia nyata terlalu berlebihan. Saya hanya menonton masalah karena itu akan membuat segalanya lebih mudah, tetapi ada "solusi". Dan banyak aplikasi dan plugin dalam produksi menggunakan fungsionalitas latar belakang.

Saya juga tidak yakin apakah beberapa +1 tidak begitu mengerti tentang apa masalahnya. (Seperti https://github.com/flutter/flutter/issues/13937#issuecomment-635683123 ). Imo dokumentasi resmi untuk isolat latar belakang & runtime tanpa kepala (dan cara mengelolanya) masih sedikit kurang dan hal-hal perpesanan firebase adalah mimpi buruk, tetapi ini tidak ada hubungannya dengan kerangka kerja, mesin ️

Mungkin berhenti saja memukul tim flutter, ada kemajuan luar biasa, dan ini bukan sesuatu yang tidak dapat diselesaikan... Dan jika ada PR, mengapa tidak menyerahkannya untuk dipertimbangkan tim flutter

Ada masalah dengan penerapan perilaku ini untuk isolat.

Contoh:

Ada isolat utama dan mesin.
Jika isolat membutuhkan sesuatu, ia meminta mesin.
Jika mesin membutuhkan sesuatu, ia meminta isolat.

Sekarang situasi kita adalah:
Ada 2 isolat dan mesin.
Jika isolat membutuhkan sesuatu, ia meminta mesin.
Jika mesin membutuhkan sesuatu, apa yang harus dilakukan?
Tanya keduanya? Lalu jawaban siapa yang harus diambil?
Atau minta hanya satu isolat? Lalu yang mana?

@hpoul Saya harus setuju untuk tidak setuju karena banyak orang di sini adalah pemula untuk pemrograman Android/ios dan mereka memilih kerangka kerja pertama mereka untuk pengembangan aplikasi menjadi bergetar.

Jadi, penting bahwa seseorang menyadari hal ini dan memperbaiki atau menyarankan solusi resmi dengan contoh untuk masalah semacam ini di mana Anda diblokir dan dipaksa oleh kerangka kerja untuk melakukan "solusi" yang rumit karena jelas para pemula tidak akan tahu apa yang harus dipelajari atau bahkan harus mulai dari mana karena tidak ada solusi yang dijelaskan dengan baik di komentar di sini atau di mana pun di stackoverflow atau blog lain. Hanya ada hal-hal yang menunjuk ke beberapa paket yang mungkin atau mungkin tidak mengimplementasikan ini dengan benar.

Orang-orang yang memiliki banyak pengalaman bekerja dengan layanan, antrian tugas latar belakang, dll., di Android dan iOS, yang dapat dengan mudah mengatasi ini bukanlah target demografis untuk flutter.

Dan keluar dari komentar topik seperti itu saya yakin ada +1 palsu di setiap masalah lainnya, yang tidak banyak mengubah urutan prioritas masalah dan masalah ini masih tetap di atas.

Hai @phanirithvij

flutter_isolate ditautkan di atas dan telah ada sejak Februari 2019. Itu juga tidak rumit. Saya akan menyalin README yang tampaknya mudah:

FlutterIsolate memungkinkan pembuatan Isolate in flutter yang dapat menggunakan plugin flutter. Ini menciptakan bit spesifik platform yang diperlukan (FlutterBackgroundView di android & FlutterEngine di iOS) untuk memungkinkan saluran platform bekerja di dalam isolat.

| | Android | iOS | Deskripsi |
| :--------------- | :----------------: | :------------------: | :-------------------------------- |
| FlutterIsolate.spawn(titik masuk,pesan) | :white_check_mark: | :white_check_mark: | memunculkan FlutterIsolate baru |
| flutterIsolate.pause() | :white_check_mark: | :white_check_mark: | menjeda isolat yang sedang berjalan |
| flutterIsolate.resume() | :white_check_mark: | :white_check_mark: | melanjutkan isoalte yang dijeda |
| flutterIsolate.kill() | :white_check_mark: | :white_check_mark: | membunuh seorang isolat |

Saya katakan langsung karena metode ini memiliki nama yang sama dengan yang ada di kelas Isolate "asli" jadi jika Anda sudah tahu cara menggunakan isolat dari dokumentasi resmi, seharusnya tidak sulit untuk memahami cara menggunakannya ini sebagai pengganti drop-in, setidaknya untuk metode yang tercantum di atas.

(Catatan: Saya juga ingin masalah ini diperbaiki secara resmi karena alasan yang telah saya nyatakan dalam komentar sebelumnya, beberapa di antaranya saya bagikan dengan Anda, tetapi saya juga tidak akan menyebut solusi ini "kompleks".)

Menanggapi komentar @nikitadol , flutter_isolate membuat mesin terpisah untuk setiap isolat sehingga tidak mengalami masalah yang sama. Tentu saja, tidak ideal untuk membuat mesin baru per isolat, itulah salah satu alasan mengapa saya yakin tim Flutter dapat melakukan pekerjaan yang lebih baik. Demikian pula, solusi alternatif dari pemipaan semua panggilan metode plugin melalui isolat utama akan menciptakan hambatan yang bertentangan dengan tujuan isolat, jadi saya yakin tim Flutter juga dapat melakukan pekerjaan yang lebih baik.

Saya setuju dengan mereka yang mengatakan bahwa tim Flutter mungkin memiliki masalah dengan prioritas lebih tinggi untuk ditangani terlebih dahulu, tetapi berdasarkan apa yang telah kita lihat sebelumnya dengan arsitektur plugin, mungkin juga perbaikan ini memerlukan perubahan arsitektur besar, dan beberapa masalah terbuka lainnya juga memerlukan perubahan arsitektur besar, dan menimbang semua persyaratan yang berbeda ini dapat memakan waktu sebelum mereka bahkan dapat mengatasi masalah khusus ini.

@ryanheise

flutter_isolate membuat mesin terpisah untuk setiap isolat

jadi saya yakin tim Flutter juga bisa melakukannya dengan lebih baik.

Anda bilang tim Flutter bisa melakukan ini, tapi bagaimana caranya?
Pertanyaan yang saya ajukan di atas tetap valid.
Jika solusinya adalah membuat mesin baru, maka Anda cukup menggunakan plugin Anda

(Catatan, flutter_isolate bukan plugin saya, tetapi karena saya juga membutuhkan fitur ini, saya memutuskan untuk berkontribusi pada proyek.)

Apa yang saya maksud dengan "pekerjaan yang lebih baik" adalah bahwa mungkin tidak perlu memutar SEMUA mesin di FlutterEngine hanya untuk berkomunikasi dengan plugin, tetapi saat ini, infrastruktur yang diperlukan semuanya terkait dengan mesin jadi itu sebabnya saat ini flutter_isolate harus memutar seluruh mesin hanya untuk mendapatkan akses ke plugin.

(Catatan, flutter_isolate bukan plugin saya, tetapi karena saya juga membutuhkan fitur ini, saya memutuskan untuk berkontribusi pada proyek.)

Maaf, saya tidak menyadari bahwa ini adalah garpu

flutter_isolate harus memutar seluruh mesin hanya untuk mendapatkan akses ke plugin.

Jika Anda menentukan bahwa 'is_background_view = true', maka mesin tidak mulai merender - yang berarti mesin tidak sepenuhnya menyala

API yang tepat untuk menjalankan headless berbeda untuk iOS dan Android v1 dan Android v2, tetapi pada dasarnya inilah yang sudah dilakukan flutter_isolate. Namun, masih ada perbedaan overhead antara isolat normal dan FlutterEngine, jadi menurunkan overhead lebih jauh hanyalah sesuatu yang dapat dilakukan oleh tim Flutter. Ini bukan hanya overhead dalam waktu startup, itu juga overhead dalam penggunaan memori.

Jadi Anda hanya menyarankan untuk mengoptimalkan pembuatan mesin baru untuk bekerja di latar belakang?

Maka ini mungkin tidak berlaku untuk masalah ini

Saya tidak menyarankan itu, saya hanya menunjukkan bahwa solusi flutter_isolate memiliki batasan ini karena overhead yang tidak dapat dihindari. Saya tidak menyarankan bahwa tim Flutter harus mencoba membuat solusi lebih efisien, saya pikir idealnya akan ada perubahan arsitektur pada cara plugin dimuat sehingga tidak perlu membuat instance FlutterEngine.

Jika mesinnya 1, dan isolatnya lebih besar dari 1, maka kita kembali ke pertanyaan ini:

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

Saya tidak mencoba untuk benar-benar menemukan solusi untuk arsitektur ringan baru ini, tetapi jika Anda bertanya kepada saya, saya dapat memikirkan dua variasi solusi ringan:

  1. Jadikan saluran metode sedikit seperti soket server dengan membuatnya menerima koneksi dari banyak klien. Jadi saluran metode di sisi platform plugin dapat menerima koneksi dari beberapa isolat, dan kemudian setelah terhubung dapat mengirim pesan ke isolat yang tepat.
  2. Buat registri plugin baru dll. untuk setiap isolat TAPI dalam FlutterEngine yang sama,

jika Anda bertanya kepada saya

Ya, saya bertanya

bisa mengirim pesan ke isolat yang tepat.

Saya pikir ini adalah solusi yang buruk karena 'ke isolat yang tepat' adalah konsep yang relatif

Buat registri plugin baru dll. untuk setiap isolat TAPI dalam FlutterEngine yang sama,

Ini bisa menjadi pilihan yang baik, tetapi hanya jika mesin selalu mendaftarkan plugin dengan sendirinya.

Tapi ini hanya terjadi jika semua plugin di GeneratedPluginRegistrant.
https://github.com/flutter/engine/blob/f7d241fd8a889fadf8ab3f9d57918be3d986b4be/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L330 -L344

Membuat panggilan balik tambahan bukanlah perilaku yang jelas.

Saya pikir ini adalah solusi yang buruk karena 'ke isolat yang tepat' adalah konsep yang relatif

Ini bisa menjadi solusi yang buruk, tetapi tidak bisa buruk karena alasan itu. "Isolasi kanan" tidak ambigu dengan cara yang sama seperti soket server menangani koneksi klien dengan cara yang tidak ambigu.

Ini bisa menjadi pilihan yang baik, tetapi hanya jika mesin selalu mendaftarkan plugin dengan sendirinya.

Yang akan menjadi hal yang baik untuk memastikan selalu terjadi. Saat ini mekanismenya agak terkelupas.

Ini bisa menjadi solusi yang buruk, tetapi tidak bisa buruk karena alasan itu. "Isolasi kanan" tidak ambigu dengan cara yang sama seperti soket server menangani koneksi klien dengan cara yang tidak ambigu.

Maka setiap plugin harus memperhitungkan bahwa itu dapat diakses dari isolat yang berbeda - buruk

Ya.

Izinkan saya menambahkan dua sen saya ke diskusi ini... Setelah bekerja secara komersial dengan Flutter selama sekitar dua tahun sekarang, hal terpenting yang saya pelajari adalah Flutter adalah toolkit UI . Tidak akan ada yang mengejutkan dalam pernyataan ini - bahkan disebutkan di situs utama Flutter - jika orang tidak melupakan hal ini.

Flutter adalah toolkit UI, dan yang berfungsi dengan baik adalah UI. Jika Anda mencoba membuat aplikasi berat CPU yang bergantung pada pemrosesan data yang mahal atau menggunakan algoritme rumit dan menghabiskan banyak data, maka Anda TIDAK boleh menggunakan toolkit UI untuk tujuan ini. Logika Bisnis tidak boleh dicampur dengan UI.

Tentu saja, dengan Dart kita bisa melakukan lebih dari sekedar UI, tapi jangan lupa tujuan utama dari toolkit ini.

Saya telah mencoba sejumlah pendekatan, termasuk menggunakan Flutter tanpa kepala - yang terbukti menjadi bencana total. Bukan karena tidak berfungsi, tetapi karena kami memiliki masalah dalam menjelaskan kepada pengembang lain cara kerjanya. Beban pemeliharaan yang ditambahkannya tidak sepadan. Belum lagi menguji solusi semacam itu. Terakhir kali saya mencoba, Flutter Driver tidak bisa menanganinya sama sekali.

Jika aplikasi Anda benar-benar membutuhkan kekuatan pemrosesan, IMHO, Anda memiliki tiga opsi:

  • pindahkan logika Anda ke server;
  • pindahkan logika Anda ke platform asli - Anda dapat menggunakan Kotlin Native untuk ini - dan gunakan saluran Acara/Metode untuk hanya menyediakan Model Tampilan ke UI Anda (saya pikir itulah yang dilakukan OLX: https://tech.olx.com/fast -prototipe-dengan-flutter-kotlin-asli-d7ce5cfeb5f1);
  • jangan gunakan Flutter dan cari kerangka kerja lain (Xamarin/Blazor mungkin cocok untuk ini karena C# menawarkan lingkungan multithreading yang lebih canggih daripada Dart).

Semua hal di atas memiliki kekurangannya masing-masing, tetapi mencoba meregangkan perpustakaan/kerangka kerja untuk melakukan apa yang sebenarnya tidak dirancang untuk itu juga tidak ideal.

Bukankah isolat_handler memecahkan masalah ini?

@hasonguo Ini menggunakan flutter_isolate di latar belakang, itu hanya menambahkan lapisan tambahan untuk membuat penanganan isolat (isolasi apa pun, sebenarnya) lebih mudah dengan menambahkan boilerplate untuk komunikasi sehingga Anda tidak perlu mengaturnya secara manual setiap waktu. Itu menggunakan cara yang berbeda untuk mengatasi masalah sebelumnya, tetapi itu menjadi tidak mungkin oleh perubahan di Flutter itu sendiri, dan diubah untuk mengandalkan flutter_isolate sebagai gantinya.

Kita akan berputar-putar. Ya, ada solusi, kedua plugin sebenarnya. Seperti yang ditunjukkan Ryan, ada overhead yang terlibat dalam membuat komunikasi saluran menjadi mungkin. Kemungkinan besar, overhead inilah yang membuat tim Flutter menghindari menambahkannya secara otomatis ke Isolate asli, karena Anda tidak memerlukan dukungan dalam banyak (kebanyakan?) kasus.

Tetap saja, itu bukan solusi sebersih yang bisa didapat. Jika tim Flutter dapat menyediakan komunikasi saluran tanpa biaya tambahan, secara langsung dan otomatis, langsung dari kotak dengan Isolate dasar apa pun, seluruh diskusi akan diperdebatkan. Namun, hal sebaliknya juga berlaku: mereka tidak benar-benar mengejar perubahan demi perubahan. Jika ada solusi yang layak dalam sebuah paket atau plugin, mengapa menghabiskan waktu dan menambah bobot pada intinya?

Buat registri plugin baru dll. untuk setiap isolat TAPI dalam FlutterEngine yang sama,

Ini bisa menjadi pilihan yang baik, tetapi hanya jika mesin selalu mendaftarkan plugin dengan sendirinya.

Tapi ini hanya terjadi jika semua plugin di GeneratedPluginRegistrant.
https://github.com/flutter/engine/blob/f7d241fd8a889fadf8ab3f9d57918be3d986b4be/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L330 -L344

Membuat panggilan balik tambahan bukanlah perilaku yang jelas.

Perbaikan lain untuk ide kedua ini adalah membuat plugin dimuat dengan malas di isolat baru. Biasanya, jika sebuah proyek bergantung pada banyak plugin, GeneratedPluginRegistrant akan cukup besar dan tergantung pada seberapa efisien rutinitas inisialisasi plugin, ini mungkin berkontribusi pada beberapa overhead. Bagaimana tepatnya menerapkan ini adalah cerita lain. Saluran platform tidak dimiliki oleh plugin, jadi saat ini mengirim pesan melalui saluran tidak dapat digunakan untuk memicu sisi platform plugin untuk dipakai. Jadi salah satu saluran metode perlu dikaitkan dengan plugin, atau perlu ada mekanisme tambahan yang dapat dipanggil oleh sisi Dart dari sebuah plugin untuk menginisialisasi dirinya sendiri dengan malas sebelum saluran metode pertama dibuat, dan mekanisme itu dapat memicu instantiasi dari sisi platform plugin itu di dalam isolat.

@TahaTesser

Dalam contoh Anda, kesalahan tidak terkait dengan masalah ini

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

Hai @nikitadol
Terima kasih, contoh yang diperbarui, tidak menimbulkan pengecualian, apakah itu benar? (Saya tidak pernah perlu menggunakan sebelumnya)
Bisakah Anda memberikan contoh kode minimal yang dapat direproduksi lengkap?
Terima kasih

@TahaTesser


contoh kode

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);
}



log

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): 



dokter berdebar -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
silahkan lihat komentar ini

Pesan platform hanya didukung dari isolat utama. [...]

Perilaku ini _diharapkan_ dan memiliki label severe: new feature
masalah ini harus dianggap sebagai _permintaan fitur_ atau _ proposal _ dan bukan sebagai _bug_

@iapicca

Dalam contoh Anda, kesalahan tidak terkait dengan masalah ini

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

Dalam contoh Anda, kesalahan tidak terkait dengan masalah ini

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>':.)

kamu benar milikku bukan contoh yang baik, abaikan saja

Apakah halaman ini membantu?
0 / 5 - 0 peringkat