Flutter Add to App 问题记录

这篇具有很好参考价值的文章主要介绍了Flutter Add to App 问题记录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Flutter Add to App 问题记录,Flutter,flutter,android
前一阵应用中接入了Flutter,使用的是官方的Multiple FlutterEngine管理方案,目前线上运行良好,这里整理一下遇到的问题。

将 Flutter 集成到现有应用整体来说没有什么问题,按照文档的说明结合demo操作就行。接入后多语言,深色模式也可以和原生部分一样正常运行。但还是遇到了一些实际开发中的细节问题。

首屏展示优化

在官方文档中有提到,即使使用了预热的FlutterEngine,第一次展示 Flutter 的内容仍然需要一些时间。为了更进一步提升用户体验,Flutter 支持在第一帧渲染完成之前展示闪屏页。

我这里遇到的问题是这样,首页有四个Tab,其中第三个Tab是Flutter页面。所以当切换到它时,第一次加载会先白屏一下。

我这里提供两种优化方法。第一种可以使用 CachedEngineFragmentBuilder 中的 shouldDelayFirstAndroidViewDraw() 方法。它表示是否延迟 Android 绘制过程,直到 Flutter UI 显示完毕。

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

使用这个方法有两点需要注意:

  • 渲染模式必须使用RenderMode.surface
  • 这个时间延迟并不是消失了,只是显示的策略不同。如果是首次点击tab切换,那么点击后会等待Flutter显示完毕后再切换。在低端机上这里会很明显感到停顿。

第二种方法可以使用SplashScreen来显示一个闪屏页。我们让承载FlutterFragment的Activity实现SplashScreenProvider接口的provideSplashScreen方法。

@Override
public SplashScreen provideSplashScreen() {
    return new DrawableSplashScreen(this.getResources().getDrawable(R.drawable.xxx), 
    ImageView.ScaleType.CENTER_CROP, 300);
}
  • 上面的参数是设置闪屏图片,图片的裁剪方式,以及Flutter UI出现时过渡动画的时间。
  • 过渡动画生效需要渲染模式必须使用RenderMode.texture

这两个方法各有适用场景,大多数情况下可以使用第一种。由于我们这个页面背景是一张图片,所以可以使用第二种方法。先展示图片,再用动画过渡显示页面内容。可以避免使用第一种方法首次切换时的等待时间。

另外,使用RenderMode.surface渲染模式目前(Flutter 3.10)有个bug,就是在承载页面onResume时。FlutterFragment会突然显示,因为 SurfaceView 在视图层级最顶层。所以覆盖了其他的Fragment页面。目前使用RenderMode.texture无此问题。
问题跟进具体可以看这里。

Activity与Fragment基本一致,默认使用RenderMode.surface渲染模式。所以等到页面第一帧渲染完成后才会打开Activity。如果flutter页面背景是张图片,那么首次进入页面时,因为图片的加载有一定时间,所以会闪一下(release相对好一些)。所以也可以考虑闪屏的方案。

以上问题对应源码位置FlutterActivityAndFragmentDelegate 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);
  }

异常处理

  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)

异常位置:

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

分析原因应该是页面在后台被回收后,重新打开页面时,在获取FlutterEngine时发现不存在于FlutterEngineCache中。因为cachedEngineId是通过getArguments()获取的,而FlutterEngineonDetach已经移除。

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

所以处理方法就是在FragmentActivity.onCreate前就判断FlutterEngine是否存在,不存在时创建。

    @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. 个别设备出现UnsatisfiedLinkError
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)

或:

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"

一般是在创建FlutterEngineFlutterEngineGroup时出现的,目前也没有较好的方法,但是可以捕获一下此类异常,做一些兜底操作。避免直接崩溃影响用户使用其他功能,这类问题占比很少,目前仅有两个用户上报了此异常。

此问题跟进可以看这里。

热重载

在调试混合开发模块时(Flutter版本3.10.x),发现当存在多个Flutter页面时(使用FlutterEngineGroup创建),热重载会使App卡死。我找到了相关问题,我尝试使用beta 3.13.0版本发现此问题已解决。等待stable的发布。

打包

众所周知Flutter的debug模式性能表现一般,所以在交给测试时,为了避免一些体验问题。我们可以将Flutter模块打包成release。

如果使用依赖 Android Archive方式集成,可以直接使用flutter_release包。如果直接依赖模块的源码,可以在直接修改flutter/packages/flutter_tools/gradle/flutter.gradle的源码:

   /**
     * 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"
    }

将上面的"debug"改为"release"就好。iOS在flutter/packages/flutter_tools/bin/xcode_backend.dart中修改。

当然直接修改显得不是很优雅,所以可以写个打包脚本处理这一操作。例如用Dart实现如下:

// 读取文件内容
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);

执行完后,还原即可。

其他

  • BUG in [v3.10.0] FlutterViewController memory leak when add an existing app, Multiple Flutter instances
  • [Android, SystemChrome, FlutterActivity] Status bar becomes semi translucent when using a pre-warmed Flutter engine

后面如果有遇到新的问题,也会同步记录到这里。文章来源地址https://www.toymoban.com/news/detail-647130.html

参考

  • multiple_flutters
  • 文档 - 展示闪屏页

到了这里,关于Flutter Add to App 问题记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Android安全】Flutter app逆向

    参考:https://www.bilibili.com/video/BV1SM41147g5 flutter app特征: lib/armeabi-v7a/下面有libapp.so和libflutter.so 逆向分析工具: 看雪:Flutter逆向助手 可以解析libapp.so文件,将其转换成dart文件 将libapp.so导入到手机,然后打开手机上的 flutter逆向助手 选中libapp.so,解析,得到dart文件 (注意要

    2024年02月13日
    浏览(40)
  • Flutter开发问题记录

    1. Q:Mac电脑通过AndroidStudio运行软件到iphone报错 A:项目中ios目录下,Podfile文件第2行 platform :ios, ‘11.0’,取消注释 2. Q:Mac电脑通过AndroidStudio运行软件到iphone报错 A:第一步终端运行 gem list(类似pip list)查看是否安装了cocoapods,如果没安装那么安装即可。如果发现已经安装,那么

    2024年02月14日
    浏览(38)
  • 【flutter电子木鱼】flutter 打包 android apk,记录配置签名的过程/调试的过程及flutter build apk放到手机上用。

    目标通过这篇blog记录一下flutter打包android apk的过程,项目是参考以下链接的git仓库,然后自己重新创建了一个project。安卓应用市场的木鱼充斥着广告和付费体验极差,自己做一个还可以根据喜好做适应性调整,不用担心被偷后台也不用烦广告和一些奇怪的布局,干净清爽,

    2024年02月10日
    浏览(52)
  • flutter遇到的小问题记录

    Get.bottomSheet( isScrollControlled: true,) isScrollControlled: true 就是控制高度 (无语) 如果是url 可以直接File(url) 如果是Uint8List 使用下面的方法 可以使用本地图片

    2024年02月13日
    浏览(35)
  • Flutter第7天--字体图标+综合小案例+Android代码交互,app架构图

    ); //中间的信息 var center4 = Row( children: [ Expanded(child: pda(center_right, 5)), Image.network( note.imgUrl, width: 80, height: 80, fit: BoxFit.fitHeight ) ], ); var end4 = Row( children: [ Icon( Icons.grade, color: Colors.green, size: 20, ), Text( “1000W”, style: infoStyle,), pd(Icon(Icons.tag_faces, color: Colors.lightBlueAccent, size: 20), l

    2024年04月22日
    浏览(33)
  • [Flutter]运行到Android报错You are applying Flutter‘s app_plugin_loader Gradle plugin imperatively using..

    flutter run到Android模拟器报错如下: Launching lib/main.dart on Android SDK built for x86 in debug mode... You are applying Flutter\\\'s app_plugin_loader Gradle plugin imperatively using the apply script method, which is deprecated and will be removed in a future release. Migrate to applying Gradle plugins with the declarative plugins block: https://f

    2024年04月26日
    浏览(35)
  • 【Android常见问题(五)】- Flutter项目性能优化

    项目迭代开发一定程度后,性能优化是重中之重,其中包括了包体积,UI 渲染、交互等多个方面。 通过 Flutter 应用的混淆为入口,我们主要探讨了UI 渲染的优化。 其中就会涉及到一个非常关健的概念 ——「FPS,Frame Per Second」即「每秒展示帧数」,它代表了应用的流畅度。

    2024年02月15日
    浏览(53)
  • Flutter层对于Android 13存储权限的适配问题

    感觉很久没有写博客了,不对,的确是很久没有写博客了。原因我不怎么想说,玩物丧志了。后面渐渐要恢复之前的写作节奏。今天来聊聊我最近遇到的一个问题: Android 13版本对于storage权限的控制问题。 我们都知道,Android的每个版本更新都会伴随着搞事,也就是所谓的谷

    2024年02月07日
    浏览(42)
  • 出现Unable to access Android SDK add-on list的问题怎么解决?

    问题:第一次打开新下载好的Android Studio时出现以下错误提示框,显示Unable to access Android SDK add-on list 解决:打开文件安装位置,在目录中找到目录名为bin的文件夹,如图所示: 双击打开bin目录,找到idea.properties。有记事本打开,在最后一行添加 disable.android.first.run=true 保存即

    2024年02月17日
    浏览(47)
  • Flutter 解决App登录页面软键盘遮挡住登录按钮或顶起底部控件的问题

    问题点 最终效果图 问题点: 当前使用的是 Column 布局,弹窗软键盘后页面超出范围。 A RenderFlex overflowed by 0.533 pixels on the bottom. 解决方式 在 Scaffold 或者 CupertinoPageScaffold 中设置 resizeToAvoidBottomInset 为false 不修改 resizeToAvoidBottomInset 属性的话,可以使用 ListView 、 SingleChildScrol

    2024年02月04日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包