演讲实录——Flutter-闲鱼的探索与收获 [复制链接]

2019-4-29 10:48
九霄逆鳞 阅读:778 评论:0 赞:0
Tag:  
本文来自2019安卓巴士开发者大会现场实录,由于录入匆忙,内容可能存在偏差,欢迎大家扫描文末二维码查看现场实录视频和下载大会完整PPT。作者:熊华丽

大家下午好!我来自闲鱼的匠修。我本名叫熊华丽,我在阿里的化名叫匠修。我自己是做安卓系统出身的,我在深圳做了5年的安卓系统开发,又去了杭州阿里巴巴,然后在闲鱼做了3年的客户端开发,今天也算是重新回到深圳了,我对大家感觉非常亲切。我看到大家这么多人在一起,我又感觉到非常幸福。今天是星期六,大家不用去加班,还要过来这里。我们没有996,也没有去ICU,我觉得我们这些人应该是在中国幸福感爆棚的一群人。Flutter是当下比较热门的跨端开发技术,因为它使用了前期的跨端思路,它的前途是一片向好的。闲鱼在很早期的时候就开始关注Flutter的发展,我们在非常早期的时候,可能Flutter连第一个版本都没发出来,我们就跟谷歌的官方团队建立合作,我们也一直跟谷歌的官方团队Flutter保持了很好的技术沟通。现在Flutter在闲鱼已经成长为了可以支持核心业务的主力的技术栈。

闲鱼在做Flutter的实践中分为三个阶段。第一阶段,前期我们需要对Flutter做一些技术栈的验证,我们看这个技术栈是否能够达到工业开发的标准,后来我们又需要对Flutter和原来的技术体系做融合,做能力上做双向打通。后面大量使用Flutter的时候,我们又需要对Flutter做基础建设,来提高日常开发的效能。闲鱼在这个过程中做了非常多的事情,今天我在这里不可能把毕生绝学在一个小时之内传给大家,这不太可能。就是说,我能不能顺着闲鱼的实践过程,从里面摘出几个比较有典型意义的代表问题,然后跟大家分享一下闲鱼解决问题的思路和一些方案。Flutter首先是一个新的跨平台技术栈,我们知道,它确实能够提高开发效率,它也不是万能的,只是它作为一个技术有自己的优点,也会有自己的缺点,我们在使用Flutter之前应该去了解一下,去考察一下Flutter的技术细节,和原来的技术去做对比。

(PPT图示)左边是Flutter大概的架构图,它可以分为两层,一个是底下的C++实现的Engine层面运行与检索,还有上面用Dart实现的Framework。Flutter就是通过这两层,它仅仅借用平台原生的图形渲染能力,使用定制的运行引擎和UI Widget,在底层实现跨端。这种做法的技术成本是很高的,有可能只有像谷歌这样的爸爸才能做得出来。因为它采用这种做法,它有自定义的Engine和Framework,所以它在在技术上可以做到非常深度的优化,所以它的运行性能就非常好。根据闲鱼的实践统计的数据和我们做的测试,我们可以看一下Flutter和当下的技术栈的对比。首先,Native拥有最好的用户体验,它的开发成本也是最高的。H5大幅降低了开发成本,同时也牺牲掉了用户体验。RN就是在H5的输入上,对用户体验做了优化,但我们觉得它依然不是很完善,因为RN最近好像发展得不是很顺利。Flutter既有一个比较好的开发效率,几乎可以媲美Native的运行性能,它的用户体验非常好,非常适合用来对开发效率对跨端有要求,同时它又要保持有良好用户体验的场景。在闲鱼里,我们通常使用Flutter来开发我们的主流业务。

整个闲鱼是一个非常大的APP,里面不仅只有Flutter,也会有一部分Native开发,也会有H5的开发,我们也引入了集团的Widget开发,类似于RN,还有Flutter开发。就是说,现在我们有4套技术栈。我们的想法非常简单,我们会在具体的业务场景下选择最合适的技术栈,充分利用这些技术栈各自的优势做到取长补短,相互补充。我们早期对Flutter做了调研之后,发现Flutter可以对整个闲鱼的技术结构体系做一个很好的补充。把Flutter引入到我们的工作中,需要把Flutter和原来的技术能力做融合。在融合的过程中我们发现了一些问题。

大家如果入门Flutter的话,仅仅使用Flutter的技术去编程的话,你想要在Flutter页面打开一个原生的页面,你会发现好像没这个能力。这个问题很容易解决,你可以直接到Flutter Pages里面找一个插件,有很多这样的插件,就可以提供这样的能力。这从层面反映了,现在Flutter的页面和原生的页面之间还是存在一定隔阂的。Flutter的技术渊源,它和WEP技术有非常深的渊源。我们在理解Flutter的时候,可以把Flutter类似于大幅优化的做法,它在平台中的运用是非常灵活的。现在闲鱼的首页有这样的场景,有4个Tap(音),下面有个Tap就是社区。我们引入Flutter之后就想,能不能把它改成Flutter的开发,然后改善客户体验。我们研究了一下发现,Flutter提供的能力,现在是把它放在Flutter里面打包给你,要不你就不用。它是不能做到混合的,比如部分使用Flutter,另外一部分还保持原生,Flutter现在还没有这个能力。在闲鱼里面就会出现下面这个图像(PPT图示),它会出现在这个页面栈里面,Flutter的页面和普通的页面会交叠出现,它会对接很多的页面栈,这对内存的使用是一个很大的压力。其实这不光光对内存使用是很大的压力,它限制了很多优化的场景。比如一个Flutter页面,如果它被另外一个Flutter页面盖住的时候,这个Flutter页面对用户来说是不可见的,我们可以把被盖住的页面里面很多资源释放掉,正在加载的资源也可以把它做一个暂停,因为它也不可见了。但是如果每一个Flutter页面都是放在各自的环境里面,它在自己的环境里都是属于最上层的页面,那我们就没办法去做这样的优化了。基于此,闲鱼建设了一个基础设施,叫混合栈。如果将Flutter引入到整个工程中来,最起码还要分为两批页面,一批是Flutter页面,一批是native页面。要实现这两个页面之间协调调动,实现混合使用,实现业务功能的话,中间一定要有一个调动的逻辑层,整个混合栈就是一个混合调动的逻辑层。

混合栈提供三个能力:第一,最基础的能力,要把Flutter页面无差别接入到本地页面的路由系统里,本地的其他页面就可以很好地通过本地路由系统去打开Flutter页面,它就可以很好的在业务实现上做很好的协同。第二,需要补充Flutter嵌入的开发能力。谷歌已经意识到这个问题,也往这方面做优化。我们在建设的这个东西也一直在跟进,如果谷歌放出这方面的能力,我们后面可能会把谷歌的能力很快补充进来。第三,你要统一调度这两种页面栈的话,它是一个复杂的逻辑,整个混合栈需要对这些逻辑进行分装,要对Flutter和Native交叠出现的场景做必要的优化,整个产品才能达到上线的标准。混合栈在闲鱼里已经做了两个版本的迭代。经过第一个版本的实践之后发现,如果要协同调度好两个页面栈,并不是一件非常容易的事情。在安卓里面各个页面栈是一件非常复杂的事情,在安卓里一个APP可以起多个页面连接栈,在栈中每次打开的时候都有4种打开模式,它的行为是非常复杂的,因为我们还要支持后面Flutter的页面模式,所以它整个逻辑是非常复杂的。在后期开发的时候,为了简化这种逻辑,我们在做Flutter部署的时候要确定一些原则,所有的Flutter环境中的页面栈行为,都必须通过本地去驱动它。也就是说,要通过Native里面的生命周期去驱动容器里Flutter页面的生命周期。打个比方,比如说我现在有个Flutter页面在跟用户交互,当用户点击back键的时候,Flutter页面理论上马上就要top(音),如果用混合栈的话,它不能自己去调度,必须把这个团传递到混合栈里面去,混合栈会再去驱动里面的Flutter页面,通过这样一种逻辑,有这样的原则的话,后面做很多功能开发,做能力拓展的时候,就不至于把这个逻辑搞得特别复杂,因为搞复杂之后你可能自己都理不清楚它们之间双向的驱动关系。这样做的前提是此,要实现Activity和Flutter中的页面,要实现在逻辑上的一一映射。

经过一段时间的实践之后,在做Flutter部署里面,总结出Flutter混合栈实现的几个难点,也可以把它讲成混合栈实现的几个要点。第一,不侵入官方的SDK。这也是我们的一个教训,我们在第一版本的时候是通过修改官方SDK中的源码去获取部分能力,我们在做这个事情的时候,有部分能力确实需要官方SDK提供,但是官方SDK没有往这方面做过多的考虑,所以很多功能没有提供得很好。我们在做第二版的时候非常克制,严禁动官方SDK的源码,这样当官方SDK升级的时候我们可以跟着升级,就不至于在升级的时候落后太多的版本。这样做有一个好处,官方SDK中API的语义不要去动它,我们可以对这个语义做包裹或做增强,但是不能改变它原来基础的语义,开发者在做开发的时候就不需要去赋能,对开发者做一个透明。第二,这是一个小坑,我们要实现Flutter页面的生命周期和Native的生命周期做很好的互通和兼容,它很容易产生坑。打开一个新的Activity,我们通常会走Opors(音),然后再走一下Ostop(音)。但是在有些情况下,比如设置了特殊的主题或者有些特殊的场景下,它可能仅仅会走一个Opors生命周期,它不会走到Ostop里面,在实现的时候对这种场景要有充分的考虑。第三,因为Flutter现在还不能很好地深度使用,我们要把Flutter6的一些环境从Flutter Activity中剥离出来,然后做一个标准运行容器的定义,要做一个基础的分装给到开发者,这样开发者才能非常灵活去是否Flutter的能力,他可以把Flutter当作Activity的一部分去嵌入使用。第四,我们要实现前面讲的原则,就是说,所有Flutter层的页面栈行为都要用Native层去驱动,这有一个前提,所有Flutter页面和本地容器的Activity都有一个逻辑上的映射关系,但整个安卓APP的栈管理非常复杂,它的数据结构非常复杂,它可能不是一个单纯的栈的数据结构,因为每个APP运行的时候可以起多个test的栈,每个Activity的行为都很复杂,因为它有4种启动模式,它不是一个简单的行为,你打开Activity的时候有可能这个Activity会新起一个页面栈,有可能这个Activity会复用老的页面栈的实例,它有可能会在前面的栈里面找到这个Activity,然后把上面这些Activity全部出栈,整个行为很抽象也很复杂。Flutter是对这种行为的特别延展,它的抽象很简单,就是一个简单的栈,它的行为也可以简单获取。我们研究了一段时间的Flutter代码之后,发现Flutter默认有个栈管理的能力,简单的页面栈行为管理的能力,但是它又允许你自己实现页面栈管理的能力,我们在Flutter实现了我们想要的页面栈管理的能力,通过这样来实现逻辑上的一一映射关系。

看一下这个代码,是Flutter部署初始化的代码。最关键的用红线标出来。一般Flutter app都会采用安卓Material的模式,这个方法引起你注入页面管理,定制自己的页面管理行为。我们现在已经实现了本地技术栈能力和Flutter技术栈的融合,在能力上就能做到相互打通,就可以开发业务了。闲鱼在最开始的时候是用Flutter重构了宝贝详情页,当时有3个同事,请了一个项目去做这个事情,做完之后就解散。后面有新的同学进来,看到前面同学的代码,第一感觉就跪掉了。他们的代码各有各的风格,很多功能组件都很模糊,可能数据放在最外层,界面在非常深的层次上,组建也提得不标准,很难把以前的工作运用起来。通过以前的开发经验我们知道,面对这种场景,非常有必要给业务开发同学做一个开发框架,有了这个开发框架之后,业务开发人员对做开发代码有一个大约的规约,我们会达成一个共识,写代码的风格也是类似的,逻辑都是共享的,可以加速以后的业务开发。

我们做这个事之前,先来看一下现在Flutter的业界,就是Flutter的社区里都有哪些业务框架可以供我们考察。第一个是scoped 业务框架,非常容易上手,它是利用Flutter的特性,实现简单的数据监听模式,有基础的数据关系和数据流。大家要研究Flutter的业务框架数据的话,先通过这个框架做入门是非常好的选择。这个框架的抽象能力不高,也很难支持复杂业务场景。第二个是BLoC,响应式流式编程,,清晰的数据流,非常值得大家去借鉴了解。这种编程方式能够带来非常清晰的逻辑,也能带来很清晰的数据流。第三是Flutter-Redux,是闲鱼在早期使用的业务开发框架。Redux有一个好处,有一个统一的状态管理,就是对数据处理的逻辑,对整个业务逻辑做了很好的规范定义,它把需要改变数据同步的逻辑想象成Redux,你在开发业务逻辑的时候很简单,它本身相当于一个逻辑框架,搭建好了,你做一个填空就好了。这就是一个标准的逻辑了。它做得非常好。Redux使用了action的方式,一般和用户进行交互的时候,会发出一个action,表达用户交互的意图,至于这个意图会带来哪些变化,界面里是完全不关心的。通过这两个,Redux非常适合大规模的比较复杂的页面编程。

闲鱼经过一段时间的实践,基于Flutter Redux做了挺多个版本的迭代,最后发现,社区很多框架里面,对页面内的复杂度预期不足,在闲鱼的场景下,一个核心的主页面可能逻辑非常复杂。闲鱼总结出了我们自己需要的四个能力,就是需要编程框架提供这四个能力。第一个是统一状态管理,可以对逻辑做标准化规范,有利于写代码员的共同。第二个是分治能力,非常有利于大型团队进行协同,可以同时有四五个同学来开发一个页面,再把它整合起来,效率很高。第三个是复用,是加速开发的方法。第四是编程模版,写出来的代码风格是类似的。基于这四个需求,闲鱼在后期开发了自己的业务模型框架,就是fish-redux。这是社区标准的Redux概念,如果大家熟悉Redux的话不用担心,就是你们熟悉的原汁原味的Redux,我们没有做破坏。它提供一个Component层,就是一个组件。还有一个Adapter层,这是一种高阶的进阶。

Component是我们设计的核心,我就介绍一下这个Component。我们对Component的定义,它是一个最小的业务单元,有自己的数据、逻辑,在某种情况下是一个自闭单元。Stateful是一个常用的组合,在安卓里面可以用。
你可以通过d在交互的时候发出一个action的数据。也可以通过S去监听,提供页面数据的连接器,从页面的数据里把自己要的数据取到。整个组件里是有逻辑的,需要改变数据的同步的逻辑,把它放在Redux里面。最后还有一个非常重要的是Slods,是组件插槽。通常有些组件并不关心具体的业务实践,相当于定义接口,类似于细节类的方式,把具体要实现的功能点通过差点的方式流出去,自己去关注怎么组合起来,通过叫做模版组件。

我们大概看一个例子,就是闲鱼的宝贝详情页。首先看这个页面,最大的就是Component,外层的Component不关注具体的业务,会根据不同的数据去组合出不同的宝贝详情页来,具体的功能流给下一层的Component实现,比如有视频的Component,有留言等。通过这种方式就可以很好地实现分治和复用。我们在开发新页面的时候,通常可以用到老页面里面的很多元素,我们只需要对某些特定的Component去实现就够了,可以利用老的Component,通过新的Component来组合它,加速开发。

这就是闲鱼里组件的代码样图,代码看起来风格都非常像素,组成要素也差不多,你写得好与不好我心里也是有一根秤的。每一次产品上线都是很大的考验,你做了很多前面的准备,也不敢保证完全不出问题。所以我们团队里经常调侃:人在家中坐,锅从天上来。在数据运行的时候,需要对线上运行的质量状态做质量监控,再做对比,就知道技术的点优化得是否可以达到预期的效果,后面还需要做什么。整个数据监控是非常关键的。在阿里巴巴数据是一种信仰,如果没有数据,什么都面谈。对于产品监控的数据多多益善。

分享一下在Flutter下面数据监控的维度和收集的方法。第一个最关键的是线上运行稳定性监控,在Flutter里面,信息怎么拿到呢?有三个方法,第一,这个框架里提供了默认的异常处理函数,我们可以对这个处理函数做包裹,在包裹的代码里上传信息的代码。第二,Flutter是运用信息开发的,它的本质是不共享内存的,它是放到一个个Slods里面运行的,可以拿到它的运行信息。第三,Flutter是在幂函数里进行的,它给你提供了ol的回调。第一种方式拿到的信息是最全的,后面两种方式可以作为补充。第二,性能监控。也很关键,因为你上线了Flutter之后,等到用户再去骂娘的时候,可能很多用户已经把这个APP卸载了。我们有一个线上的监控体系,有一些指标,有滑动,页面加载时长等。

Flutter分为两层,这两层有代码的交付层,后面做开发会遇到B。我们采取一种方案,可以在运行的很早期把这个框架建起来,通过这种方法可以拿到一些生命周期的回调。可以通过这两个去看一下增率的情况。前面讲了很多Flutter的问题,怕给大家带来一个印象,Flutter好像坑太多了,我还是不搞算了。我自所以讲这些问题,因为时间很短,我不可能把很多事情讲得面面俱到,我又不想讲概念,想讲我们的一些思考。Flutter整体用起来是一个非常成熟的框架,但是如果想接入一个新的技术栈,完全一点坑都不踩是不可能的。如果在开发的时候感觉有一些能力没有,可以进入到Flutter的Pub里面去找一找。如果你想进阶成为Flutter的高手也很好,可以去github里面看一看。

闲鱼走得比较早,我们很早和Flutter官方团队有一些合作,对于一些遇见的问题都有一些方案了,我们在社区里会把这些方案做分享,给后面的同学做借鉴和参考。因为Flutter现在还是不支持反射的,所以有一些账号还是不好做的。我们现在用Flutter的业务开发框架,其实已经把它开发出来了。还有很多数据收集、高可用的框架,后面做整体之后也会把它开源出来。闲鱼还是非常想在Flutter社区和大家一起携手并进,通过新的技术为客户端技术提供新的可能。大家如果感兴趣可以关注闲鱼技术的微信公众号,谢谢大家!

flutter_boost: https://github.com/alibaba/flutter_boost
fish_redux: https://github.com/alibaba/fish-redux

现场PPT分享:       
关注【安卓巴士Android开发者门户】公众号,后台回复“420”获取讲师完整PPT。


大会现场视频小程序:


我来说两句
您需要登录后才可以评论 登录 | 立即注册
facelist
所有评论(0)
领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )