CSDN编者按】很少有文章,介绍如何将大型应用,移植到Flutter。而本文的作者——一位来自澳洲的Native iOS & Flutter的开发者,尝试这样做了,结果让他十分惊讶。到底是什么情况?一起来看文章吧!
澳大利亚有一个名为Easy Diet Diary的原生iOS应用程序。
该应用:
• 已被下载120万次;
• 用Objective-C和Swift编写,后端是Amazon AWS;
• 代码统计工具CLOC,报告该应用包含75,000行代码。
我在这家小公司工作了很长一段时间,他们的任务列表上一直有个安卓版本,但我们一直没有开发,因为:
• 支持两个代码库需要太多精力且难以管理。
• 跨平台开发的主要选择Xamarin和React Native都有重要缺陷(这是另一个故事)。
最后,我们选择进入Flutter的世界!
Flutter的速度很快,并且可以保证用户界面体验,而且一切都与原生应用没有区别(特别是有了2018年9月加入的iOS小窗体之后)。
以下是本文的主干:
• 代码行数与开发速度
• 架构
• 社区
• 性能
• 语言
• 还缺少什么?
• 结论
第一阶段我预估需要6个人月。但是,项目进度居然领先了!对我来说,这简直太不可思议了。
为什么?
Flutter的布局并不是像iOS那样,使用的不是Storyboard,或者像安卓那样使用XML,而是在代码中将窗体组合成窗体树,即可创建应用程序的用户界面。这对我来说听起来有点可怕,但是我做了尝试,虽然花了一些时间来习惯,但不久我就可以灵活地使用这些窗体树了。
然后,大约第一个阶段进展到第三个月的时候,发生了一件很奇怪的事情。随着我越来越熟练、越来越快地移植功能,项目中的代码行却开始迅速减少了。这很奇怪,因为我移植的业务逻辑数量非常庞大,这些逻辑在代码数量上的比例几乎是一比一。
事情的真相是,我可以通过创建类和编写函数来重用用户界面部分的代码,这比使用原生iOS更容易。通常,我可以利用几个额外的参数,简单地重构用户界面的窗体就可以重用它们。如果这样不行,我还可以简单地在现有的窗体周围再包上另一个窗体,就可以实现需要的行为了。这简直太赞了!
最终我预计代码行数将少于30,000(而原生iOS版本为75,000行)。当然,原生iOS版本包含一些虽然当时开发了最终却被取代或未被使用的代码。我估计未使用的代码占15,000行。换句话说,需要移植的代码量为60,000行。
因此,总的来说,Flutter的代码量只有原生iOS原有代码的一半!
此外,Flutter项目不包含任何Storyboard XML。Storyboard中有很多XML。原生iOS项目包含:
• 15个Storyboard;
• 47个Nib文件;
• 92个View Controllers。
我与Storyboard斗争了很多年,一直在尝试遵循最佳实践,最后感觉在Flutter中构建用户界面非常自由而且速度很快。
以前我没有意识到自己的大部分时间都用在了编写与用户界面相关的代码,还要忍受大量的Storyboard和自动布局限制。
我无数次听人说应该将用户界面布局与代码分开,我非常同意,但就我的Flutter经验而言,这一点未必是真的。而且关于用户界面分离的这种概念也不仅仅是原生iOS的最佳实践。
我记得自己在微软WPF中与XML作斗争,也见过Quora上有人问《为什么安卓使用XML来定义用户界面而不仅仅是Java代码?》
Storyboard是一种自上至下的布局方式(适用于桌面应用程序),而窗体采用自下而上的方法,在构建移动应用程序时,窗体可以极大地简化编程。我听说这种方式类似于React Native和CSS flex-boxes的布局方式。Wm Leler在Hacker Noon上发表的文章《Flutter带来了哪些创新》很好地解释了这个问题。
以下方式对于利用窗体构建用户界面很有帮助性:
• 大多数情况下,Flutter中支持状态的“热重载”功能可以在几秒钟内,将代码更改无缝地整合到正在运行的应用程序中;
• Android Studio的快捷键Alt + Enter可以插入或删除用户界面的窗体(行、列或容器)。请查看这篇文章《使用Android Studio开发Flutter应用程序的小窍门》(https://medium.com/@liewjuntung/tips-on-using-android-studio-to-develop-flutter-apps-9e42c047b7f4)。
我觉得VS Code中应该有类似的东西。这似乎微不足道,但我觉得这个快捷键十分有用。使用热重载和Alt + Enter,我可以在几分钟内做好一个画面,甚至可以在用户界面上做实验。
• 将debugPaintSizeEnabled设置为true。这会在所有用户界面的窗体周围显示鲜艳的边框。实际上这个功能用得并不如想象得多,但是当布局出现问题时该功能确实非常有用。
我在选择架构时做了如下几件事:
• 看了几遍Brian Egan的这个演讲《保持简单、有状态:Flutter应用的架构》(https://www.youtube.com/watch?v=zKXz3pUkw9A&feature=youtu.be)。
• 读了几遍Eric Windmill的这篇文章《有效地使用Flutter的继承窗体》(https://ericwindmill.com/posts/inherited_widget/)。
• 阅读Flutter架构示例:http://fluttersamples.com/。
最终,我按照Eric的建议使用了一种名为Lifting State Up的架构模式,这是Redux的第一步,看起来非常诱人。然而,Redux对我来说遥不可及。由于开发时间紧迫,学习和尝试Redux似乎太令人生畏了。
一路走来,我从Stack Overflowers上的人解答的关于架构的问题中得到了许多帮助。这让我想到了社区。
有关Flutter的开源社区非常多元化,并且非常乐于助人,让我看到了人类的希望(特别是在这些疯狂的时期)。
举个例子,当时我正在使用Romain Rastel编写的flutter_slidable软件包,而且我还提了一个改善建议,仅仅48小时之内他就实现了一个比我想象的更好的解决方案......类似这样的事情比比皆是。
我唯一遗憾的是,我一直忙于移植工作,与我收到的帮助相比,我给予别人的远远不够。
总的来说,参加了内部测试的用户对Flutter的“活泼”非常满意。在同一台iOS设备上,同时并排运行iOS应用与Flutter应用时,并没有看到性能明显下降。
我们的应用没有很多需要大量图形的任务,但有一个功能需要从数百个JSON文件读取数据,然后对该数据进行一系列浮点计算,在该功能中Flutter的应用明显更快。
我没有花时间去研究导致差异的究竟是是读取文件的功能、还是JSON解析或者是日期处理等等,因此我无法做出类别上的判断,但我感觉这无疑是Flutter桂冠上的一颗明珠。
很期待看到其他人能给出怎样的性能测试数据。
Flutter使用Dart,这种语言已经在Google之外萎靡不振,直到最近才随着Flutter再次兴起。
然而,这门语言很成熟且易于学习。幸运的是,我在加入Dart队伍时,拥有更强大的类型功能的Dart 2.0刚刚出现,所以无需再在代码中不断地敲“new”关键字了。
Dart可能没有Swift和Kotlin所拥有的Null和非Null类型,但我很喜欢它的简单性。 例如:
• 以下划线开头的函数为私有;
• 自动格式化意味着不必再头疼我的习惯问题——我喜欢在末尾加上逗号将参数分行;
• 包管理很简单。
还有很多很多。我喜欢这些功能,也许其他人可能不喜欢。对我而言,最重要的是我可以更快地(用奇怪但很愉悦的方式)实现功能。
没有太多缺少的东西。在原生iOS应用中:
• 我在XCode中使用了很多Targets,并结合一堆#define创建bundle ID不同的各种版本的应用。我不知道在Flutter中怎么处理这个问题;
• 我在iOS中使用了一个很好的第三方日志框架,叫做CocoaLumberjack。还没找到能代替它的东西;
• 我使用UIKit框架中的WKWebView来加载并渲染本地的HTML文件(如使用条款、隐私政策等)。我在Flutter中没找到正确的包来实现这个功能。最后只能使用一个包HTML2MD将HTML转换成Markdown再使用另一个包flutter_markdown来渲染;
• 我使用AVFoundation框架,在单一的全屏视图中实现条码扫描、QR二维码扫描和拍照。在Flutter中我还不能如此细致地进行控制,虽然它的Camera包很适合拍照,另一个由facundomedica编写的包fast_qr_reader_view很适合扫描条码。在安卓设备上,这个包与Firebase的ML Kit配合从Camera包中获取实时图像。
读到这里,你肯定不会惊讶为什么我如此赞美Flutter。到目前为止:
• Flutter的用户界面几乎与原生安卓和原生iOS的用户界面没有区别;
• 得益于Flutter用户界面的构建方式,利用Flutter制作新功能比原生代码更快;
• 测试没有收到有关性能劣化的报告;
• iOS版和安卓版之间的代码共享目前达到了90%以上。我还没有集成苹果的HealthKit或Google Fit,但我觉得共享率应该不会下降太多。
尽管Flutter还不成熟(2018年5月才发布正式版),但对于我来说它已经足够强壮了。
我遇到的唯一问题就是Dart同步读取目录的函数List Sync在最近的一次发布中出现了崩溃。我将它加到了Flutter在Github上的代码库中,然后改用异步版本。
原文:https://medium.com/flutter-community/porting-a-75-000-line-native-ios-app-to-flutter-57c6571c57b4
作者:Gary Hunter,Native iOS & Flutter的开发者。
译者:弯月,责编:胡巍巍
“征稿啦!”
CSDN 公众号秉持着「与千万技术人共成长」理念,不仅以「极客头条」、「畅言」栏目在第一时间以技术人的独特视角描述技术人关心的行业焦点事件,更有「技术头条」专栏,深度解读行业内的热门技术与场景应用,让所有的开发者紧跟技术潮流,保持警醒的技术嗅觉,对行业趋势、技术有更为全面的认知。
如果你有优质的文章,或是行业热点事件、技术趋势的真知灼见,或是深度的应用实践、场景方案等的新见解,欢迎联系 CSDN 投稿,联系方式:微信(guorui_1118,请备注投稿+姓名+公司职位),邮箱(guorui@csdn.net)。
ypora 是一款支持实时预览的 Markdown 文本编辑器。它有 OS X、Windows、Linux 三个平台的版本,目前完全免费。
https://typora.io/#
Markdown是一种轻量级标记语言,创始人为约翰·格鲁伯(英语:John Gruber)。 它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档。这种语言吸收了很多在电子邮件中已有的纯文本标记的特性。
由于Markdown的轻量化、易读易写特性,并且对于图片,图表、数学式都有支持,目前许多网站都广泛使用Markdown来撰写帮助文档或是用于论坛上发表消息。 如GitHub、Reddit、Diaspora、Stack Exchange、OpenStreetMap 、SourceForge、简书等,甚至还能被使用来撰写电子书。
在使用Dreamweaver编写网页时,遇到需要插入代码块、流程图、数学公式时,总是显得很无力,效率很低,效果不好,使用Typora会让这些问题迎刃而解,且轻便,简单。
直接看一个demo:
导出为html:
html网页源代码:
其可以导出的格式有:
流程图样式包括:
1、标准流程图源码格式(横向):
```flow
st=>start: 开始框
op=>operation: 处理框
cond=>condition: 判断框(是或否?)
sub1=>subroutine: 子流程
io=>inputoutput: 输入输出框
e=>end: 结束框
st(right)->op(right)->cond
cond(yes)->io(bottom)->e
cond(no)->sub1(right)->op
```
2 mermaid语言库绘流程图
Mermaid 是一个用于画流程图、状态图、时序图、甘特图的库,使用 JS 进行本地渲染,广泛集成于许多 Markdown 编辑器中。
Mermaid 作为一个使用 JS 渲染的库,生成的不是一个“图片”,而是一段 HTML 代码,因此安全许多。
官网:https://mermaidjs.github.io/
Github 项目地址:https://github.com/knsv/mermaid
2.1 横向流程图源码格式:
graph LR
A[方形] -->B(圆角)
B --> C{条件a}
C -->|a=1| D[结果1]
C -->|a=2| E[结果2]
2.2 竖向流程图源码格式:
sequenceDiagram
Title: 标题:复杂使用
对象A->对象B: 对象B你好吗?(请求)
Note right of 对象B: 对象B的描述
Note left of 对象A: 对象A的描述(提示)
对象B-->对象A: 我很好(响应)
对象B->小三: 你好吗
小三-->>对象A: 对象B找我了
对象A->对象B: 你真的好吗?
Note over 小三,对象B: 我们是朋友
participant C
Note right of C: 没人陪我玩
2.3 时序图源码复杂样例
gantt
dateFormat YYYY-MM-DD
title 软件开发甘特图
section 设计
需求 :done, des1, 2014-01-06,2014-01-08
原型 :active, des2, 2014-01-09, 3d
UI设计 : des3, after des2, 5d
未来任务 : des4, after des3, 5d
section 开发
学习准备理解需求 :crit, done, 2014-01-06,24h
设计框架 :crit, done, after des2, 2d
开发 :crit, active, 3d
未来任务 :crit, 5d
耍 :2d
section 测试
功能测试 :active, a1, after des3, 3d
压力测试 :after a1 , 20h
测试报告 : 48h
2.4 甘特图样例:
gantt
dateFormat YYYY-MM-DD
title 软件开发甘特图
section 设计
需求 :done, des1, 2014-01-06,2014-01-08
原型 :active, des2, 2014-01-09, 3d
UI设计 : des3, after des2, 5d
未来任务 : des4, after des3, 5d
section 开发
学习准备理解需求 :crit, done, 2014-01-06,24h
设计框架 :crit, done, after des2, 2d
开发 :crit, active, 3d
未来任务 :crit, 5d
耍 :2d
section 测试
功能测试 :active, a1, after des3, 3d
压力测试 :after a1 , 20h
测试报告 : 48h
教程:
Markdown 高级技巧 | 菜鸟教程(使用 Typora 编辑器讲解 Markdown 的语法)
https://www.runoob.com/markdown/md-advance.html
ref
1 Typora 完全使用详解
https://sspai.com/post/54912/
2 用什么软件画流程图好?-悟空问答
https://www.wukong.com/question/6809962012198568195/
3 Mermaid 实用教程
https://blog.csdn.net/fenghuizhidao/article/details/79440583
-End-
关工具:
1.PyMuPdf 组件, python组件包,含mupdf的核心功能并与mutool功能兼容
备选: FreePic2Pdf.exe 第三方PDF书签管理工具,可直接提取PDF文档目录
2.Python SDK包及PyCharm开发工具(PyCharm只需下载社区版本即可)
相关操作:
提取pdf的索引目录: 有多种方法,常见的如: 1.直接拷贝目录/OCR识别目录 2.PDF阅读器自带获取目录 3.通过zotero和zotfile的提取功能 4.devonthink来创建TOC 5.通过FreePic2Pdf第三方提取软件 6.通过mutool命令行工具包 都可以实现, 这里放几张展示图:
获取PDF目录(不需要Python):
方法一:mutool工具执行命令行获取TOC方法二:mutool run javascript脚本来获取方法三:FreePic2Pdf等第三方提取软件
如上所示: 我们提供的多种方法都可以不依赖Python实现提取PDF目录,但为了后续的文章技术进阶,以及操作流程自动化,我们在本文开始引入python来实现我们的需求.因此这里我们采用的是PyMuPdf来实现.具体的下载链接已放置于文章开头.
获取PDF目录(使用了Python)
步骤一:配置基础的开发环境
Python的安装比较简单,官网下载安装包双击安装即可,因此本文不再详细讲解,(只是要注意本文我们要下载大于2.7的版本来使用,如果你的python环境有问题,请和我的3.9版本保持一致)
到官网下载后双击安装
上面安装好python环境后,接着我们来安装PyMuPDF Python组件包,相关帮助文档可参考如下:
按python版本二种方式选一种来安装
这里我们选择的是下载whl包安装按自己系统下载,我下载的是macos-cp39
按上面方式2下载whl文件并双击安装Mupdf Python组件后,接着我们开始配置PyCharm开发工具, 用最前面的链接下载PyCharm软件并设置好PyCharm的Python SDK版本,具体如下所示:
确保用的是Python3的SDK
步骤二: 编写Python代码,执行生成HTML:
## 提取章节信息import fitz # = PyMuPDFpdfName = "java2.pdf"doc = fitz.open(pdfName) # open PDFtoc = doc.getToC() # get current table of contentswith open("chapter-t.html", mode="w", encoding="utf-8") as f: for t in toc: t[2] = pdfName + "-" + str(t[2]) # show what we have so far chapterInfo = "{}{}-{}{}".format("<p>", t[1], t[2], "</p> ") print(chapterInfo) f.write(chapterInfo)
用IE浏览器打开上面生成的HTML,用Ctrl+Shift+A导入至SuperMemo进行Split命令分割,如图:
IE浏览器打开展示效果SM中使用自定义符号来分割PDF索引目录导入SM操作最终的索引目录导入效果
步骤三: 开始PDF文档的增量学习
我们对上面的分支添加学习计划并Ctrl+L开始学习,同样左侧排列SM软件,右侧排列PDF阅读器: 提示: 取消SuperMemo软件的窗口背景选项,才能进行如下二个软件的左右布局
当复习到对应的元素时打开PDF(PDF学习)
基于Alfred/AHK/Quicker来编写脚本,实现搜索并打开PDF,接着按页或章节内容跳转到指定章节
按PDF索引数据打开PDF基于PDF索引的增量阅读
添加/检索/及批量管理注释书签(PDF提取)
使用福昕阅读器的注释功能PDF注释添加/删除/管理(PDF提取)
增量提取的子注释同步回SM软件(提取同步)
因为我们的PDF内容提取是在PDF阅读器中实现的,为了能使提取的文章内容也能在SM软件中增量,我们考虑同步回SM软件中,这个操作可按自己的需求或频次来决定,不一定非得同步回SM软件.
同步回SM时唯一要注意的是最好不要覆盖掉原来同步过的子元素内容(确保保留学习进度) 具体如何避免这个问题我在文章末尾会有较详细的概述及相关方法.
用福昕来阅读只能导出文本注释
提示 : 上面导出的福昕阅读器内容注释我们处理如下,接着再导入SuperMemo进行Split分割:
自己手动处理下段落分割符号
上面的注释文本复制进SuperMemo软件,再使用Split命令分割就是按段落内容正确的分割了.
导入SuperMemo来分割处理最终处理结果(PDF提取同步)
如上所述: 整个PDF增量阅读提取流程结束.以后我们只需重复执行这些步骤来阅读即可.你可以参考我之前写的一篇内容来了解基于PDF目录索引来进行增量学习PDF文件的流程(链接如下):
主要讲解的是Mac系统下使用DevonThink及PDFExpert阅读器来实现SuperMemo增量学习的整体思路.如果你用的是Windows系统,那么你可以使用Everything/FileLocator Pro 工具代替DevonThink的功能,使用福昕阅读器或者BookxNote阅读器来代替PDFExpert的功能.
考虑到要支持注释中含有图片的问题,我推荐在Mac操作系统下使用Highlights PDF阅读器,在Windows下使用BookxNote阅读器.如果还不能满足你特定的需求,则建议用本文的PyMuPdf组件编写一些简单的Python代码,来实现更复杂及更自由化的需求.
Mac系统下的Highlights演示:
Mac下的Highlights阅读器灵活丰富的注释导出功能
由于html导出的是base64的图片存储,因此建议导出TextBundle(本地图片|markdown)格式:
导出成markdown再转成html
说明:
如上所示,如果是MAC系统使用Highlight作为PDF阅读器,导出注释时. 第一种可以直接拷贝右侧的注释内容至SuperMemo中,第二种则是导出右侧注释为TextBundle格式(图片本地存储)并把md文件转换成Html(如果直接Highlights导出为Html注释笔记,里面的图片为base64编码,不能在SuperMemo直接导入)再导入SuperMemo.
TextBundle对应为本地图片和md
## MarkDown -> Htmlpandoc text.md -o text1.html
Windows下BookxNote的演示:
下图为BookxNote阅读器的功能演示,可在Windows下作为代替Mac端Highlights软件部分功能:
Windows下BookxNote演示
步骤四: 提取内容同步回SM软件时保留学习进度
通过Python脚本来实现进度同步功能,流程上依赖于时间戳标记(不同于官方默认的按ID同步),即阅读时在PDF文件中通过文本注释的形式添加时间戳标识作为书签,阅读中断后,我们会按上面的方式导出注释,作为书签使用的这个时间戳会在导出的注释(最终的html)中存在,这个html导入SuperMemo分割时,用此时间戳作为分割符号,即生成了每个元素都有了一个时间戳作为唯一ID使用.因此在重复导入的多个不同的集合中(跨集合)我们也可以迁移旧的学习进度至新集合.
PDF阅读加时间戳作书签及分割符脚本使用脚本来实现学习进度同步功能该图所示的网盘下载地址在评论区
完结:
以上进度同步功能的具体操作,后期会考虑直接录制视频来演示.因此本文不再继续对细节进行说明. 说明: 上图的脚本下载链接详见评论区...
我是一只热爱学习的小胖子,如果你也热爱学习,并且对SuperMemo感兴趣,欢迎转发和评论!
*请认真填写需求信息,我们会在24小时内与您取得联系。