FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • AndroidUI

  • Android第三方SDK

  • Android混淆

  • Android仓库

  • Android新闻

  • Android系统开发

  • Android源码

  • Android注解AOP

  • Android脚本

  • AndroidTv开发

  • AndroidNDK

  • Android音视频

  • Android热修复

  • Android性能优化

  • Android云游戏

  • Android插件化

  • iOSUI

  • iOS工具

  • iOS底层原理与应用

  • iOS组件化

  • iOS音视频

  • iOS疑难杂症

  • iOS之Swift

  • iOS之RxSwift

  • iOS开源项目

  • iOS逆向

  • Flutter开发

    • Dart - 抽象类的实例化
    • Flutter - 打印好用的Debug日志
    • Flutter - 混合开发
    • Flutter - 解决混合开发iOS脚本打包遇到的问题
    • Flutter - 低版本在iOS14上遇到的问题与解决方案
    • Flutter - 解决原生弹窗的触摸事件被Flutter响应的问题
    • Flutter - 实现列表上下拉切换header
    • Flutter - 获取ListView当前正在显示的Widget信息
    • Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥
    • Flutter - 快速实现聊天会话列表的效果,完美💯
    • Flutter - 聊天输入框更新文本时的必备优化点🔖
    • Flutter - 我给官方提PR,解决run命令卡住问题 😃
    • Flutter - 探索run命令到底做了什么 🤔
    • Flutter - 引擎调试(iOS篇)🛠
    • Flutter - 引擎调试bug到提交PR实战 🐞
    • Flutter - 船新升级😱支持观察第三方构建的滚动视图💪
    • Flutter - 瀑布流交替播放视频 🎞
    • Flutter - IM保持消息位置大升级(支持ChatGPT生成式消息) 🤖
    • Flutter - 滚动视图中的表单防遮挡 🗒
    • Flutter - 秒杀1/2曝光统计 📊
    • 一天内加入 Flutter 和 FlutterCandies 两大组织是什么体验 🧐
    • Flutter - 如何快速搓一个微信通讯录列表(azlist) 📓
    • Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)
    • Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)
    • Flutter - 解决返回原生页面时dispose方法未被触发的问题 🐞
      • 一、概述
      • 二、解决方案
      • 三、代码
    • Flutter - 升级3.19之后页面多次rebuild?🤨
    • Flutter - 热更新 Shorebird 1.0 正式版来了 🐦
    • Flutter - 使用Pigeon实现视频缓存插件 🐌
    • Flutter - 轻松搞定屏幕旋转功能 😎
    • Flutter - 解决Connection closed before full header was received
    • Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻
    • Flutter - 支持观察NestedScrollView,兼容性更强 😈
    • Flutter - 聊天键盘与面板丝滑切换的强势升级 🍻
    • Flutter - 升级到3.24后页面还会多次rebuild吗?🧐
    • Flutter - 轻松实现PageView卡片偏移效果
    • Flutter - 轻松搞定炫酷视差(Parallax)效果
    • Flutter - 危!3.24版本苹果审核被拒!
    • Flutter - 子部件任意位置观察滚动数据
    • Flutter - iOS编译加速
    • Flutter - Xcode16 还原编译速度
  • 移动端
  • Flutter开发
LinXunFeng
2024-03-10
目录

Flutter - 解决返回原生页面时dispose方法未被触发的问题 🐞

欢迎关注微信公众号:[FSA全栈行动 👋]

# 一、概述

在去年对公司的 Flutter 混编项目做了性能优化,其中最坑的,莫过于从 Flutter 页面返回原生页面时 dispose 方法竟然不走,如图所示

这就导致一些资源无法得到释放,如 fijkplayer 所使用的视频资源。在原生页面与满屏视频的 Flutter 页面之间反复进出时,内存占用越来越高

上图是进出 7 次视频页面后的内存占用情况。

最终会导致手机发烫,App 变得卡顿,甚至最后闪退(当 App 的内存占用达到图中红色虚线时就会直接被系统杀掉)

随即向 Flutter 官方提出了 issue: https://github.com/flutter/flutter/issues/137924 (opens new window)

# 二、解决方案

在提出 issue 的第二天,组织成员 dnfield 出提了一个 PR: https://github.com/flutter/flutter/pull/137957 (opens new window) ,不过该 PR 只是加了说明,解释为何会出现该问题,以及如何去处理。

根据其说明进行了优化,内存确实得到了有效的及时收回,如图所示

该PR的说明指出:

当通过原生的方式关闭了 Flutter 页面时,Flutter 端并不知道开发者是否打算提前释放资源,所以 Widget 树依旧保持着挂载的状态,这么做是为了再一次展示 Flutter 页面时可以尽可能快的进行渲染。【想必这也是上图中内存占用并没有得到彻底回落的原因!】

如果想要尽快的释放资源,可以建立 platform channel,当返回原生页时告知 Flutter 端去再次调用 runApp 方法,并传入 SizedBox.shrink,这样就可以释放掉活跃的 Widget 树。

通过 platform channel 的方式需要与原生端打交道,有没有纯 Flutter 的方式呢?

有的,那就是通过监听 Flutter App 的状态,当状态为 AppLifecycleState.detached 时,就可以去执行 runApp 方法了,达到一样释放资源的效果。

关于 AppLifecycleState.detached 的说明:

此时 Flutter App 仍被 Flutter 引擎所持有,但已与任何的视图分离。也就是说引擎正在以没有视图的状态运行着。

该状态既可能是在 Flutter 引擎初始化时附加视图的过程中,也可能是在视图因 Navigator 弹出而被销毁之后。

当 App 从 Flutter 页面返回原生页面后,就会切换到该状态。

# 三、代码

下面给出完整的示例代码,供大家参考

main(List<String> args) async {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  final bool isDetached;

  const MyApp({
    Key? key,
    this.isDetached = false,
  }) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);

    switch (state) {
      case AppLifecycleState.detached:
        // 相关处理参考
        // https://github.com/flutter/flutter/pull/137957
        runApp(const MyApp(isDetached: true));
        // 清除内存中的图片缓存
        // https://github.com/Baseflow/flutter_cached_network_image/issues/429
        final imageCache = PaintingBinding.instance.imageCache;
        imageCache.clear();
        imageCache.clearLiveImages();
        break;
      default:
    }
  }

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    if (widget.isDetached) {
      // 引擎被detach, 移除所有的widget, 以此来及时释放资源
      Widget resultWidget = const SizedBox.shrink();
      // Fix report no OKToast widget found.
      // resultWidget = OKToast(child: resultWidget);
      return resultWidget;
    }
    return MaterialApp(
      ...
    );
  }
}

细心的朋友肯定注意到了 OKToast 的相关代码,当时在 Sentry 上看到了错误: No OKToast widget found,这是因为有些网络请求在返回原生页面后才请求成功,进而触发吐丝,而此时已经执行了 runApp 去展示空白页面,考虑到即使取消了所有请求,有些业务会去对 cancel 的情况去吐丝提醒,所以还是套一个 OKToast 来得方便~

#Dart#Flutter
Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)
Flutter - 升级3.19之后页面多次rebuild?🤨

← Flutter - 混编项目集成Shorebird热更新🐦(iOS篇) Flutter - 升级3.19之后页面多次rebuild?🤨→

最近更新
01
Flutter - Xcode16 还原编译速度
04-05
02
AI - 免费的 Cursor 平替方案
03-30
03
Android - 2025年安卓真的闭源了吗
03-28
更多文章>
Theme by Vdoing | Copyright © 2020-2025 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×