在FlutterBoost下FadeInImage反复刷新问题

问题背景

路由框架采用flutterboost,根视图UITabbarController的其中一个item的页面是flutter,该flutter页面采用了FadeInImage显示图片,其余都是native。在剩余的native中可以点击进入flutter页面。直接看现象:

2021-09-15 14.41.42.gif

结果:只要切换了别的flutter页面再回内嵌在TabbarItem的flutter页面,页面中的图片就会重新动画。

开始以为是自己的项目哪里没写对,想着放到flutterboost的example上尝试一下,

在tabItem页随便添加一个widget:

FadeInImage.assetNetwork(
  placeholder: "images/flutter.png",
  image: "https://gw.alicdn.com/tfs/TB1aUlEYLb2gK0jSZK9XXaEgFXa-252-252.png",
  width: 300,
  height: 300,
  fadeOutDuration: const Duration(milliseconds: 300),
  fadeInDuration: const Duration(milliseconds: 700),
),

现象如下:

2021-09-16 11.01.43.gif

结果:同样的场景,得到了一个更让人奇怪的现象,切换了tab就会重新动画,不切tab怎么玩都没关系。

排查过程

1.首先先查明为什么会刷新图片,来到FadeinImage内部的build方法

image-20210915154239430.png

通过断点调试最终发现图片是通过imageframBuilder回调创建视图,而该回调中的wasSynchronouslyLoaded决定了是返回网络图片,还是返回占位图动画。所以再跟进去看一下。

官方注解:如果framBuilder为null,一旦第一个图像帧可用,则此小部件将显示绘制为的图像。调用方也可以使用此生成器向图像添加效果(例如在图像变暗时淡入)或在加载图像时显示占位符小部件。

2.看一下Image的build方法,因为image是一个statefulwidget,所以这里是imageState

image-20210915160330391.png

也验证了刚才的说的官方注解。那么就看一下为什么tab切换这里返回的是wasSynchronouslyLoadedfasle,其他情况都是true

3.查看Widget树,发现两种切换方式层级都是一样的。

image-20210915165550149.png

众所周知,当同级的子树发生变化的时候,都用didChangeDependencies(),每次都会进入_updateSourceStream

image-20210915172035541.png
image-20210915172256536.png

关键可以看到这个方法,关键在于如果_imageStream.key不相等,wasSynchronouslyLoaded就设置成false了。如果正常加载过一次,该值默认就是true,这个这里就不展开了。这里的key是stream的completer对象。

image-20210915173815574.png

4.到这就更奇怪了,为什么URL都是一样的,_imageStream为啥会不一样呢?这里主要就要看下这个newStream是怎么拼出来的了。这块代码会有点深,说结论:

image-20210915174053453.png
image-20210915173928584.png

imageCache的_cache属性如果有key,直接返回image.completer。所以通过断点发现,切换tab。

到这里的结论就是这里的_cache在某个时刻清空了,导致返回的key(image.completer)不一致了。

5.看看_cache在何时清空的:

image-20210915180522047.png

看起来是从native调过来的,

channeltype分别是flutter/systemmemoryPressure

image-20210916103914716.png
image-20210916103843272.png

6.调试一下engine看看怎么传过来的。

全局搜一下engine源码,看起来是这个。

image-20210916104304137.png

lldb打上方法断点看看什么时候调:

image-20210916105009888.png

快知道最终原因了:

image-20210916105109496.png
image-20210916105141640.png

因为flutter_boost把engine.viewController设置为了nil,触发了memoryPressure

方法的源头是detachFlutterEngineIfNeeded

detachFlutterEngineIfNeeded就是在vc dismiss的时候调用。

image-20210916104755214.png

7.到这更奇怪了,两种方式都是dismiss,为什么切换tab,会重新动画呢?

image-20210916105535275.png

结论

最终答案在这,如果是当前页面push和pop会进入attatchFlutterEngine的逻辑,而在简单场景下例如A->B->A detachFlutterEngine方法内部因为判断self.viewController.engine != self,所以就不会执行之后的逻辑。而嵌tabbar页中flutter vc在flutterboost看起来,也做了相同的逻辑,一旦切出,我也默认这个页面销毁了,不再进行管理。切换tab,FadeInImage重新做动画的根本原因就是,FlutterBoost在detachFlutterEngine把engine.viewcontroller置为了nil,触发了memoryPressure(内存压力),把ImageCache清空了,导致虽然网络图片的缓存还在,但是让flutter误以为需要重新加载。

稍微改一下代码

image-20210916110126681.png
2021-09-16 11.01.43.gif

完美。

暂时不知道官方这么写的原因,所以最后的解决方法,还是把动画时间调成最短,让人肉眼看不到图片重新做动画。

补充核心过程(假设A是tab-native,B是tab-flutter,B的图片已经加载完毕):

第一种情况(B->A->C->A->B)

第一步:回到A,push一个flutter的C页面,触发memoryPressure(init),并且因为层级变化image触发didChangeDependencies

image-20210916144339234.png

可以看到_cache清空了,但_liveImages中的网络图片对象还在,这个时候会把_liveImages的对象取出,重新放到_cache中。

image-20210916144742323.png

并且返回对象。这个时候_cache又恢复正常。

image-20210916144828593.png

之后因为image不需要在响应动画,所以移除通知

image-20210916153426636.png

再移除的过程中将liveImage清掉

image-20210916153507713.png

所以进入C页面的最后结果是

image-20210916153552069.png

第二步.C dismiss,触发memoryPressure(engineDetach),清了一把_cache

image-20210916153714071.png

这个时候什么缓存都没了,Image的key需要重新创建初始化,所以回到B会重新动画。

第二种情况:(B->C->B)

第一步同上。

第二步.C dismiss,不会engineDetach,所以不会触发memoryPressure,所以回到B,直接拿缓存,不用重新创建。

后续

给官方提了一个PR,新增vc keepalive属性,针对这种tab内嵌的页面进行特殊管理。
https://github.com/alibaba/flutter_boost/pull/1422?w=1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容