京东技术中台的 Flutter 实践之路,android 界面开发经典书籍
核心组件
组件管理:组件之间通过标准的协议接口进行通信,降低组件耦合,便于维护及组件升级;
状态管理:实现数据和界面分离,统一状态管理,以数据的变化来驱动界面的改变,更有利于数据的持久化和保存,同时也有利于 UI 组件的复用;
Hybrid Router:主要解决 Flutter 和 Native 之间交叉跳转的问题,减少内存开销,共享同一个 Flutter Engine。
工具介绍
编译发布:优化 Flutter 原有的编译逻辑,管理依赖 Flutter 原生依赖关联,打包 Flutter 和原生代码,实现自动化构建发布。
资源管理:管理图片资源,将资源转换成 Flutter 类,便于资源的读取操作,类似 Andorid 的 R 类;
模版代码生成:减少 Flutter 的代码编写,自动生成 Flutter 组件的框架模板代码,提升代码编写效率;
JSON 转换:将 JSON 数据转换成 Flutter code,并提供 json 转 Flutter 对象的 API,减少动手编写 Flutter code 及解析。
JDFlutter 业务开发实践
JDFlutter 为业务研发团队提供了全流程的开发解决方案:
配置混合工程
Flutter 和原生混合开发有两种情况,其一,开发 Flutter 业务的同学,需要和原生做交互,因此需要有 Flutter 和原生的混合编译环境;其二,使用原生 SDK 开发业务的同学,需要和 Flutter 业务一起集成打包,此时需对 Flutter 透明,以减少对 Flutter 编译环境的依赖,并且,只依赖原生编译环境即可,此时我们将 Flutter 编译成 aar 依赖,放入原生项目中即可。接下来,我们将重点介绍 Android 和 iOS 的混合编译环境配置。
Android 平台配置
创建一个 flutter module
flutter create -t module --org com.example my_flutter
在原生根项目的 settings.gradle 加入如下配置信息
// MyApp/settings.gradle
include ':app' // assumed existing content
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'my_flutter/.android/include_flutter.groovy' // new
))
在原生 App 模块中加入 flutter 依赖
dependencies {
implementation project(':flutter')
}
这样就可以原生项目一起编译了。具体可以参照官方文档:http://github.com/flutter/flu…这样的方式虽可以满足混编需求,但还不是特别方便,开发完项目后,还需要去 Android Studio 项目中进行编译,比较麻烦,所以我们也可以把 Flutter 项目 settings.gradle 改造,在 Flutter 开发环境下直接运行包含原生代码的混合项目,改造方式如下
// MyApp/settings.gradle
//projectName 原生模块名称
//projectPath 原生项目路径
include ":$projectName"
project(":projectPath")
这样改造之后即可在 Flutter IDE 中直接编译 Flutter 混合工程,并进行调试,也可以运行 futter run 来启动 Flutter 混合工程,不过在配置的时候,需要注意 Flutter 中 gradle 编译环境和原生编译环境的一致性,如果不一致可能会导致编译错误。
iOS 平台配置
创建 flutter module
flutter create -t module my_flutter
进入 iOS 工程目录,初始化 pod 环境(如果项目工程已经使用 Cocoapods,跳过此步骤)
pod init
编辑 Podfile 文件
#在 Podfile 文件添加的新代码
flutter_application_path = '/{flutter module 目录}/my_flutter'
eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
安装 pod
pod install
打开工程(***.xcworkspace) 配置 build phase,为编译 Dart 代码添加编译选项打开 iOS 项目,选中项目的 Build Phases 选项,点击左上角+号按钮,选择 New Run Script Phase,将下面的 shell 脚本添加到输入框中:
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
搭建 PUB 私服仓库
Flutter 开发中使用的组件,一般公司内部会采用共享的方式,以避免重复开发,而 Flutter 组件共享,即需要使用 pub 仓库。由于公司内部的业务组件不适合上传到 pub 官方仓库,因此,需要搭建私服仓库,以解决各个业务研发团队,对 Flutter 组件共享需要。感兴趣的同学可以研究下官方 pub 仓库的源码 http://pub.dartlang.org/,其对 Google Cloud 环境有很大的依赖 , 也可以基于https://github.com/kahnsen/pub_server来搭建一个简易版本的私服仓库,以满足上传和下载功能,pub 协议相对比较简单,我们可以在源码增加协议接口来实现更多功能。运行 pub_server
~ $ git clone https://github.com/dart-lang/pub_server.git
~ $ cd pub_server
~/pub_server $ pub get
...
~/pub_server $ dart example/example.dart -d /tmp/package-db
Listening on http://localhost:8080
To make the pub client use this repository configure your shell via:
$ export PUB_HOSTED_URL=http://localhost:8080
发布一个 Flutter 组件需要修改 pubspec.yaml,增加以下内容:
name: hello_plugin //plugin 名称
description: A new Flutter plugin. //介
绍
version: 0.0.1//版本号
author: xxx xxx@xxx.com//作者和邮箱
homepage: https://localhost:8080 //组件的介绍页面
publish_to: http://localhost:8080//仓库上传地址
上传时可以使用如下命令检查代码错误,并显示出上传的目录结构。
pub publish --dry-run
如果有不想上传的文件,可以在根目录增加一个.gitignore 文件来忽略如下:
/build
Flutter 组件的依赖配置,在项目的 pubspec.yaml 中 dependencies:下增加如下信息:
dependencies:
hello_plugin:
hosted:
name: hello_plugin
url: http://localhost:8080
version: 0.0.2
这样可以在公司内部实现 Flutter 组件共享,如果不想搭建自己的 pub 仓库,也可以采用 git 依赖,配置如下:
dependencies:
hello_plugin:
git:
url: git://github.com/hello_plugin.git //git 地址
ref: dev-branch //分支
Flutter 业务的开发与调试
在 Flutter IDE 中编译代码调试会很方便,直接点击 debug 按钮即可进行代码调试,如果是混合工程在 Android studio 或者 xcode 中运行的工程,则没办法这么做,但也可以实现调试:将要调试的 App 安装到手机中(安装 debug 版本),连接电脑,执行如下命令,同步 Flutter 代码到设备的宿主 App 中
$ cd flutterProjectPath/
$ flutter attach
执行完命令后会进行等待设备连接状态,然后打开宿主 App,进入 Flutter 页面,看到如下信息提示则表示同步成功
zbdeMacBook-Pro:example zb$ flutter attach
Waiting for a connection from Flutter on MI 5X...
Done.
Syncing files to device MI 5X... 1.2s
?? To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler on MI 5X is available at: http://127.0.0.1:54422/
For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".
打开http://127.0.0.1:54422可以查看调试信息,如有代码改动可以按 r 来实时同步界面,如果改动没有实时生效可以按 R 重新启动 Flutter 应用。
JDFlutter 热更新实践
大部分跨端框架,诸如 React Native / Weex / H5 等,基本都能做到随时进行热修复,并随时上线,用于及时修复突发的在线问题,架构非常灵活。Flutter 因其 AOT 的设计,预想会很难达到这种灵活度,但技术上仍具有一定的可行性,正如我们在之前的 Flutter 介绍文章中提到的,按照先有的 API 设计,是可以支持热修复的,但仅限于 Android。官方最新的架构上已经支持了热修复架构,大家可以更新到 1.2.1 版本查看,但是官方的功能还比较弱,无法做到版本控制和回滚的灵活性,所以 JDFlutter 并没有采用。我们可以首先一起看一下 Google 官方热修复方案的设计原理:Flutter1.2.1 版本引入了 Dynamic Patch
为了更清楚的了解官方热修复的原理和过程,我们需要首先深入了解 Flutter 的业务包结构和整体运行过程:
Flutter App 的包结构
可以看到主体代码集中在 asset 目录中,除此之外还有少量 Android 端的框架 java 代码及 flutter so 引擎库外:
1、icudtl.dat
2、isolate_snapshot_data
3、isolate_snapshot_instr
Flutter 包的初始化流程
Flutter 页面启动时是如何加载这些代码的呢?那就要从 Flutter 的初始化说起了,在页面启动前需要调用 FlutterMain.startInitialization 来做初始化:
可以看到该初始化是要求在主线程完成的,另外主要完成了以下三点:
评论