腾讯翻译君Android客户端集成Flutter Module的探索

前言

这篇文章介绍翻译君客户端如何在现有的原生App中加入一些flutter页面,把已有的工程改造成flutter和native结合的App。
目前翻译君App中已经应用了大量Flutter页面,性能良好。
分享出来,如果有好的方案可以互相交流。

集成方案

如果需要本地开发,可前往flutter.io下载SDK并配置环境变量。
在配置完Flutter环境之后,如果通过IDE或者CML创建一个Flutter Application,目录结构是这样的:

Project
├── android //原生android app目录
├── assets // dart引用的资源文件,比如图片
├── build
├── gen
├── ios //原生iOS app目录
├── lib //dart代码目录
├── pubspec.lock
└── pubspec.yaml //包管理工具pub

这是一个完全通过Flutter构建的App,入口也是Flutter的lib目录下的main.dart;那么通过在lib目录编写dart代码,就可以通过flutter run命令生成Android和iOS安装包安装运行。对于Android来讲,是通过flutter.gradle来添加flutter.jar和vm/isolate_snapshot_data/instr,用它们编译lib中的dart代码,嵌入App中运行的。

那如果想要在已有的App里增加一些flutter页面,根据上面的目录结构,可以有两种思路:

graph1. dependecy

左边的方案,是在普通的Flutter Application的基础上,把已有的Application替换到Flutter Project的子目录;国内较早使用混合flutter开发较多的团队使用的一般是左边这种方案。后来大家也发现了这种方案的弊端,也就是耦合严重导致的开发效率降低。于是很多团队都有自己的方案进行依赖清理,达到右边这种集成方式。
好处是:
1)Native工程可以独立构建,也可以迅速与flutter模块交互,实现「热插拔」。并且,jenkins打包也不再一定需要flutter环境。

2)Flutter工程可以通过flutter attach在chrome中进行调试,hot reload等等。如果使用Android Studio,并且把flutter模块作为子目录,无需打开两个IDE,可在一个窗口中写native代码和dart代码。

有兴趣的同学,可以参考闲鱼早期的方案

好的集成方案

好消息是,Flutter团队也意识到了这个问题,大家可能一开始只是想在已有原生工程中集成flutter,而不是写一个纯flutter的app。所以,他们也在紧锣密鼓地制定更简单的集成方案

翻译君使用了Flutter提供的创建模块的方式来构造flutter模块,可以参考Add Flutter to existing apps
也就是,使用$ flutter create -t module my_flutter创建flutter模块。
这样就直接实现了上面图1右边的剥离的flutter模块带来的种种好处。

注意,生成的Flutter Module可以放在Android、iOS原生工程的外面,也可以放在原生工程的内部,使用git submodule来管理,两种方案各有优劣,可以自行选择。放在外面的好处是在本地同一台机器开发的时候,Android、iOS工程可以依赖同一个Flutter目录,方便修改和调试;放在目录内部作为git submodule的好处是,可以在同一个IDE窗口中开发调试native和dart代码。

源码依赖和aar依赖,打包

  1. 源码依赖
    源码依赖的优点是开发、调试方便,也就是在settings.gradlebuild.gradle里加入对创建的flutter模块的依赖即可,可参考上面的链接。
// 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
))                                                                      // new
  1. aar依赖
    那如果需要用jenkins/RDM远程打包,远程的机器上没有flutter环境,就可以把flutter和一些plugin打包成aar文件进行依赖。可以进入Flutter目录和plugin目录,分别运行./gradlew assembleDebug/assembleRelease来生成aar文件,再在主工程里引用,flutter aar中包含了flutter sdk的代码,所以这种方式是不需要flutter环境的,也适合第三方快速接入。

由于这两种方式只是在 settings.gradleapp/build.gradle中有一些差别,所以可以利用脚本实现快速切换两种打包方式,切换完成之后sync即可。

flutter/native混合页面跳转

使用了stack manager实现native/flutter页面跳转,原理简单讲就是使用openurl来路由nativeflutter页面,比如hrd://profile_page代表个人信息页。在flutternative侧各有一个路由中心来控制分发。

这种方式的优势是在混合页面比较多的情况下可以方便地管理跳转,通过截图手段减少了跳转的白屏/黑屏现象;缺点是少量修改了flutter sdk里的源码,以及使用起来比较麻烦,比如参数传递的功能实现得并不是很完善,我fork了一份对它进行了少量修改,它的原工程写得蛮厉害的,我觉得大家可以在原工程的代码上做适合自己的精简或者优化。

另外,我认为如果新增的flutter页面居多,可以尽量多用flutter本身的跳转方式,无需采用Activity代理的方式,这样性能更好也更方便。

调试和hot reload

作为模块导入的flutter工程也是可以调试和hot reload的,但不能直接在IDE中进行,需要借助Observatory在浏览器中进行。
在flutter工程目录下运行CML,输入flutter attach。如果无法attach,可以重启VM或者检查代理。
打开一个flutter页面,CML提示An Observatory debugger and profiler on Android SDK built for x86 is available at: http://127.0.0.1:55428/。
在CML窗口按下r可以hot reload, 按下R可以hot restart。
在chrome地址栏输入http://127.0.0.1:55428/,可以进入Observatory进行debug等操作。

插件(plugin)管理

Plugin Packages插件可以放在flutter模块的子目录比如plugins通过yaml依赖,这样,在源码依赖的时候,主工程的gradle会自动通过flutter.gradle识别插件中包含的原生工程代码。

graph2. plugin packages

如果需要提供桥接,可以通过注册监听器的方式来规避双向依赖的问题;比如插件中需要引用外部工程的代码,这肯定是不合适的,这种情况就可以通过接口回调的方式实现flutter对native功能的调用。

一些issues

  1. ./gradlew build的时候提示找不到product_vm_isolate_snapshot.bin的问题。这个应该是flutter本身的问题。已提issue。可以执行gradlew assembleDebug/assembleRelease规避。
  2. 找不到icudtl.dat的问题。这个也是一个flutter的issue,貌似仍未解决(flutter团队更新0.9+以后以为解决了其实并没有),目前可以通过手动拷贝它到assets/flutter_shared/目录规避。
  3. "flutter/runtime/dart_vm.ccVM snapshot must be valid/ duplicated resources"之类的问题,可通过手动删除app/build目录进行规避。

目前简单总结了这么多,有问题可以多多交流。

https://github.com/chaangliu

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

推荐阅读更多精彩内容

  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,473评论 2 59
  • 夏天,羊羊再一次搬家了,大人们忙里忙外,小孩子尽量不往前凑,避免大人们将烦躁发泄在他们身上。 1,2,3。。第六次...
    走边关的蒲公英阅读 254评论 5 0
  • 上回书说到,约瑟发现,给埃及国王法老敬酒的那个朋友,不会来救自己了,很失望,上帝跟他说,不要着急,法老是国王,你觉...
    GodFatherPaul阅读 678评论 0 0
  • 野蛮撕裂骨架的组合喇叭吹出火焰山大气在燃烧天是个倒过来的锅飞行器划过的痕迹这是大厨用铲子翻滚天锅烟火缓缓散去去与白...
    一元真人阅读 211评论 0 3