重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
目前我们是flutter项目,有个需求是需要在app内引导用户去appStore或是安卓的应用商店去评价,该需求我选用了两个插件 in_app_review 和 launch_review , 然而仔做的过程中发现一个问题,当弹出系统的跳转应用商店的弹框时,iOS是单一弹框,Android是弹出一个选择打开商店的弹窗,可选择打开一次或是始终选择某一个商店打开,此时锁屏,然后再解锁,发现iOS没啥问题,安卓系统弹框后的flutter页面黑屏了
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:国际域名空间、虚拟空间、营销软件、网站建设、临颍网站维护、网站推广。
看到这个现象,目测是由于安卓的生命周期和flutter的生命周期没有同步,以下是验证过程
安卓的MainActivity添加生命周期方法
flutter 添加生命周期方法
还是刚才的场景 锁屏 安卓和flutter的后台方法都调用,解锁回到前台 只有安卓的前台方法走 MainActivity会restart,flutter的resume方法,没有调用,验证了开始的猜想,是由于flutter没有检测到前台操作或是这种情况flutter不认为自己在前台,导致flutter没有执行页面的重新绘制导致黑屏
关于flutter的生命周期,查阅资料发现 我们可以手动刷新flutter页面的状态,即使用
我们只需要在MainActivity restart的时候调用上述 方法 告知flutter重绘,该问题就解决了
关于原生加载flutter页面 生命周期相关 看这里 能有一些启发
webview的版本是webview_flutter: ^0.3.22+1
现在遇到的问题是如果webview中输入密码的话,像华为这种会调用自己的安全键盘,这时候就会黑屏,应该是内部计算键盘高度的问题。这时候没办法了,网页web端密码框需要修改一下了自己自定义一下不调用密码类型就好,但是无法被输入框弹上去,后来解决方案是用SingleChildScrollView包裹一下,然后自己监听一下键盘的弹窗和隐藏做一下jump的高度就好了
ps: jump的时候要注意高度,可以在键盘出来的时候底部增加一个只有高度的view,键盘收起隐藏就好了
重新打开即可。
因为软件在后台时间过长,软件会出现一个黑屏动画,就需要重启软件即可恢复。
网易云信播放器 Flutter 封装
事情是这样的 我们公司的业务是有 视频播放这一块业务 而且 是基于网易云信的 视频服务的 做的开发 。公司的App开发框架是使用 Flutter , 那么问题来了 Flutter 怎么 实现视频播放嘞 , 官方给出的解决方案 是 ### video_player 这个库的 实现 是 原生端做视频解码 然后通过 Texture 的方式 映射到 Flutter 中 但是解码器 IOS 使用的是 官方的 AVPlayer (苹果官方提供的播放器 啥都好 就是不支持流媒体播放 ) Android 解码器则是 exoplayer 很好很nice
但是
网易云信的视频 是加密的 只有自己的 播放器sdk 在能解码播放 android 和 ios 都支持流媒体 so 只能自己封装
Android 使用 SurfaceTexture 衔接 视频流 正常 但是 ios emmm 网易云信 播放器 返回 的 编码格式 是 NELP_YUV420 就是 YUV420 直接映射到 Flutter 黑屏 但是有声音
因为Skia 引擎底层只支持了 BGRA 格式的视频数据 所以 和黑屏了
首先我们吧 YUV420 转换成 CVPixelBufferRef 方法如下
该方法依赖 libyuv 请自行导入
然后是 pixelBuffer To SampleBuffer
最后吧 SmapleBuffer 转换 BGRA
方法如何使用
APP 启动页在国内是最常见也是必备的场景,其中启动页在 iOS 上算是强制性的要求,其实配置启动页挺简单,因为在 Flutter 里现在只需要:
一般只要配置无误并且图片尺寸匹配,基本上就不会有什么问题, 那既然这样,还有什么需要适配的呢?
事实上大部分时候 iOS 是不会有什么问题, 因为 LaunchScreen.storyboard 的流程本就是 iOS 官方用来做应用启动的过渡;而对于 Andorid 而言,直到 12 之前 windowBackground 这种其实只能算“民间”野路子 ,所以对于 Andorid 来说,这其中就涉及到一个点:
所以下面主要介绍 Flutter 在 Android 上为了这个启动图做了哪些骚操作~
在已经忘记版本的“远古时期” , FlutterActivity 还在 io.flutter.app.FlutterActivity 路径下的时候,那时启动页的逻辑相对简单,主要是通过 App 的 AndroidManifest 文件里是否配置了 SplashScreenUntilFirstFrame 来进行判断。
在 FlutterActivity 内部 FlutterView 被创建的时候,会通过读取 meta-data 来判断是否需要使用 createLaunchView 逻辑 :
是不是很简单,那就会有人疑问为什么要这样做?我直接配置 Activity 的 android:windowBackground 不就完成了吗?
这就是上面提到的时间差问题, 因为启动页到 Flutter 渲染完第一帧画面中间,会出现概率出现黑屏的情况,所以才需要这个行为来实现过渡 。
经历了“远古时代”之后, FlutterActivity 来到了 io.flutter.embedding.android.FlutterActivity , 在到 2.5 版本发布之前,Flutter 又针对这个启动过程做了不少调整和优化,其中主要就是 SplashScreen 。
自从开始进入 embedding 阶段后, FlutterActivity 主要用于实现了一个叫 Host 的 interface ,其中和我们有关系的就是 provideSplashScreen 。
默认情况下它会从 AndroidManifest 文件里是否配置了 SplashScreenDrawable 来进行判断 。
默认情况下当 AndroidManifest 文件里配置了 SplashScreenDrawable ,那么这个 Drawable 就会在 FlutterActivity 创建 FlutterView 时被构建成 DrawableSplashScreen 。
DrawableSplashScreen 其实就是一个实现了 io.flutter.embedding.android.SplashScreen 接口的类,它的作用就是:
之后 FlutterActivity 内会创建出 FlutterSplashView ,它是个 FrameLayout。
FlutterSplashView 将 FlutterView 和 ImageView 添加到一起, 然后通过 transitionToFlutter 的方法来执行动画,最后动画结束时通过 onTransitionComplete 移除 splashScreenView 。
所以整体逻辑就是:
当然这里也是分状态:
当然这个阶段的 FlutterActivity 也可以通过 override provideSplashScreen 方法来自定义 SplashScreen 。
看到没有,做了这么多其实也就是为了弥补启动页和 Flutter 渲染之间, 另外还有一个优化,叫 NormalTheme 。
通过该配置 NormalTheme ,在 Activity 启动时,就会首先执行 switchLaunchThemeForNormalTheme(); 方法将主题从 LaunchTheme 切换到 NormalTheme 。
大概配置完就是如下样子, 前面分析那么多其实就是为了告诉你,如果出现问题了,你可以从哪个地方去找到对应的点 。
讲了那么多, Flutter 2.5 之后 provideSplashScreen 和 io.flutter.embedding.android.SplashScreenDrawable 就被弃用了,惊不喜惊喜,意不意外,开不开心 ?
通过源码你会发现,当你设置了 splashScreen 的时候,会看到一个 log 警告:
为什么会弃用?
其实这个提议是在 这个 issue 上,然后通过 这个 pr 完成调整。
大概意思就是: 原本的设计搞复杂了,用 OnPreDrawListener 更精准,而且不需要为了后面 Andorid12 的启动支持做其他兼容,只需要给 FlutterActivity 等类增加接口开关即可 。
也就是2.5之后 Flutter 使用 ViewTreeObserver.OnPreDrawListener 来实现延迟直到加载出 Flutter 的第一帧。
为什么说默认情况? 因为这个行为在 FlutterActivity 里,是在 getRenderMode() == RenderMode.surface 才会被调用,而 RenderMode 又和 BackgroundMode 有关心 。
所以在 2.5 版本后, FlutterActivity 内部创建完 FlutterView 后就会执行一个 delayFirstAndroidViewDraw 的操作。
这里主要注意一个参数: isFlutterUiDisplayed 。
当 Flutter 被完成展示的时候, isFlutterUiDisplayed 就会被设置为 true。
所以当 Flutter 没有执行完成之前, FlutterView 的 onPreDraw 就会一直返回 false ,这也是 Flutter 2.5 开始之后适配启动页的新调整。
看了这么多,大概可以看到其实开源项目的推进并不是一帆风顺的,没有什么是一开始就是最优解,而是经过多方尝试和交流,才有了现在的版本,事实上开源项目里,类似这样的经历数不胜数:
一、由于安卓那边升级了某些插件,我这边pull之后,进行了Pub get。运行ios项目,发现app启动之后,先闪现黑屏,然后再出现启动页,之后在进入主页面。解决此问题的方法,是把启动页的图片移除,把启动页的图片修改一个新名字,再导入项目中,再重新运行项目,就正常了。