[Bug]: InAppWebView 加载异常(platformView集成显示异常)(必现)
请描述遇到的问题,以及您所期望的正确的结果
flutter版本从3.16.9升级到3.24.5(升级到3.19.0,也是一样), 同样的代码,FlutterFragment 显示的 platformView 加载异常。 希望显示正常。
请说明如何操作会遇到上述问题
出现方式: 1、点击 open flutter fragment page 2、切换 tab 必现。
问题视频: https://github.com/user-attachments/assets/3db5bae0-58dd-420a-9328-1ebfafce9370
在下面填入关键复现代码
flutter版本从3.16.9升级到3.24.5(升级到3.19.0,也是一样),
InAppWebView 加载的view,50%会加载异常,白屏或者是加载完成以后动不了
代码: example.zip
复现的平台
Both
Flutter SDK版本
3.24.5
FlutterBoost版本
5.0.1,5.0.2
是否延迟初始化FlutterBoost
No
解决方案
暂时没有思路,感觉跟生命周期也没关系,因为50%的概率显示,感觉那个地方有缓存,第一次加载失败,第二次就重新加载,反复如此。 我对比了加载成功和失败的日志,基本都是一模一样的。调试也没发现太大出入。 之前使用的flutter 3.16.9,显示没有问题。 @0xZOne
我们项目中遇到了同样的问题 flutter 3.16.9版本没有问题 3.24.5版本 platformView @加载异常,从日志分析没有异常。希望可以提供解决思路 @0xZOne
可以先设置InAppWebViewSettings里的useHybridComposition参数为false,来解决。
我发现目前FlutterBoost里有关PlatformView的一些异常问题,都是因为useHybridComposition这个玩意儿导致,更里面就是使用了PlatformViewsService.initExpensiveAndroidView导致的。直接使用AndroidView或者PlatformViewsService的其他init方法就没事。
触发的原因大概查到了,和#1960 的问题一样,都是FlutterTextureView中isPaused变量的问题,这个变量会作为FlutterRender.startRenderingToSurface方法的第二个参数传入,然后这个startRenderingToSurface在3.16和3.19上的区别:
3.16:
3.19:
可以看到区别在于3.16中最后只会调用flutterJNI.onSurfaceCreated,而3.19会区分调用flutterJNI.onSurfaceCreated和flutterJNI.onSurfaceWindowChanged,然后就和#1960 里一个表现了。
那么再看isPaused为什么是true,是因为触发了FlutterView.convertToImageView方法,里面的renderSurface.pause()使其变成了true。
根据Flutter 深入探索混合开发的技术演进这篇文章里面所写,另一种解法是在Flutter层调用
PlatformViewsService.synchronizeToNativeViewHierarchy(false);
不过我看这个方法在高版本Flutter SDK里已经没了,不过Java层的方法定义还在,因此可以这么写:
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);
其实就是不让用FlutterImageView
确实,设置了 SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false); 查看的话,少了 FlutterImageView。 对比3.16.9,和3.19.0,设置 Hybrid Composition,view集成方式也出现了变化。
但是,文档也说明了:那为什么会有把 FlutterSurfaceView 变成了 FlutterImageView 这样的操作?原因其实是为了更好的动画同步和渲染效果。
现在去掉了,demo目前看不出有什么问题,项目中还没验证。这里采用 synchronizeToNativeViewHierarchy,去掉,会有什么影响吗?暂时看不出来。
- synchronizeToNativeViewHierarchy 方法 作用: 这个方法用于确保 Flutter 的视图层次结构与原生视图层次结构同步。这在使用 Hybrid Composition 时尤其重要,因为你可能希望 Flutter 渲染的内容与原生 UI 组件无缝集成。 去掉的影响: 如果你去掉了 synchronizeToNativeViewHierarchy 的调用,可能会导致 Flutter 渲染的内容与原生视图的层次结构不同步。这可能会影响到视图的交互、动画效果和性能。 在某些情况下,可能不会立即发现显著问题,但在复杂的 UI 或动画场景中,潜在的问题可能会显现出来
我的想法是如果PlatformView和Flutter上的UI没有复杂的交叉层级堆叠显示的问题,那么就没必要使用Hybrid模式。
如果想继续使用Hybrid,只要想办法让代码去触发onSurfaceCreated就行。
嗯,暂时看是的。最好的办法,还是flutter,去修复这个问题,或者看看在什么合适的时机,改变 isPaused 状态。就跟之前手动处理的 platformview 的显示和隐藏一样。
这个应该很难让Flutter去修改了,因为纯Flutter不存在跨Activity切换Surface的情况。真要FlutterBoost解决这个问题,之前为了修复 #1960 的PR里的动态代理的方案改改应该是能用的。
这个应该很难让Flutter去修改了,因为纯Flutter不存在跨Activity切换Surface的情况。真要
FlutterBoost解决这个问题,之前为了修复 #1960 的PR里的动态代理的方案改改应该是能用的。
最近苹果升级到 18.2 版本,必须得升级到 3.27.1,然后这个修改,又无效化了。
设置了
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);
反而显示白屏了。
不设置,又还是之前的问题。可能底层又改了 surface 的状态。
查了一下,是 FlutterMutatorView 变成了 gone,不知道哪里出了问题,还得查查。
这里增加了 initializePlatformViewIfNeeded(viewId),导致 return 了。
public void onDisplayPlatformView( int viewId, int x, int y, int width, int height, int viewWidth, int viewHeight, @NonNull FlutterMutatorsStack mutatorsStack) { initializeRootImageViewIfNeeded(); if (!initializePlatformViewIfNeeded(viewId)) { return; }
导致,后面的 contains 为false,然后platformView就没有显示。
if (currentFrameUsedPlatformViewIds.contains(viewId) && (isFrameRenderedUsingImageReaders || !synchronizeToNativeViewHierarchy)) { parentView.setVisibility(View.VISIBLE); } else { parentView.setVisibility(View.GONE); }
我去flutter里面也增加了提问,看看那边有什么解释。 https://github.com/flutter/flutter/issues/160490
我调试运行后,方法 onBeginFrame,在运行几次以后,后面不会运行 onDisplayOverlaySurface, 3.24.5: onBeginFrame -> onDisplayPlatformView -> onEndFrame
3.27.1: 开始运行: onBeginFrame -> onDisplayPlatformView -> onEndFrame
运行几次以后: onBeginFrame -> onEndFrame
暂时不清楚原因。
我看跟方法改动:
initializeRootImageViewIfNeeded();
if (!initializePlatformViewIfNeeded(viewId)) {
return;
}
关系不大。
我这边在flutter boost的example中的webview_flutter_demo.dart里的weview的实现改成InAppWebView,并没有出现你说的问题。
需要增加代码:
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);
最后我的回复,我下午尝试复现了:https://github.com/flutter/flutter/issues/160490
@joechan-cq
有复现没?你改成 InAppWebView,需要运行一下
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);
是必现的,我继承 PlatformViewsController,在方法 onEndFrame,强制可见,也不行。
@joechan-cq
还是使用 FlutterImageView 的话,可以处理吗?
就是这段代码不加,
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);
我发现是flutter 那边的engine c++代码修改,造成的这个问题,暂时不清楚该如何解决。他们目前也没有回复。
https://github.com/flutter/flutter/issues/160490
@joechan-cq 有复现没?你改成 InAppWebView,需要运行一下
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);是必现的,我继承 PlatformViewsController,在方法 onEndFrame,强制可见,也不行。
我用inAppWebView的项目,加synchronizeToNativeViewHierarchy代码也没有复现。
@joechan-cq 还是使用 FlutterImageView 的话,可以处理吗? 就是这段代码不加,
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);我发现是flutter 那边的engine c++代码修改,造成的这个问题,暂时不清楚该如何解决。他们目前也没有回复。 flutter/flutter#160490
如你之前所说,修改isPaused的值应该就行了
@joechan-cq 有复现没?你改成 InAppWebView,需要运行一下
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);是必现的,我继承 PlatformViewsController,在方法 onEndFrame,强制可见,也不行。我用inAppWebView的项目,加
synchronizeToNativeViewHierarchy代码也没有复现。
需要用3.27.1的flutter版本。
修改isPaused,我晚点试试。
@joechan-cq 有复现没?你改成 InAppWebView,需要运行一下
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);是必现的,我继承 PlatformViewsController,在方法 onEndFrame,强制可见,也不行。我用inAppWebView的项目,加
synchronizeToNativeViewHierarchy代码也没有复现。需要用3.27.1的flutter版本。
修改isPaused,我晚点试试。
是的, 我用的就是3.27.1的Flutter。这么看,可能和设备也有关系
看engine那边的提交记录,确实和设备GPU型号有关,另外不用Impeller估计也没这个问题。
我这边验证了小米+oppo,一共4台设置,都是必白屏。也可能这几台设备都差不多吧。
@joechan-cq 还是使用 FlutterImageView 的话,可以处理吗? 就是这段代码不加,
SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false);我发现是flutter 那边的engine c++代码修改,造成的这个问题,暂时不清楚该如何解决。他们目前也没有回复。 flutter/flutter#160490如你之前所说,修改isPaused的值应该就行了
我验证了,单独反射调用,时机上不好弄,在 FlutterEngineAttachmentListener 里面调用,反射不行,找到 flutterView的方法,revertImageView 里面有调用 resume,使用以后,是可行的。
// 解决HybridComposition PlatformView中间页面问题
flutterView.addFlutterEngineAttachmentListener(new FlutterView.FlutterEngineAttachmentListener() {
@Override
public void onFlutterEngineAttachedToFlutterView(@NonNull FlutterEngine engine) {
FlutterBoostFixUtil.log(FlutterBoostFixUtil.TAG, "onFlutterEngineAttachedToFlutterView: ");
// 显示之前的记录的platform view
showPlatformViews(true);
if (flutterView != null) {
flutterView.setVisibility(View.VISIBLE);
}
}
@Override
public void onFlutterEngineDetachedFromFlutterView() {
FlutterBoostFixUtil.log(FlutterBoostFixUtil.TAG, "onFlutterEngineDetachedFromFlutterView: ");
// 解决platformView 切换的时候,显示异常的问题
// 这里根据url 记录platform view,隐藏view
FlutterBoostFixUtil.findFlutterPlatformViews(FlutterBoostActivity.this, platformViews,getFlutterEngine(), getUrl());
if (flutterView != null) {
// 解决flutter 从3.16.9,升级到3.24.x,flutter platformView 显示异常的问题
// platformView 的状态存在问题,flutter 升级以后,renderSurface 调用了 pause 改变了状态,需要调用 resume 改变回去
// 1、单独调用 resume 没有效果;2、使用 SystemChannels.platform_views.invokeMethod('synchronizeToNativeViewHierarchy', false); 存在 3.27.0,升级到3.27.1,flutter 白屏的问题
// # https://github.com/alibaba/flutter_boost/issues/2161
flutterView.revertImageView(new Runnable() {
@Override
public void run() {
}
});
flutterView.setVisibility(View.INVISIBLE);
}
showPlatformViews(false);
}
});