Flutter: Não é possível chamar um método de canal de plataforma de outro isolado

Criado em 5 jan. 2018  ·  133Comentários  ·  Fonte: flutter/flutter

Quando tento invocar um método de canal de plataforma de um isolado gerado personalizado, o aplicativo falha mal (tanto no iOS quanto no Android). Estou tentando descobrir se isso é esperado ou não.

Se não for, provavelmente vale a pena mencionar isso em algum lugar.

De qualquer forma, acho que isso pode ser uma forte limitação. Existe uma maneira de chamar plugins de plataforma de um isolado secundário?

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

Comentários muito úteis

Eu tenho vários casos de uso, por exemplo:

  • Faça o download e analise uma grande quantidade de dados (os métodos dart:convert não são assíncronos) e armazene-os em um banco de dados sqlite usando o plug-in sqflite (que usa ligações de plataforma);
  • Pré-busque e analise os dados antes de servi-los à ui;
  • Criptografar/descriptografar e ler/gravar dados de/para um arquivo (o material de criptografia é feito através de um canal de método para usar bibliotecas de segurança da plataforma);

Em geral, pode acontecer de você usar dependências que declaram alguns métodos públicos que você realmente não sabe se eventualmente usarão código específico da plataforma para realizar suas coisas (estou pensando em flutterfire, por exemplo); usá-los imho deve ser mais um detalhe de implementação que pode mudar com o tempo, em vez de algo escrito em pedra.

Sua solução parece correta no momento e relativamente fácil de implementar, pois os dados já estão codificados de forma a serem passados ​​para o canal do método e, por esse motivo, passarão facilmente também por uma porta isolada, no entanto, meu palpite é que o desempenho seria subótimo .

No entanto, fiquei um pouco preso durante a implementação: você pode fornecer um exemplo de uma maneira fácil de invocar um método estático no isolado principal de um secundário?

Obrigado

Todos 133 comentários

cc @mravn-google para triagem

O código do mecanismo cancela a referência nula e trava ao tentar enviar uma mensagem de plataforma do isolado secundário. Ainda não identifiquei exatamente onde; Eu recebo uma lápide, mas preciso aprender a interpretar uma coisa dessas.

Como uma solução (desajeitada), o isolado secundário poderia pedir ao primário para enviar a mensagem.

@sroddy Posso perguntar o que você está tentando realizar com o isolado secundário?

Eu tenho vários casos de uso, por exemplo:

  • Faça o download e analise uma grande quantidade de dados (os métodos dart:convert não são assíncronos) e armazene-os em um banco de dados sqlite usando o plug-in sqflite (que usa ligações de plataforma);
  • Pré-busque e analise os dados antes de servi-los à ui;
  • Criptografar/descriptografar e ler/gravar dados de/para um arquivo (o material de criptografia é feito através de um canal de método para usar bibliotecas de segurança da plataforma);

Em geral, pode acontecer de você usar dependências que declaram alguns métodos públicos que você realmente não sabe se eventualmente usarão código específico da plataforma para realizar suas coisas (estou pensando em flutterfire, por exemplo); usá-los imho deve ser mais um detalhe de implementação que pode mudar com o tempo, em vez de algo escrito em pedra.

Sua solução parece correta no momento e relativamente fácil de implementar, pois os dados já estão codificados de forma a serem passados ​​para o canal do método e, por esse motivo, passarão facilmente também por uma porta isolada, no entanto, meu palpite é que o desempenho seria subótimo .

No entanto, fiquei um pouco preso durante a implementação: você pode fornecer um exemplo de uma maneira fácil de invocar um método estático no isolado principal de um secundário?

Obrigado

@sroddy Obrigado por fornecer as informações básicas.

Sobre invocar métodos estáticos no isolado principal: não é suportado diretamente e deve ser implementado usando portas. Este pacote pode fornecer essa funcionalidade, por exemplo, aqui . Não tentei eu mesmo embora.

Mas devemos procurar uma solução mais geral aqui, que funcione também quando a comunicação do canal da plataforma é feita como parte da implementação de um plug-in ou outra biblioteca.

Ainda não temos os ganchos para isso, mas se pudermos supor por um momento que você tem um conjunto conhecido de nomes de canais que devem ser usados ​​a partir do isolado secundário e não do principal, você deve ser capaz de reconfigure a manipulação de mensagens da plataforma para esses canais, implementando de forma transparente o encaminhamento necessário de mensagens binárias e respostas entre seus dois isolados. Plugins nunca saberiam a diferença.

Solução esboçada abaixo.

Suponha que você configure as portas M1 e R1 em seu isolado principal e M2, R2 em seu isolado secundário. M para mensagem, R para resposta.

  • Em seu isolado secundário, use BinaryMessages.setMockMessageHandler para cada canal para encaminhar mensagens da plataforma para M1 (transparentemente para o plugin que usa BinaryMessage.send com esse canal). Armazene retornos de chamada de resposta e configure R2 com um manipulador que chame o correto ao receber a resposta da mensagem. Configure o M2 com um manipulador que encaminha para BinaryMessages.handlePlatformMessage . Implemente o retorno de chamada de resposta para encaminhar respostas para R1.
  • Simetricamente em seu isolado principal. Configure M1 com um manipulador que encaminha mensagens para a plataforma para BinaryMessages.send . Defina um retorno de chamada de resposta que encaminha as respostas da plataforma para R2. Também chame BinaryMessages.setMessageHandler para cada um dos canais para configurar um manipulador de mensagens recebidas da plataforma que encaminha para M2. Armazene os retornos de chamada de resposta e configure R1 com um manipulador que chame o correto ao receber uma resposta do isolado secundário.

@Hixie , sugiro passar para o próximo marco. Uma solução parece existir. Uma boa solução exigirá algum trabalho de design de API e iteração.

Estou com o mesmo problema, alguma novidade sobre esse problema?

@mravn-google Você tem alguma atualização sobre isso? Eu me deparei com o mesmo problema (preciso gerar, por exemplo, um par de chaves RSA 2048 no lado da plataforma, o que demora um pouco em dispositivos mais antigos ou criptografar/descriptografar dados). Prefiro evitar criar threads ou serviços, pois isso duplicaria meu trabalho no lado da plataforma, pois deve ser implementado para plataformas Android e iOS. Temos alguma maneira fácil de executar essas operações específicas da plataforma de forma assíncrona do Dart? Sua solução parece estar bem, no entanto, parece que tenho alguns problemas com a implementação disso, pois sou bastante novo no Flutter e no Dart (não tenho certeza de como configurar um manipulador que encaminhe mensagens para um Isolate e assim por diante), desculpe sobre isso.

Eu tenho o mesmo problema.

Eu preciso gerar um PDF no lado Android/iOS que demora um pouco e o método compute(...) trava o aplicativo se eu executar a chamada de lá.

Eu tenho esse problema, e agora estou preso.

Eu quero ler accessToken, de preferências compartilhadas, em segundo plano, isolado, mesmo que o aplicativo não esteja em primeiro plano (o isolado principal pode não estar em execução, não tenho certeza).

Então, alguém pode me dizer uma solução para ler dados persistentes do isolado de fundo.
Estou realmente em apuros agora.

/cc @bkonyi Isso parece relacionado a algo em que você está trabalhando.

Eu tenho o mesmo problema. nossa base de produtos em CPU-bound. precisamos usar o método de plataforma de forma isolada. Você poderia nos dizer outra maneira de resolver este problema?

@Thomson-Tsui Eu tive um problema semelhante e, com base em uma destilação do trabalho de @bkonyi e sua amostra FlutterGeofencing, consegui fazer algo funcionar em um isolado que estou usando com flutter_blue e outros plugins. É bastante não testado, mas por favor, tente.

Estou com o mesmo problema, acho importante

Alguém solucionou este problema?

Eu preciso reproduzir áudio em intervalos variados em segundo plano. A maneira mais simples de fazer isso seria ter um Isolate separado gerenciando o áudio, mas sem esse recurso, será necessário um trabalho muito estranho com as portas

Eu tenho uma suposição de que, quando você executa um isolado do dart, as bibliotecas nativas não são vinculadas.

Por exemplo, ao executar o código flutter de java, esse método é chamado:

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

Em que toda a magia acontece)

Isso é verificado pelo fato de que se você criar vários FlutterNativeView, e em cada execução:

  public void runFromBundle (FlutterRunArguments args)

então temos alguns "isolados de dardo"

Também enfrentamos esse problema. Precisamos coletar dados de isolados de fundo (por exemplo, localização, ruído, etc.) que dependem dos canais da plataforma. @mravn-google -- alguma notícia sobre uma atualização da API que poderia resolver isso?

É realmente um problema sério em ferramentas ou aplicativos de mídia.
O encadeamento da plataforma não é uma maneira fácil.

Eu também enfrentei o problema semelhante e preso agora.

+1 (meu projeto também precisa muito de canais de plataforma em isolados)

Eu preciso renderizar imagens em segundo plano. Isso deve ser feito de forma isolada porque cada imagem leva alguns segundos e bloqueia a interface do usuário se for feita no thread principal. Estou preso na primeira chamada nativa de dart:ui.PictureRecorder e suponho que cada chamada gráfica (que usa funções nativas) também não funcionará. Como estou usando muitas funções na tela, seria difícil trabalhar com portas de retorno de chamada.

Uma solução é muito apreciada.

+1 Eu tenho este problema

Parece que @mravn-google parou de funcionar para o Google em Aarhus. Gostaria de saber se há mais alguém da equipe do Google Flutter que está investigando isso. IMO, esta é uma grande rolha para o uso sério do Flutter para a construção de aplicativos Flutter resilientes e robustos....

Talvez @sethladd possa dar um status?

+1

alguma atualização sobre isso?

+1

O dia em que você encontrou um bug que você sabe como resolver sozinho, mas apenas esperar é a última esperança até que eu me torne engenheiro de flutter.

Precisamos de uma explicação mais detalhada da solução alternativa para novos usuários de flutter/dart. Eu só comecei a usar isolados há dois dias, e flutter/dart na semana passada, e parece que vou ter que usar o thread principal para algumas tarefas muito trabalhosas, o que torna o aprendizado de flutter uma solução ruim. Sem multithreading utilizável, é melhor aprender Kotlin e construir meus aplicativos duas vezes. Espero que vocês consigam algo que seja inteligível para iniciantes, eu realmente gostaria de poder justificar o aprendizado do flutter para meus empregadores para que eu possa usá-lo no trabalho.
Se não estiver aqui, talvez no StackOverflow, postei uma pergunta aqui: https://stackoverflow.com/q/57466952/6047611

+1 espero que seja corrigido em breve

+1 Meu projeto precisa de canais de plataforma isolados.

Eu me deparei com esse mesmo problema e fiz um pacote ( Isolate Handler ) usando uma solução semelhante à postada por @mravn-google. No entanto, o Isolate Handler permite que você use canais do isolado principal junto com outros isolados. Ele suporta chamadas diretas de MethodChannel de dentro de isolados, mas atualmente não há suporte para streams EventChannel . Vou procurar adicionar suporte para eles se for algo que as pessoas precisam.

Uma desvantagem da solução alternativa é a necessidade de fornecer nomes de canal ao manipulador isolado. Não é um problema no meu projeto, pois estou escrevendo meu código nativo, mas, caso contrário, você terá que examinar o código-fonte de qualquer plug-in que estiver usando para encontrá-los.

Outra opção que já foi postada acima é o FlutterIsolate , que usa uma abordagem diferente e também vale a pena pesquisar.

Espero que os desenvolvedores do Flutter forneçam uma solução completa e adequada para esse problema em breve.

Editar:

Acabei resolvendo meu problema de forma mais limpa, iniciando o isolamento do lado nativo, em vez de usar uma abordagem semelhante ao plug-in do Flutter Android Alarm Manager .

A única coisa que eu tinha que fazer era converter meu código nativo em plugins para que eles pudessem ser registrados com o aplicativo nativo, o que era uma migração relativamente indolor. Se você usar plugins do pub.dev, é ainda mais simples, pois eles devem funcionar perfeitamente.

+1 alguma atualização aqui?

+1

Coisa interessante que estou notando aqui:
Vários plugins parecem ser escritos com a suposição de que a chamada do canal do método está passando mensagens do Dart para um thread em segundo plano; relacionado, existem algumas pessoas muito confusas que seguiram o exemplo de geofencing bg e agora estão se perguntando como voltar ao tópico principal ...
Esse código de plug-in não causará "jank" se eles estiverem executando uma tarefa curta, mas eles estão, por padrão, sendo executados no thread da plataforma , o que costumava ser chamado de "thread da interface do usuário" no Android; isso pode absolutamente fazer com que os gestos sejam descartados durante a alta carga de trabalho, e entendo que também bloquearia todos os outros canais de mensagens.
Você então fica à mercê da engenharia do autor do plugin; somente se eles moveram seu trabalho pesado para outro encadeamento no código nativo, você estará executando um código que espera nos canais do método; esses canais de mensagens precisam ser executados no encadeamento da plataforma.
Embora eu entenda a lógica que o bom chinmaygarde relata aqui , concordando que:

  • a maioria das chamadas de API da plataforma são rápidas e geralmente precisam ser feitas no encadeamento principal;
  • faz mais sentido considerar threads no código nativo
  • encontrar o caminho de volta para o "thread principal" em uma substituição de método de estrutura é obviamente confuso para pessoas acostumadas ao Android Java

    • Só posso imaginar como isso se multiplicaria ao fazer suas primeiras chamadas de API somente de thread de plataforma.

Eu sinto que a arquitetura resultante abriu um anti-padrão significativo como padrão para autores de plugins; à primeira vista, acho que até o plug-in ml do firebase usa o thread da plataforma para seu processamento.
Então eu gostaria que houvesse algum tipo de aviso forte que seria dado às pessoas que fazem o trabalho pesado; talvez algo no aplicativo de exemplo padrão que gere erros na tela se as chamadas do canal de mensagens demorarem mais de ~20 ms para retornar, talvez algo mais na cara de todos; honestamente, o que quer que convença as pessoas que fazem chamadas de banco de dados etc. para ir buscar um fio já.

Oi, não existe almoço grátis, isso é certo...

Acabei implementando uma carga de trabalho pesada com os threads da plataforma nativa usando Java e Swift. Não encontrou nenhuma solução alternativa.

@AndruByrne Esta é realmente uma informação muito interessante e eu não sabia disso, tão óbvio quanto parece agora em retrospectiva. Eu já tenho um problema relacionado ao meu plugin em que um usuário estava tentando usá-lo como uma maneira de realizar uma tarefa pesada em segundo plano e bloqueou a interface do usuário.

Concordo que deve haver um aviso e vou adicionar um ao meu plugin.

+1 precisa disso

Infelizmente - Flutter não está pronto para o horário nobre para que falte algo tão básico e tão crítico. Nem todos os aplicativos Flutter podem se safar com simples async/await. Para o trabalho pesado, os Isolados são essenciais e os plugins que não podem permitir chamadas de método de canal de plataforma de outro Isolate os tornam inúteis e redundantes. O Xamarin tinha isso embutido na estrutura desde o primeiro dia - certamente isso deve ser votado para uma solução agora.

+1 precisa disso

+1

Eu preciso chamar um método de canal de plataforma de outro isolado também!

+1

+1

+1

+1 Este é um deve ter

+1
Eu tenho um SDK nativo que devo chamar. Seria bom ter algo como BackgroundFlutterMethodChannel .

+1

+1

Alguém tem alguma informação sobre o que _crashes mal_ realmente descreve? Estou trabalhando em uma prova de conceito que implementa uma biblioteca nativa e estou executando tempos limite de watchdog (0x8badf00d) e outros erros estranhos ao enviar mensagens de um lado para o outro por meio de um canal de método.

Suspeito que isso seja causado por alguns métodos [e resultados] sendo invocados de outros threads, mas não consegui fazer nenhum progresso na identificação do problema exato.

+1

+1

+1

+1

+1 precisa disso

+1 precisa disso também!

Acho que essa questão deveria ser priorizada pelo menos como P2.
Os usos dos canais da plataforma que consigo pensar são

  • Para chamar APIs específicas da plataforma
  • Para executar tarefas pesadas específicas da plataforma em segundo plano e relatar quando terminar

E este é um grande bloqueador.

O plug-in flutter_downloader lida com código de isolamento em segundo plano. Acho que vale a pena dar uma olhada.

eu esperava que isso fosse resolvido na próxima versão, mas o bug ainda permanece na versão 1.12!

nosso código do lado do cliente está fortemente do outro lado do canal e é escrito em kotlin e o aplicativo quase trava para cada conexão http única

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

Este plugin pode ser útil flutter_isolate

+1 precisamos disso... vamos lá

+1

O maior problema para mim é que a comunicação entre o Dart e as plataformas nativas acontece no encadeamento principal - é impossível enviar pedaços significativos de dados sem atrasar a interface do usuário ou escrever código complicado nos dados da página.

+1 precisa disso

escrever código complicado para dados de página.

@lukaszciastko o que você quer dizer com escrever código nos dados da página. Existe uma maneira de resolver o atraso da interface do usuário ao enviar blocos significativos de dados?

@YaredTaddese

Dependendo da complexidade do que seu ISOLATE faz e da intercomunicação entre sua interface do usuário e o ISOLATE - é possível contornar esse problema em seu código de interface do usuário estruturando quais dados o ISOLATE requer para fazer seu trabalho antecipadamente antes de invocar o ISOLATE. Eu tenho geração de PDF rico no meu aplicativo Flutter atual - um usuário pode selecionar vários PDFs para gerar.

Cada um pode gerar de 1 a 10.000 páginas. Minha interface do usuário é responsiva, quando cada trabalho é concluído, uma TAB é construída mostrando o relatório - o usuário é notificado quando um trabalho é concluído por meio de um TOAST e a interface do usuário não para - o usuário não sabe que existem serviços em segundo plano ocupados gerando PDFs ricos.

Observe que minha geração de PDF é um ISOLATE. No ISOLATE -, também preciso baixar imagens do CLOUD FIRESTORE e incorporar em um PDF - tudo isso acontece perfeitamente.

@MsXam , mas não consigo chamar uma função específica da plataforma de outro isolado ... o que me faz chamar a função específica da plataforma no isolado principal ... o que leva a uma interface do usuário atrasada

Estou com o mesmo problema, acho muito importante

@YaredTaddese

Dependendo da complexidade do que seu ISOLATE faz e da intercomunicação entre sua interface do usuário e o ISOLATE - é possível contornar esse problema em seu código de interface do usuário estruturando quais dados o ISOLATE requer para fazer seu trabalho antecipadamente antes de invocar o ISOLATE. Eu tenho geração de PDF rico no meu aplicativo Flutter atual - um usuário pode selecionar vários PDFs para gerar.

Cada um pode gerar de 1 a 10.000 páginas. Minha interface do usuário é responsiva, quando cada trabalho é concluído, uma TAB é construída mostrando o relatório - o usuário é notificado quando um trabalho é concluído por meio de um TOAST e a interface do usuário não para - o usuário não sabe que existem serviços em segundo plano ocupados gerando PDFs ricos.

Observe que minha geração de PDF é um ISOLATE. No ISOLATE -, também preciso baixar imagens do CLOUD FIRESTORE e incorporar em um PDF - tudo isso acontece perfeitamente.

Você está chamando um código específico da plataforma?

Alguém tem uma solução alternativa para chamar canais de plataforma de um isolado? Isso anula a idéia de fazê-lo a partir do thread de interface do usuário.

Estou preso na mesma coisa.

Estou chamando um methodchannel com um código kotlin e a API getStream.io, mas isso é lento como o inferno, e minha interface do Flutter congela, eu queria adicionar computação a ele, mas recebi os mesmos erros.

Como posso consertar isso?

Acabei de passar por isso também. Estou prototipando um novo aplicativo e, na verdade, esse problema pode ser um impedimento para mim e me fazer procurar outras soluções que não sejam Flutter, o que me deixará triste.

Como o volume de respostas a esse problema indica, esse é um problema GRANDE e praticamente derrota o principal caso de uso de isolados no contexto de um aplicativo Flutter.

Acabei de passar por isso também. Estou prototipando um novo aplicativo e, na verdade, esse problema pode ser um impedimento para mim e me fazer procurar outras soluções que não sejam Flutter, o que me deixará triste.

Como o volume de respostas a esse problema indica, esse é um problema GRANDE e praticamente derrota o principal caso de uso de isolados no contexto de um aplicativo Flutter.

sim, este é um grande problema, precisa corrigir o mais rápido possível

Estou aprendendo flutter há 10 dias, e estou adorando até agora, mas depois de um tempo experimentando, você começa a ter problemas e ver as desvantagens, e ESSA edição, aberta desde 5 de janeiro de 2018, oof...

Por favor, corrija este bug.

Espero apoiar em breve.

@jpsarda , @klaszlo8207 Não prenda a respiração. E sim, *houve uma solução para o problema, até hoje: https://pub.dev/packages/isolate_handler. O programador teve que desligá-lo hoje porque uma mudança muito recente no Flutter (com apenas alguns dias) tornou impossível seguir esse caminho.

Você tem que saber, no entanto, que isso não significa realmente paralelismo. Pode ter parecido assim e a aparência é importante em aplicativos móveis, mas o código da plataforma é executado no encadeamento principal de qualquer maneira, portanto, delegar uma longa tarefa de volta do isolado para o encadeamento principal não conseguiu realmente nada.

Seria bom ter o dart manipulando o multithreading por padrão sempre que você usar uma função assíncrona.

@spiderion Async nunca foi feito para ser multithreading, assim como em muitas outras plataformas. Não me entenda mal, não vou dizer que a limitação do canal da plataforma não é um grande problema porque é (meu aplicativo estava trabalhando com o plugin que mencionei até ontem e agora tenho que procurar soluções alternativas), mas async/ await nunca foi sobre multithreading desde o primeiro dia.

@zoechi Gostaríamos de saber se há alguma atualização sobre esse problema ou se há algum plano para corrigi-lo?
Obrigada.

@spiderion Eles realmente não podem consertá-lo, segue a maneira como os isolados funcionam. Eles não têm um mecanismo de interface do usuário por trás deles, portanto, não há comunicação de canal de plataforma. No entanto, existem dois plugins para ajudá-lo:

  • https://pub.dev/packages/flutter_isolate fornece um isolado de substituição que pode se comunicar com plugins porque cria seu próprio suporte de interface do usuário (nada que você vê ou precisa lidar, apenas tecnicamente),

  • https://pub.dev/packages/isolate_handler que acabamos de modificar para depender do pacote acima porque a maneira anterior que ele usava foi impossibilitada por uma alteração recente do Flutter. A vantagem de usar este pacote em vez do flutter_isolate si é que isso adiciona recursos de manuseio, você pode iniciar vários isolados, acompanhá-los e não precisa configurar sua própria comunicação entre o isolado e o principal thread (algo que você precisa fazer manualmente com o estoque original Isolate e FlutterIsolate ) porque é abstrato e prontamente disponível.

Eu uso o segundo com perfeito sucesso. Na verdade, eu o uso há algum tempo e quando a mudança de interrupção do Flutter veio, ajudei seu programador a passar para a nova solução apenas porque meu aplicativo também quebrou. :-)

Portanto, não acho razoável esperar a solução do núcleo, especialmente porque a maioria dos casos de uso não exige comunicação de canal de plataforma, portanto, o mecanismo de interface do usuário de apoio não é algo que eles gostariam de adicionar a cada isolado. Basta usar os plugins existentes quando precisar dessa funcionalidade extra.

Oi @deakjahn obrigado por sua resposta rápida.

As soluções que você forneceu parecem ser muito úteis. No entanto, não sei se isso poderia resolver o meu problema. Atualmente tenho uma situação no aplicativo onde o usuário cria uma história digamos semelhante ao Instagram. O aplicativo, nesse caso, precisa carregar no armazenamento do Firebase uma lista de vídeos e arquivos que podem levar muito tempo para carregar (cerca de 15 minutos se a conexão for lenta). um documento na nuvem do Firebase, Firestore. O problema que encontro é que, se o usuário encerrar o aplicativo enquanto os arquivos estiverem sendo carregados, a tarefa completa não será totalmente executada.

Isso não é realmente responsabilidade do isolado, ele apenas faz o que você manda. O Firebase pode retomar descarregamentos interrompidos, até onde posso ver, embora eu nunca o tenha usado.

Estou tentando executar o exemplo getBatteryLevel:
https://flutter.dev/docs/development/platform-integration/platform-channels?tab=android-channel-java-tab
e ele trava toda vez que tento executá-lo em um dispositivo Android. Alguém pode ajudar por favor? Não tenho certeza de como verificar o erro, pois não há logs e ele continua sendo executado em um loop infinito e estou usando exatamente o mesmo código.

Rotular este problema para P5 ??? 🥵

Por favor, verifique os comentários anteriores. Parece muito improvável que isso seja alterado na estrutura porque requer mecanismos que a maioria dos isolados não precisa, portanto, não faz sentido adicionar essa sobrecarga. Para os casos em que você precisar, existem pacotes para resolvê-lo.

Os usuários não se importam com o mecanismo; quão difícil é ou não. Eles só precisam de resultados. Isso pode ser impossível de fazer. Mas se os usuários precisarem, o Flutter deve fornecer...

Entendo que a implementação tem limitações e pode levar muito tempo para adicionar esse recurso ao isolado existente ou criar uma nova API com esse recurso. Eu só acho que isso deveria ter uma prioridade maior do que P5.

Além disso, li todos os seus comentários e me parece que você está apenas promovendo o pacote que criou.

Não, não fui eu que o criei, mas aceitei sua propriedade algumas semanas atrás, sim. Acho que isso não foi mantido em segredo neste tópico. Além disso, mencionei dois pacotes (em um único post, na verdade, apenas uma vez) que são capazes de fornecer o que você procura e não tenho afiliação com o segundo além de contar com o excelente trabalho feito até agora.

Se você leu, a única razão pela qual eu usei isso em primeiro lugar foi que eu precisava da mesma funcionalidade que você pediu. E meus aplicativos contam com ele há mais de um ano, sem problemas. Além disso, ficar mais familiarizado com o funcionamento interno de isolados do que um desenvolvedor ocasional apenas os usando (além de que comecei a trabalhar em sua contraparte no Flutter Web), me faz pensar que o que escrevi era verdade: essa funcionalidade não será incorporados na estrutura porque há muitos argumentos contra sua inclusão. Não porque levaria muito tempo, nem um pouco. Levaria relativamente pouco tempo. Simplesmente porque adicionaria uma complexidade de back-end a todos os isolados do que apenas alguns deles realmente precisariam e usariam.

@deakjahn obrigado por essas dicas, o uso de isolated_handler me ajudará a invocar um método e enviar dados para o lado do dardo sempre que eu receber uma notificação no lado nativo? (Estou usando sdks nativos do twilio e recebendo notificações push no lado nativo)

Se isso significa que você tem um plug-in de patform que você usa para se comunicar através de um canal de plataforma, então sim, qualquer um dos dois pacotes ajudaria. Se funcionar a partir de código normal, esses pacotes também farão com que funcione de um isolado.

@deakjahn Obrigado pela resposta rápida! Meu problema é que não consegui fazer isso com um canal de plataforma regular, não tenho certeza, mas acho que o sdk twilio lida com a escuta de notificações push em um isolado separado e não no thread principal, e acho que esse é o meu problema. Eu queria saber se funcionaria se eu invocasse o método toda vez que recebo uma notificação usando um dos 2 pacotes ?muito obrigado. (Estou pensando em mudar para aplicativos nativos se isso não for possível)

Eu nem sei o que é Twilio (OK, eu pesquisei :-) ), então eu realmente não posso ter certeza. Mas de qualquer forma, se você quiser fazer interface com qualquer código nativo, você tem que usar um canal de plataforma, de qualquer maneira. Ou você usa um plugin já existente que alguém criou para usar este Twilio ou você mesmo o cria (nesse caso, você cria praticamente o mesmo plugin, apenas não o publica e se refere a ele localmente), ou simplesmente copia o plugin relevante código em seu próprio aplicativo (não será uma dependência externa, mas fora isso, terá praticamente o mesmo código, de qualquer maneira).

E, se você tiver um canal de plataforma, a resposta anterior se aplica: se você já pode usar o canal de plataforma e o plug-in do thread principal de um aplicativo Flutter regular, pode usar o mesmo de um isolado usando qualquer um dos dois pacotes.

Portanto, esse problema existe desde 5 de janeiro de 2018 e ainda não é possível com esforço extra. As soluções fornecidas são boas - mas ainda não há nada funcionando na área de trabalho. Portanto, para quem quiser testar o quão bem o flutter está funcionando no desktop e ver se é possível um produto futuro, ficará totalmente preso aqui.
Quase todos os aplicativos precisam de isolamentos, porque sempre há alguns cálculos enormes, como é possível que não haja nada acontecendo em falhas de design tão importantes da API?

Eu adicionei uma saída para isso, no meu plugin System Alert Window

A solução é mencionada aqui Isolar a comunicação .
Embora o exemplo fornecido fale especificamente sobre o plug-in da janela de alerta do sistema, ele pode ser facilmente replicado para outros plug-ins. E não requer outros plugins sofisticados para fazê-lo funcionar!

Isso ainda pode ser bastante problemático. Isso significa que não há como chamar um plugin diretamente de um isolado que está sendo executado em segundo plano/primeiro plano, porque você teria que enviar uma mensagem para o escopo do aplicativo para fazer qualquer coisa com um plugin. Portanto, se seu aplicativo não estiver em execução, não há como fazer nada com um plug-in no backgroud.

Por exemplo, se você quiser fazer qualquer coisa com preferências compartilhadas no backgroundMessageHandler do plug-in FCM quando o aplicativo for fechado, ele lançará um MissingPluginException.

Ou se você não quiser abrir o aplicativo com uma janela de alerta do sistema. A janela de alerta está sendo executada em um isolado diferente. Então você teria que executar o código dentro do escopo do seu aplicativo. Isso é problemático, porque o aplicativo está fechado no momento.

E provavelmente há muitos outros senários onde isso é um grande problema.

@michael-ottink os cenários que você descreve são precisamente aqueles em que não deveria haver um problema. Plugins que executam código em segundo plano geralmente instanciam um FlutterEngine totalmente novo com seu próprio registro de plug-in e o isolado que é executado dentro dele poderá se comunicar diretamente pelos canais da plataforma para esses plug-ins.

Aliás, isso significa que, se você precisar de um isolado para usar plugins, a maneira mais fácil é apenas envolver esse isolado em um FlutterEngine e ele herdará magicamente esses poderes. Isso é o que o pacote flutter_isolate faz para você no Android e no iOS (embora eu concorde que seria melhor ter uma correção adequada em vez de uma solução alternativa. todas as plataformas.)

@ryanheise então flutter_isolate faz isso por mim? Como eu realmente não posso dizer a partir dos documentos. O que preciso fazer para que funcione para meu FCM onBackgroundMessage? Então eu preciso gerar um novo isolado no meu onBackgroundMessageHandler? Mas eu não posso usar plugins lá, então como eu usaria o flutter_isolate então?

Seu problema é que o firebase_messaging não é atualizado há muito tempo e está muito atrasado em relação às APIs de execução em segundo plano mais recentes. Se eles atualizassem o plugin, você não teria mais esse problema. Apenas para esclarecer novamente, o tipo de cenário que você descreve é ​​exatamente aquele em que não deve haver um problema porque os isolados de fundo já devem ter acesso a plugins se implementados corretamente. o plugin firebase_messaging não está implementado de acordo com as APIs mais recentes e é por isso que não funciona para você. Você pode enviar um relatório de bug com esse projeto.

Estou precisando urgentemente de ajuda com isso. Existe possivelmente alguma maneira que eu posso fazer isso. Eu tenho meu primeiro contrato sério amanhã e eu simplesmente não consigo descobrir isso.

Faça tudo para garantir que os plugins estejam sendo carregados. Não sei se você seguiu as instruções do README sobre como habilitar mensagens em segundo plano, mas você precisa criar uma classe de aplicativo personalizada que se conecte à inicialização do FlutterNativeView em segundo plano do plug-in. Se você não tiver feito isso, nenhum dos plugins estará disponível para você usar. Se isso ainda não estiver funcionando, você pode tentar fazer o downgrade do seu projeto para a antiga arquitetura de plug-in estilo v1 (e arriscar quebrar outros plug-ins que exigem v2).

Eu configurei meu Application.kt assim

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

É melhor postar isso na página de problemas do firebase_messaging porque não está relacionado a esse problema.

Eu posso fazer um PR, que terá um novo método 'spawnIsolate' para 'SchedulerBinding'.

Então será possível chamar métodos de plataforma neste isolado.

Isso só o ajudará se você precisar chamar um método de plataforma e obter uma resposta.

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

Registro

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

Eu posso fazer um PR, que terá um novo método 'spawnIsolate' para 'SchedulerBinding'.

Então será possível chamar métodos de plataforma neste isolado.

Isso só o ajudará se você precisar chamar um método de plataforma e obter uma resposta.

SchedulerBinding.instance.spawnIsolate

Alguma chance de testar isso? Se atende minhas necessidades etc..

@Nailik
Substitua este arquivo (Real para a versão 1.17.5)


ligação.dart

```dart// Copyright 2014 The Flutter Authors. Todos os direitos reservados.
// O uso deste código-fonte é regido por uma licença estilo BSD que pode ser
// encontrado no arquivo LICENSE.

import ' dart:async ';
import ' dart:isolar ';
import ' dart:typed_data ';
import ' dart:ui ' como ui;

import ' pacote:flutter/foundation.dart ';

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

/// Escuta as mensagens da plataforma e as direciona para o [defaultBinaryMessenger].
///
/// O [ServicesBinding] também registra um [LicenseEntryCollector] que expõe
/// as licenças encontradas no arquivo LICENSE armazenado na raiz do ativo
/// pacote e implementa a extensão de serviço ext.flutter.evict (veja
/// [despejar]).
mixin ServicesBinding em BindingBase {
@sobrepor
void initInstances() {
super.initInstances();
_instância = isso;
_defaultBinaryMessenger = createBinaryMessenger();
window.onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
initLicenças();
SystemChannels.system.setMessageHandler(handleSystemMessage);
}

/// O atual [ServicesBinding], se um tiver sido criado.
static ServicesBinding get instance => _instance;
static ServicesBinding _instance;

/// A instância padrão de [BinaryMessenger].
///
/// Isso é usado para enviar mensagens do aplicativo para a plataforma e
/// mantém o controle de quais manipuladores foram registrados em cada canal para
/// ele pode enviar mensagens recebidas para o manipulador registrado.
BinaryMessenger get defaultBinaryMessenger => _defaultBinaryMessenger;
BinaryMessenger _defaultBinaryMessenger;

/// Cria uma instância padrão [BinaryMessenger] que pode ser usada para enviar
/// mensagens da plataforma.
@protegido
BinaryMessenger createBinaryMessenger() {
return const _DefaultBinaryMessenger._();
}

/// Manipulador chamado para mensagens recebidas no [SystemChannels.system]
/// canal de mensagem.
///
/// Outras ligações podem substituir isso para responder às mensagens recebidas do sistema.
@protegido
@mustCallSuper
FuturohandleSystemMessage(Object systemMessage) assíncrono { }

/// Adiciona licenças relevantes ao [LicenseRegistry].
///
/// Por padrão, a implementação do [ServicesBinding] de [initLicenses] adiciona
/// todas as licenças coletadas pela ferramenta flutter durante a compilação.
@protegido
@mustCallSuper
void initLicenças() {
LicenseRegistry.addLicense(_addLicenses);
}

Stream_addLicenses() assíncrono* {
// Usamos temporizadores aqui (em vez de scheduleTask da ligação do agendador)
// porque a camada de serviços não pode usar a ligação do agendador (o agendador
// a vinculação usa a camada de serviços para gerenciar seus eventos de ciclo de vida). Temporizadores
// são o que o scheduleTask usa sob o capô de qualquer maneira. A única diferença é
// que eles serão executados em seguida, em vez de serem priorizados em relação a
// as outras tarefas que podem estar em execução. Usando _something_ aqui para quebrar
// isso em duas partes é importante porque os isolados demoram um pouco para copiar
// dados no momento, e se recebermos os dados no mesmo loop de eventos
// iteração à medida que enviamos os dados para o próximo isolado, estamos definitivamente
// vai perder frames. Outra solução seria ter o trabalho todo
// acontece em um isolado, e podemos chegar lá eventualmente, mas primeiro estamos
// vamos ver se a comunicação isolada pode ficar mais barata.
// Veja: https://github.com/dart-lang/sdk/issues/31959
// https://github.com/dart-lang/sdk/issues/31960
// TODO(ianh): Remova essa complexidade assim que esses bugs forem corrigidos.
finalizador finalrawLicenses = Completer();
Timer.run(() assíncrono {
rawLicenses.complete(rootBundle.loadString('LICENSE', cache: false));
});
aguarde rawLicenses.future;
finalizador final> parsedLicenses = Completer>();
Timer.run(() assíncrono {
parsedLicenses.complete(compute(_parseLicenses, await rawLicenses.future, debugLabel: 'parseLicenses'));
});
aguarde analisadoLicenses.future;
rendimento* fluxo.fromIterable(aguarda analisadaLicenses.future);
}

// Isso é executado em outro isolado criado por _addLicenses acima.
Lista estática_parseLicenses(String rawLicenses) {
final String _licenseSeparator = '\n' + ('-' * 80) + '\n';
Lista finalresultado =[];
Lista finallicenças = rawLicenses.split(_licenseSeparator);
for (licença de String final em licenças) {
final int split = license.indexOf('\n\n');
if (dividir >= 0) {
result.add(LicenseEntryWithLineBreaks(
license.substring(0, split).split('\n'),
licença.substring(dividir + 2),
));
} senão {
result.add(LicenseEntryWithLineBreaks(const[], licença));
}
}
retorno resultado;
}

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

}

/// Chamado em resposta à extensão de serviço ext.flutter.evict .
///
/// Isso é usado pela ferramenta flutter durante o hot reload para que todas as imagens
/// que foram alterados no disco são limpos dos caches.
@protegido
@mustCallSuper
void evict(String ativo) {
rootBundle.evict(recurso);
}

FuturospawnIsolar(
FuturoOuponto de entrada (mensagem T),
mensagem T, {
bool pausado = false,
bool errorsAreFatal,
SendPort onExit,
SendPort onError,
String debugName,
}) {
afirmar(
_isMainIsolate,
'Não é possível fazer vários níveis de isolados',
);

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

}
}

Futuro_startIsolar(SendPort sendPort) assíncrono {
_sendPortToMainIsolate = sendPort;
_IsolateBinding();

Finalizador final<_IsolateStarter> completador =
Completor<_IsolateStarter>();

final RawReceivePort receivePort = RawReceivePort(
(Objeto isoladoStarter) {
assert(isolateStarter é _IsolateStarter);
completer.complete(isolateStarter as _IsolateStarter);
},
);

sendPort.send(receivePort.sendPort);

final _IsolateStarterisolarStarter = aguardar completer.future;

receberPorta.close();

função função final =
ui.PluginUtilities.getCallbackFromHandle(isolateStarter.callbackHandle);

função de espera(isolateStarter.message);
}

SendPort _sendPortToMainIsolate;

bool get _isMainIsolate => _sendPortToMainIsolate == null;

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

final ui.CallbackHandle callbackHandle;
mensagem T final;
}

class _Mensagem {
_Mensagem(
este.canal,
esta mensagem,
this.sendPort,
);

canal String final;
mensagem final ByteData;
final SendPort sendPort;
}

classe _IsolateBinding estende BindingBase com ServicesBinding {}

/// A implementação padrão de [BinaryMessenger].
///
/// Este mensageiro envia mensagens do lado do aplicativo para o lado da plataforma e
/// despacha as mensagens recebidas do lado da plataforma para o local apropriado
/// manipulador.
class _DefaultBinaryMessenger estende BinaryMessenger {
const _DefaultBinaryMessenger._();

// Manipuladores para mensagens recebidas de plugins da plataforma.
// Isso é estático para que esta classe possa ter um construtor const.
Mapa final estático_manipuladores =
{};

// Manipuladores simulados que interceptam e respondem a mensagens enviadas.
// Isso é estático para que esta classe possa ter um construtor const.
Mapa final estático_mockHandlers =
{};

Futuro_sendPlatformMessage(canal de string, mensagem ByteData) {
finalizador finalcompletador = completador();

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;

}

@sobrepor
FuturohandlePlatformMessage(
canal de cordas,
Dados ByteData,
ui.PlatformMessageResponseCallback callback,
) assíncrono {
resposta ByteData;
experimentar {
final MessageHandler manipulador = _handlers[canal];
if (manipulador != null) {
resposta = aguardar manipulador(dados);
} senão {
ui.channelBuffers.push(canal, dados, retorno de chamada);
retorno = null;
}
} catch (exceção, pilha) {
FlutterError.reportError(FlutterErrorDetails(
exceção: exceção,
pilha: pilha,
biblioteca: 'biblioteca de serviços',
context: ErrorDescription('durante um retorno de chamada de mensagem da plataforma'),
));
} finalmente {
if (retorno de chamada!= null) {
retorno de chamada(resposta);
}
}
}

@sobrepor
Futurosend(canal de string, mensagem ByteData) {
manipulador final MessageHandler = _mockHandlers[canal];
if (manipulador != null)
manipulador de retorno (mensagem);
return _sendPlatformMessage(canal, mensagem);
}

@sobrepor
void setMessageHandler(canal de string, manipulador MessageHandler) {
if (manipulador == null)
_handlers.remove(canal);
senão
_handlers[canal] = manipulador;
ui.channelBuffers.drain(canal, (dados ByteData, retorno de chamada ui.PlatformMessageResponseCallback) async {
await handlePlatformMessage(canal, dados, retorno de chamada);
});
}

@sobrepor
void setMockMessageHandler(canal de string, manipulador MessageHandler) {
if (manipulador == null)
_mockHandlers.remove(canal);
senão
_mockHandlers[canal] = manipulador;
}
}
```

Eu tenho o flutter 1.21, então tentei atualizar todo o código para compilar.
Agora resta um problema porque _IsolateBinding está faltando muitas implementações de métodos BindingBase ...

eu recebo algo como

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

cerca de 30 vezes.

No final o console imprime

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

Não tenho 100% de certeza do que a chamada de _IsolateBinding() em _startIsolate faz, mas sem o erro comum ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized. .

Eu tentei consertar isso, mas acho que meu conhecimento de mixins ainda não é tão bom.
Alguma idéia de como consertar isso?


O novo arquivo se parece com isso (até agora)

// 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
No momento, não posso atualizar o flutter para ajudar você

Estou me perguntando por que as outras ferramentas de flutter implementadas pela equipe de flutter (como, digamos, devtools) nunca atingem esse bloqueador.
Eles parecem estar corrigindo bugs apenas se forem bloqueados por eles.

Este não é um novo recurso grave, é um bloqueador de estrutura.
Então, de acordo com isso https://github.com/flutter/flutter/issues/18761#issuecomment -639248761 deve ser um P3

Estou me perguntando por que as outras ferramentas de flutter implementadas pela equipe de flutter (como, digamos, devtools) nunca atingem esse bloqueador.
Eles parecem estar corrigindo bugs apenas se forem bloqueados por eles.

Para ser honesto, porque acho que o impacto desse problema nos aplicativos do mundo real é superestimado. Estou apenas observando o problema porque facilitaria as coisas, mas existem "soluções alternativas". E muitos aplicativos e plugins em produção estão usando a funcionalidade em segundo plano.

Também não tenho certeza se alguns dos +1s simplesmente não entendem bem do que se trata o problema. (Curta https://github.com/flutter/flutter/issues/13937#issuecomment-635683123 ). Ainda falta um pouco a documentação oficial para isolados em segundo plano e tempo de execução sem cabeça (e como gerenciá-lo) e o material de mensagens do Firebase é um pesadelo, mas isso não tem nada a ver com a estrutura, o mecanismo 🤷‍♂️

Talvez pare de criticar a equipe do flutter, há um progresso incrível, e isso não é algo que não pode ser contornado ... E se houver um PR, por que não enviá-lo para a equipe do flutter considerar 🤔

Há um problema com a implementação desse comportamento para isolados.

Exemplo:

Há um isolador principal e um motor.
Se o isolado precisar de algo, ele pede ao motor.
Se o motor precisar de algo, ele pede o isolado.

Agora nossa situação é:
Existem 2 isolados e um motor.
Se o isolado precisar de algo, ele pede ao motor.
Se o motor precisar de algo, o que deve fazer?
Pergunte aos dois? Então a resposta de quem tomar?
Ou pedir apenas um isolado? Então qual?

@hpoul Eu tenho que concordar em discordar, pois muitas pessoas aqui são iniciantes na programação android/ios e escolheram sua primeira estrutura para o desenvolvimento de aplicativos para ser flutter.

Portanto, é importante que alguém perceba isso e corrija ou sugira uma solução oficial com um exemplo para esses tipos de problemas em que você é bloqueado e forçado pela estrutura a fazer "soluções alternativas" complexas, pois obviamente os iniciantes não saberão o que aprender ou mesmo por onde começar porque não há soluções alternativas bem explicadas nos comentários aqui ou em qualquer lugar no stackoverflow ou em outros blogs. Há apenas coisas apontando para alguns pacotes que podem ou não ter implementado isso corretamente.

As pessoas que têm muita experiência trabalhando com serviços, filas de tarefas em segundo plano etc., tanto no Android quanto no ios, que podem facilmente contornar isso não são o público-alvo da vibração.

E chegando a comentários fora do tópico como esse, tenho certeza de que há falsos +1s em todos os outros problemas, o que não altera muito a ordem das prioridades dos problemas e esse problema ainda permanece no topo.

Olá @phanirithvij

flutter_isolate foi vinculado acima e existe desde fevereiro de 2019. Também não é complexo. Vou copiar o README que parece simples:

FlutterIsolate permite a criação de um Isolate em flutter que é capaz de usar plugins de flutter. Ele cria os bits específicos da plataforma necessários (FlutterBackgroundView no Android e FlutterEngine no iOS) para permitir que os canais da plataforma funcionem dentro de um isolado.

| | Android | iOS | Descrição |
| :--------------- | :----------------: | :------------------: | :-------------------------------- |
| FlutterIsolate.spawn(entryPoint,message) | :white_check_mark: | :white_check_mark: | gera um novo FlutterIsolate |
| flutterIsolate.pause() | :white_check_mark: | :white_check_mark: | pausa um isolado em execução |
| flutterIsolate.resume() | :white_check_mark: | :white_check_mark: | retomou um isoalte pausado |
| flutterIsolate.kill() | :white_check_mark: | :white_check_mark: | mata um isolado |

Digo simples, pois esses métodos têm os mesmos nomes que os da classe Isolate "original", portanto, se você já sabe como usar isolados da documentação oficial, não deve ser difícil entender como usar isso como uma substituição imediata, pelo menos para os métodos listados acima.

(Observação: eu também gostaria que esse problema fosse corrigido oficialmente por motivos que já declarei em um comentário anterior, alguns dos quais compartilho com você, mas também não chamaria essa solução alternativa de "complexa".)

Em resposta ao comentário de @nikitadol , flutter_isolate cria um mecanismo separado para cada isolado para que não ocorra o mesmo problema. Claro, não é ideal ter que criar um novo motor por isolado, essa é uma das razões pelas quais tenho certeza que a equipe Flutter poderia fazer um trabalho melhor. Da mesma forma, a solução alternativa de canalizar todas as chamadas de método de plug-in através do isolado principal criaria um gargalo que contraria os objetivos dos isolados em primeiro lugar, então tenho certeza que a equipe do Flutter também poderia fazer um trabalho melhor.

Concordo com aqueles que disseram que a equipe do Flutter pode ter problemas de maior prioridade para resolver primeiro, mas com base no que vimos antes com a arquitetura do plug-in, também pode ser que corrigir isso exija uma grande mudança na arquitetura e vários outros problemas em aberto também exigem uma grande mudança de arquitetura, e ponderar todos esses diferentes requisitos pode levar algum tempo antes que eles possam resolver esse problema específico.

@ryanheise

flutter_isolate cria um mecanismo separado para cada isolado

então tenho certeza que a equipe do Flutter também poderia fazer um trabalho melhor.

Você diz que a equipe Flutter pode fazer isso, mas como?
As perguntas que fiz acima continuam válidas.
Se a solução for criar um novo mecanismo, você pode simplesmente usar seu plug-in

(Observe que flutter_isolate não é meu plugin, mas como eu também precisava desse recurso, decidi contribuir com o projeto.)

O que eu quis dizer com "um trabalho melhor" é que provavelmente não é necessário ativar TODAS as máquinas em um FlutterEngine apenas para se comunicar com plugins, mas no momento, a infraestrutura necessária está toda vinculada ao mecanismo, então é por isso que atualmente flutter_isolate precisa ativar um mecanismo inteiro apenas para obter acesso aos plugins.

(Observe que flutter_isolate não é meu plugin, mas como eu também precisava desse recurso, decidi contribuir com o projeto.)

Desculpe, não percebi que isso é uma bifurcação

flutter_isolate precisa ativar um mecanismo inteiro apenas para obter acesso aos plugins.

Se você especificar que 'is_background_view = true', o mecanismo não iniciará a renderização - o que significa que o mecanismo não iniciará completamente

A API precisa para executar headless é diferente para iOS e Android v1 e Android v2, mas essencialmente é isso que flutter_isolate já faz. Ainda há uma diferença na sobrecarga entre um isolado normal e um FlutterEngine, portanto, reduzir ainda mais as despesas gerais é apenas algo que a equipe do Flutter seria capaz de fazer. Não é apenas uma sobrecarga no tempo de inicialização, é também uma sobrecarga no uso da memória.

Então você sugere apenas otimizar a criação de um novo mecanismo para funcionar em segundo plano?

Então isso provavelmente não se aplica a este problema

Não estou sugerindo isso, estou apenas apontando que a solução alternativa flutter_isolate tem essa limitação devido às sobrecargas que não podem ser evitadas. Não estou sugerindo que a equipe do Flutter tente tornar a solução alternativa mais eficiente, acho que idealmente haveria uma mudança arquitetural na maneira como os plugins são carregados para que não seja necessário instanciar um FlutterEngine.

Se o motor for 1 e os isolados forem maiores que 1, voltamos a esta questão:

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

Não estou tentando realmente encontrar uma solução para essa nova arquitetura leve, mas se você está me perguntando, posso pensar em duas variações de solução leve:

  1. Torne os canais de método um pouco como soquetes de servidor, fazendo com que aceitem conexões de vários clientes. Portanto, um canal de método no lado da plataforma do plug-in pode aceitar conexões de vários isolados e, uma vez conectado, pode enviar mensagens para o isolado correto.
  2. Crie um novo registro de plug-in etc. para cada isolado, MAS dentro do mesmo FlutterEngine,

se você está me perguntando

Sim, eu pergunto

poderia enviar mensagens para o isolado certo.

Eu acho que essa é uma solução ruim, já que 'isolar certo' é um conceito relativo

Crie um novo registro de plug-in etc. para cada isolado, MAS dentro do mesmo FlutterEngine,

Esta pode ser uma boa opção, mas apenas se o mecanismo sempre registrar plugins por si só.

Mas isso só acontece se todos os plugins no GeneratedPluginRegistrant.
https://github.com/flutter/engine/blob/f7d241fd8a889fadf8ab3f9d57918be3d986b4be/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L330 -L344

Criar retornos de chamada adicionais não é um comportamento óbvio.

Eu acho que essa é uma solução ruim, já que 'isolar certo' é um conceito relativo

Pode ser uma solução ruim, mas não pode ser ruim por esse motivo. O "isolar direito" é inequívoco da mesma forma que um soquete de servidor lida com conexões de cliente de maneira inequívoca.

Esta pode ser uma boa opção, mas apenas se o mecanismo sempre registrar plugins por si só.

O que seria uma coisa boa para garantir que sempre aconteça. Atualmente o mecanismo é um pouco esquisito.

Pode ser uma solução ruim, mas não pode ser ruim por esse motivo. O "isolar direito" é inequívoco da mesma forma que um soquete de servidor lida com conexões de cliente de maneira inequívoca.

Então cada plugin deve levar em conta que pode ser acessado de diferentes isolados - ruim

Sim.

Deixe-me acrescentar meus dois centavos a esta discussão... Tendo trabalhado comercialmente com o Flutter por cerca de dois anos, a coisa mais importante que aprendi foi que o Flutter é um kit de ferramentas de interface do usuário. Não haveria nada de surpreendente nesta declaração - até diz isso no site principal do Flutter - se as pessoas não esquecessem disso.

Flutter é um kit de ferramentas de interface do usuário, e o que faz bem é a interface do usuário. Se você está tentando criar um aplicativo com CPU pesada que depende de processamento de dados caro ou usa algoritmos complicados e consome grandes quantidades de dados, você NÃO deve usar um kit de ferramentas de interface do usuário para essa finalidade. A lógica de negócios não deve ser misturada com a interface do usuário.

É claro que com o Dart podemos fazer muito mais do que apenas a interface do usuário, mas não vamos esquecer o objetivo principal deste kit de ferramentas.

Eu tentei várias abordagens, incluindo o uso do Flutter sem cabeça - o que provou ser um desastre total. Não porque não funciona, mas porque temos problemas em explicar a outros desenvolvedores como funciona. O fardo de manutenção que ele adiciona simplesmente não vale a pena. Sem mencionar o teste de tal solução. A última vez que tentei, o Flutter Driver não conseguiu lidar com isso.

Se o seu aplicativo realmente precisa do poder de processamento, IMHO, você tem três opções:

  • mova sua lógica para o servidor;
  • mova sua lógica para a plataforma nativa - você pode usar o Kotlin Native para isso - e use os canais Event/Method para fornecer apenas os View Models para sua interface do usuário (acho que foi o que o OLX fez: https://tech.olx.com/fast -prototypes-with-flutter-kotlin-native-d7ce5cfeb5f1);
  • não use o Flutter e procure outro framework (o Xamarin/Blazor pode ser uma boa opção para isso, pois o C# oferece um ambiente multithreading mais sofisticado do que o Dart).

Todos os itens acima têm suas deficiências, mas tentar esticar uma biblioteca/framework para fazer o que não foi realmente projetado também não é o ideal.

O isolado_handler não resolveu esse problema?

@hasonguo Ele usa flutter_isolate em segundo plano, apenas adiciona uma camada extra para facilitar o manuseio de isolados (qualquer isolado, na verdade), adicionando o clichê para comunicação para que você não precise configurá-lo manualmente a cada Tempo. Ele usou uma maneira diferente de resolver o problema anteriormente, mas isso foi impossibilitado por alterações no próprio Flutter e foi alterado para depender de flutter_isolate .

Estamos andando em círculos. Sim, existem soluções, ambos os plugins na verdade. Como Ryan apontou, há uma sobrecarga envolvida em tornar possível a comunicação do canal. Provavelmente, é exatamente essa sobrecarga que fez com que a equipe do Flutter evitasse adicioná-lo automaticamente ao Isolate original, pois você não precisa do suporte em muitos (a maioria?) dos casos.

Ainda assim, não é uma solução tão limpa quanto poderia ser. Se a equipe do Flutter pudesse fornecer comunicação de canal sem a sobrecarga, direta e automaticamente, imediatamente com qualquer Isolate básico, toda a discussão seria discutível. No entanto, o oposto também é verdadeiro: eles não estão realmente buscando a mudança por causa da mudança. Se existe uma solução viável em um pacote ou plugin, por que gastar tempo e adicionar peso ao núcleo?

Crie um novo registro de plug-in etc. para cada isolado, MAS dentro do mesmo FlutterEngine,

Esta pode ser uma boa opção, mas apenas se o mecanismo sempre registrar plugins por si só.

Mas isso só acontece se todos os plugins no GeneratedPluginRegistrant.
https://github.com/flutter/engine/blob/f7d241fd8a889fadf8ab3f9d57918be3d986b4be/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L330 -L344

Criar retornos de chamada adicionais não é um comportamento óbvio.

Outra melhoria para esta segunda ideia é fazer com que os plugins sejam carregados preguiçosamente no novo isolado. Normalmente, se um projeto depende de muitos plugins, o GeneratedPluginRegistrant será bem grande e dependendo da eficiência da rotina de inicialização de um plugin, isso pode contribuir para algumas das despesas gerais. Exatamente como implementar isso é outra história. Os canais de plataforma não são de propriedade de plug-ins, portanto, o envio de uma mensagem por um canal não pode ser usado para acionar o lado da plataforma de um plug-in a ser instanciado. Portanto, qualquer canal de método precisaria ser associado a plugins, ou seria necessário haver um mecanismo adicional que o lado Dart de um plugin pudesse chamar para inicializar-se lentamente antes que o primeiro canal de método fosse criado, e esse mecanismo poderia acionar a instanciação do lado da plataforma desse plugin dentro do isolado.

@TahaTesser

No seu exemplo, o erro não está relacionado a esse problema

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

Olá @nikitadol
Obrigado, exemplo atualizado, não lança uma exceção, está correto? (nunca precisei usar antes)
Você pode fornecer uma amostra de código mínimo reproduzível completa
Obrigada

@TahaTesser


amostra de código

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



Histórico

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



médico palpitante -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
por favor veja este comentário

As mensagens da plataforma são suportadas apenas a partir do isolado principal. [...]

Este comportamento é _esperado_ e tem como rótulo severe: new feature
este problema deve ser considerado como _solicitação de recurso_ ou _proposta_ e não como _bug_

@iapicca

No seu exemplo, o erro não está relacionado a esse problema

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

No seu exemplo, o erro não está relacionado a esse problema

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

você está certo o meu não é um bom exemplo, por favor ignore-o

Esta página foi útil?
0 / 5 - 0 avaliações