Flutter实现动态化更新-技术预研

699 阅读5分钟

前言:有做过完整项目的小伙伴应该都知道,随着业务的发展,app的运营需求会越来越多(比如:根据运营活动动态更换页面的UI)。这就要求我们的app要尽可能的满足市场的运营的动态化需求,通过这篇文章你将了解到:

  1. Flutter动态化的方案使用和效果对比;

  2. 针对中小型团队,该如何最小成本、最高效的实现app的动态化需求。

动态化的常用方式和实现原理

首先什么是动态化?即不依赖程序安装包,就能进行动态实时更新页面的技术。

接下来列举常用的方式和原理:

  • 一般大家都会想到webview,这确实是最常用的方式,但也是动态化中最不稳定的方式;webview的体验比较差,同时需要做大量设备的兼容。
  • 基于 GPL 的 Native 增强。 GPL即通用编程语言,比如我们常见的Dart、JavaScript等,通过这些通用语言来为Native功能增强动态化能力。通俗的举个例子解释:运营者动态更改线上的js文件,Flutter应用通过网络拉取更新后动态渲染,这就是基于GPL的Native增强。
  • 基于 DSL 的 Native 增强。 DSL即专用领域语言,为了解决某些特定场景下的任务而专门设计的语言,比如xml、json、css、html。通过生成简单的DSL语言文件,通过解析协议对页面进行动态配置。

我们整体来看Flutter的动态化生态,目前市面上并没有一个成熟的开源框架,只有国内各大互联网公司陆续开源,但也都处在急需维护的状态。当前主流框架有:

  1. 腾讯开源的 MXFlutter
  2. 58同城开源的 Flutter Fair
  3. 阿里巴巴开源的 北海Kraken

同时我还会介绍另外两种比较通用的方式:

  1. webview增强 【植入腾讯 X5内核,模型升级改造】
  2. UI库模板化

各大厂动态化架构对比

image.png Webview增强优化

几乎所有的移动应用中,都会用到webview来作为h5的容器。通过运营平台配置生成h5,app直接显示即可。但很遗憾,webview的体验性、稳定性/兼容性有很多的问题。

体验上加载过程白屏,加载中、出错状态没法定义等;兼容性上iOS还好,浏览器内核都是WKWebView,但是Android的设备多种多样,浏览器内核也参差不齐,所以在兼容性上经常存在问题。 为了解决以上问题,我们基于官方插件webview_flutter,做了以下方案:

  • 体验上修改webview插件为可配置透明背景,去除加载条;Flutter层开发webview的增强容器,实现可定义加载中、加载失败的视图,达到基本符合app的加载效果
  • 稳定性上,我们采取统一植入X5内核的方法

为何采取X5内核?

目前开源的浏览器内核sdk不多,主要有以下几个:ChromeView、Crosswalk、TbsX5。

  1. 基于Chromium内核的开源ChromeView 目前基本没有维护,另一个问题是编译出来的动态库太大,ARM-29M,x86-38M,这无疑对app体积来说是个大难题。因此放弃采用基于Chromium的ChromeView。
  2. Crosswalk同样是基于Chromium内核,同样存在上述app体积问题,因此也放弃。
  3. TbsX5 基于谷歌Blink内核,生态在国内是很成熟的,只要装有微信的手机,都支持X5。X5 提供两种集成方案:
    • 只共享微信手Q空间的x5内核(for share)
    • 独立下载x5内核(with download)

优化体验

最终业务层代码:

WebView( initialUrl: 'https://www.baidu.com', transparentBackground: true )

  • 构建webview容器。webview背景处理为透明后,通过Stack布局,以及监听onProgress回调,赋予webview容器加载中、加载失败的效果,让用户的体验达到与原生应用类似。

/// 我们用的是flutter_bloc进行状态管理 Stack( alignment: Alignment.center, children: [ WebView( transparentBackground: widget.transparentBackground, onProgress: (int progress) { if (progress >= 100) { context.read<WebViewContainerCubit>().loadSuccess(progress); } }, onWebResourceError: (error) { context.read<WebViewContainerCubit>().loadError(); }, ), if (state is WebViewLoading) Center( child: widget.loadingView ?? const LoadingView(), ), ], )

再看看bloc层,非常简单的状态切换。

/// Cubit class WebViewContainerCubit extends Cubit<WebViewContainerState> { WebViewContainerCubit() : super(WebViewLoading()); loadSuccess(int progress) { if (state != WebViewLoadSuccess()) { emit(WebViewLoadSuccess()); } } loadError() { emit(WebViewLoadError()); } } /// State abstract class WebViewContainerState {} class WebViewLoading extends WebViewContainerState {} class WebViewLoadSuccess extends WebViewContainerState {} class WebViewLoadError extends WebViewContainerState {}

植入X5内核

pub上也有一些webview for x5的轮子,但都是年久失修,没有持续维护,连null safely都没有支持。

所以我们继续拓展webview_flutter库,新建webview_flutter_android_x5模块,引入X5 SDK,重点对官方的webview_flutter_android相关功能和Api进行替换开发。同时提供Api给业务层,由调用方来决定是否启用x5内核。

植入X5需要一定的原生基础,这里不对源码进行过多讲解,有机会的话后面我直接开源一个库,把透明背景和x5内核一起弄上去。

UI组件库模板化

这个方式是通过UI设计预埋一些坑位,运营端通过匹配已有组件存储在接口,每次拉取后台服务确定如何展示UI。  这个是非常通用的方式,没有那么动态化,需要先把可能出现的UI先设计出来。但是最靠谱,不过在开发时需要做好UI库的封装、协议的定制,同时要非常注意降级处理,如果网络差拉不到后台数据,那页面如何做显示,这点要处理好。

总结归纳

框架方面毫无疑问Kraken最能应用于生产,但想说的是这些框架都不成熟,想要应用于生产,团队必须有能参与开源,填坑的能力。 另外顺带提一下,腾讯QQ音乐正在准备开源的Kant也可以关注下,基于Kraken进行改造,已经应用于内部生产,值得期待。

而webview增强,UI组件模板化则是相对靠谱的方式,一般团队都有能力维护。

这两种方式,运营平台的同事就不需要再去学习新的Api,管理后台的配置动态内容的开发成本也小很多。