Registro de problemas do Flutter Add to App

insira a descrição da imagem aqui
Ele foi conectado ao aplicativo há algum tempo Fluttere a Multiple Fluttersolução oficial de gerenciamento do mecanismo é usada. Atualmente, está funcionando bem online. Aqui está um resumo dos problemas encontrados.

Não há problema em integrar o Flutter ao aplicativo existente como um todo, basta seguir as instruções do documento e combinar a operação de demonstração. Depois de acessar vários idiomas, o modo escuro também pode ser executado normalmente como a parte nativa. Mas ainda encontrou alguns detalhes no desenvolvimento real.

Acima da otimização da dobra

Conforme mencionado na documentação oficial, mesmo com pré-aquecido , ainda demora algum tempo para exibir o conteúdo FlutterEnginepela primeira vez . FlutterPara melhorar ainda mais a experiência do usuário, Flutterhá suporte para exibir a página da tela inicial antes que o primeiro quadro seja renderizado.

O problema que encontrei aqui é que existem quatro guias na página inicial e a terceira guia é a página do Flutter. Portanto, ao mudar para ele, o primeiro carregamento será uma tela branca.

Eu forneço dois métodos de otimização aqui. O primeiro método pode ser usado CachedEngineFragmentBuilderem arquivos shouldDelayFirstAndroidViewDraw(). Ele indica se deve atrasar o processo de desenho do Android até que a IU do Flutter seja exibida.

FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")
    .shouldDelayFirstAndroidViewDraw(true)
    .build();

Há dois pontos a serem observados ao usar esse método:

  • O modo de renderização deve ser usado RenderMode.surface.
  • Este atraso não desaparece, apenas mostra uma estratégia diferente. Se for a primeira vez que você clica na guia para alternar, após clicar, ele aguardará o término da exibição do Flutter antes de alternar. Em máquinas de baixo custo, haverá gagueira perceptível aqui.

O segundo método pode ser usado SplashScreenpara exibir uma página inicial. Deixamos a FlutterFragmentActivity hospedada implementar o método SplashScreenProviderda interface provideSplashScreen.

@Override
public SplashScreen provideSplashScreen() {
    
    
    return new DrawableSplashScreen(this.getResources().getDrawable(R.drawable.xxx), 
    ImageView.ScaleType.CENTER_CROP, 300);
}
  • Os parâmetros acima são para definir a imagem da tela inicial, o método de recorte da imagem e o tempo de animação de transição quando a interface do usuário do Flutter aparecer.
  • O modo de renderização deve ser usado para que a animação de transição tenha efeito RenderMode.texture.

Esses dois métodos têm seus próprios cenários aplicáveis, e o primeiro pode ser usado na maioria dos casos. Como o plano de fundo da nossa página é uma imagem, podemos usar o segundo método. Exiba a imagem primeiro e, em seguida, use a transição de animação para exibir o conteúdo da página. O tempo de espera ao trocar pela primeira vez pode ser evitado usando o primeiro método.

Além disso, RenderMode.surfaceatualmente existe um bug no modo de renderização (Flutter 3.10), que é ao carregar a página onResume. FlutterFragmentaparecerá repentinamente porque SurfaceViewestá no topo da hierarquia de visualização. Portanto, cobre outras páginas do Fragment. Não existe tal problema no uso atual RenderMode.texture.

Problemas de acompanhamento podem ser encontrados aqui .

Activity é basicamente o mesmo que Fragment, e usa RenderMode.surfaceo modo de renderização por padrão. Portanto, a atividade não será aberta até que o primeiro quadro da página seja renderizado. Se o fundo da página flutuante for uma imagem, quando você entrar na página pela primeira vez, ela piscará por um tempo porque a imagem carrega por um determinado período de tempo (a liberação é relativamente melhor). Portanto, você também pode considerar a solução de tela inicial.

As perguntas acima correspondem ao FlutterActivityAndFragmentDelegate local do código-fonte onCreateView:

 View onCreateView(
      LayoutInflater inflater,
      @Nullable ViewGroup container,
      @Nullable Bundle savedInstanceState,
      int flutterViewId,
      boolean shouldDelayFirstAndroidViewDraw) {
    
    
    Log.v(TAG, "Creating FlutterView.");
    ...

    SplashScreen splashScreen = host.provideSplashScreen();

    if (splashScreen != null) {
    
    
      FlutterSplashView flutterSplashView = new FlutterSplashView(host.getContext());
      flutterSplashView.setId(ViewUtils.generateViewId(FLUTTER_SPLASH_VIEW_FALLBACK_ID));
      flutterSplashView.displayFlutterViewWithSplash(flutterView, splashScreen);

      return flutterSplashView;
    }

    if (shouldDelayFirstAndroidViewDraw) {
    
    
      delayFirstAndroidViewDraw(flutterView);
    }
    return flutterView;
  }

    private void delayFirstAndroidViewDraw(FlutterView flutterView) {
    
    
    if (host.getRenderMode() != RenderMode.surface) {
    
    
      throw new IllegalArgumentException(
          "Cannot delay the first Android view draw when the render mode is not set to"
              + " `RenderMode.surface`.");
    }

    if (activePreDrawListener != null) {
    
    
      flutterView.getViewTreeObserver().removeOnPreDrawListener(activePreDrawListener);
    }

    activePreDrawListener =
        new OnPreDrawListener() {
    
    
          @Override
          public boolean onPreDraw() {
    
    
            if (isFlutterUiDisplayed && activePreDrawListener != null) {
    
    
              flutterView.getViewTreeObserver().removeOnPreDrawListener(this);
              activePreDrawListener = null;
            }
            return isFlutterUiDisplayed;
          }
        };
    flutterView.getViewTreeObserver().addOnPreDrawListener(activePreDrawListener);
  }

manipulação de exceção

  1. FlutterFragment, IllegalStateException
IllegalStateException: The requested cached FlutterEngine did not exist in the FlutterEngineCache: 'my_engine_id'
       at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.setupFlutterEngine (FlutterActivityAndFragmentDelegate.java:280)
       at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onAttach (FlutterActivityAndFragmentDelegate.java:189)
       at io.flutter.embedding.android.FlutterFragment.onAttach (FlutterFragment.java:1046)
       at androidx.fragment.app.Fragment.performAttach (Fragment.java:2922)
       at androidx.fragment.app.FragmentStateManager.attach (FragmentStateManager.java:464)
       at androidx.fragment.app.FragmentStateManager.moveToExpectedState (FragmentStateManager.java:275)
       at androidx.fragment.app.FragmentStore.moveToExpectedState (FragmentStore.java:112)
       at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:1647)
       at androidx.fragment.app.FragmentManager.dispatchStateChange (FragmentManager.java:3128)
       at androidx.fragment.app.FragmentManager.dispatchCreate (FragmentManager.java:3061)
       at androidx.fragment.app.FragmentController.dispatchCreate (FragmentController.java)
       at androidx.fragment.app.FragmentActivity.onCreate (FragmentActivity.java:276)

Localização anormal:

  void setupFlutterEngine() {
    
    
    Log.v(TAG, "Setting up FlutterEngine.");

    // First, check if the host wants to use a cached FlutterEngine.
    String cachedEngineId = host.getCachedEngineId();
    if (cachedEngineId != null) {
    
    
      flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
      isFlutterEngineFromHost = true;
      if (flutterEngine == null) {
    
    
        throw new IllegalStateException(
            "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '"
                + cachedEngineId
                + "'");
      }
      return;
    }
    ...
  }

O motivo da análise deve ser que, após a página ser reciclada em segundo plano, quando a página é reaberta, FlutterEnginedescobre-se que ela não existe FlutterEngineCacheem . Por cachedEngineIdter sido obtido por meio de getArguments()aquisição, FlutterEnginefoi onDetachremovido.

  @Override
  public String getCachedEngineId() {
    
    
    return getArguments().getString(ARG_CACHED_ENGINE_ID, null);
  }

Portanto, o método de processamento é julgar se existe FragmentActivity.onCreateantes FlutterEnginee criá-lo quando não existir.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        if (!FlutterEngineCache.getInstance().contains("my_engine_id")) {
    
    
            FlutterEngine flutterEngine = new FlutterEngine(this);
            flutterEngine.getDartExecutor().executeDartEntrypoint(
                    DartEntrypoint.createDefault()
            );
            FlutterEngineCache
                    .getInstance()
                    .put("my_engine_id", flutterEngine);
        }
        super.onCreate(savedInstanceState);
        ...
    }
  1. Dispositivos individuais aparecemUnsatisfiedLinkError
Caused by java.util.concurrent.ExecutionException: java.lang.UnsatisfiedLinkError: No implementation found for
 void io.flutter.embedding.engine.FlutterJNI.nativeUpdateRefreshRate(float) (tried Java_io_flutter_embedding_engine_FlutterJNI_nativeUpdateRefreshRate and Java_io_flutter_embedding_engine_FlutterJNI_nativeUpdateRefreshRate__F)
       at java.util.concurrent.FutureTask.report(FutureTask.java:123)
       at java.util.concurrent.FutureTask.get(FutureTask.java:193)
	   at io.flutter.embedding.engine.loader.FlutterLoader.ensureInitializationComplete(FlutterLoader.java:221)

Caused by java.lang.UnsatisfiedLinkError: No implementation found for void io.flutter.embedding.engine.FlutterJNI.nativeUpdateRefreshRate(float) (tried Java_io_flutter_embedding_engine_FlutterJNI_nativeUpdateRefreshRate and Java_io_flutter_embedding_engine_FlutterJNI_nativeUpdateRefreshRate__F)
       at io.flutter.embedding.engine.FlutterJNI.nativeUpdateRefreshRate(FlutterJNI.java)
       at io.flutter.embedding.engine.FlutterJNI.updateRefreshRate(FlutterJNI.java:7)
       at io.flutter.embedding.engine.loader.FlutterLoader$1.call(FlutterLoader.java:27)
       at io.flutter.embedding.engine.loader.FlutterLoader$1.call(FlutterLoader.java)
       at java.util.concurrent.FutureTask.run(FutureTask.java:266)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
       at java.lang.Thread.run(Thread.java:923)

ou:

Caused by java.util.concurrent.ExecutionException: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xx/base.apk"],nativeLibraryDirectories=[/data/app/xx/lib/x86, /system/lib, /vendor/lib]]] couldn't find "libflutter.so"

Geralmente ocorre durante a criação FlutterEngineou FlutterEngineGroup, e não há método melhor no momento, mas você pode capturar essas exceções e fazer algumas operações de baixo para cima. Evite travamentos diretos que afetam o uso de outras funções pelos usuários. Esse tipo de problema é responsável por uma pequena proporção. Atualmente, apenas dois usuários relataram essa exceção.

O acompanhamento deste problema pode ser encontrado aqui .

recarga quente

Ao depurar o módulo de desenvolvimento misto (Flutter versão 3.10.x), descobriu-se que, quando há várias páginas Flutter (usando FlutterEngineGroupCreate), o recarregamento dinâmico fará com que o aplicativo congele. Encontrei um problema relacionado , tentei a versão beta 3.13.0 e descobri que isso foi corrigido. Aguardando o lançamento do estável.

Pacote

Todos nós sabemos que o desempenho do modo de depuração do Flutter é mediano, portanto, ao entregá-lo ao teste, para evitar alguns problemas de experiência. Podemos empacotar o módulo Flutter em uma versão.

Se você usar o método de integração baseado no Android Archive, poderá usar o pacote diretamente flutter_release. Se você depende diretamente do código-fonte do módulo, pode modificar diretamente flutter/packages/flutter_tools/gradle/flutter.gradleo código-fonte:

   /**
     * Returns a Flutter build mode suitable for the specified Android buildType.
     *
     * The BuildType DSL type is not public, and is therefore omitted from the signature.
     *
     * @return "debug", "profile", or "release" (fall-back).
     */
    private static String buildModeFor(buildType) {
    
    
        if (buildType.name == "profile") {
    
    
            return "profile"
        } else if (buildType.debuggable) {
    
    
            return "debug"
        }
        return "release"
    }

Basta alterar o "debug" acima para "release". iOS flutter/packages/flutter_tools/bin/xcode_backend.darté modificado em .

Obviamente, a modificação direta não é muito elegante, então você pode escrever um script de empacotamento para lidar com essa operação. Por exemplo, usando o Dart para obter o seguinte:

// 读取文件内容
File file = File('xxx\flutter\packages\flutter_tools\gradle\flutter.gradle');
String content = file.readAsStringSync();
// 修改文件内容
String newContent = content.replaceAll('return "debug"', 'return "release"// weilu');
// 将修改后的内容写回文件
file.writeAsStringSync(newContent);

Após a execução, restaure-o.

outro


Se houver novos problemas encontrados posteriormente, eles também serão registrados aqui de forma síncrona.

referência

Acho que você gosta

Origin blog.csdn.net/qq_17766199/article/details/131992775
Recomendado
Clasificación