学线移动的两年:安卓从小白到进阶
学线移动的两年:安卓从小白到进阶
前言的前言
从2020入学时的小白,到如今即将卸任学生在线移动开发部总监,不知不觉之间安卓已经陪伴了我接近两年的时光。两年以来有过学习的汗水、前进方向的迷茫,也有过将想法变成现实的喜悦,我很庆幸过去的两年时光之中自己能够按照自己的想法去生活,既不被焦虑裹挟而卷,也没有因佛系而摆烂。
从去年十月以来,因为学线的纳新培训,我写了很多关于安卓开发的博客,当然这其中也参考了网上很多大佬的教程,但一直没有太逻辑性的总结,所以如果你一点点翻看我的博客会感觉特别凌乱,在此我对过往两年中的路程进行梳理,写下我的学习路程与收获。
前言
如何快速上手一门技术?我曾与很多大佬讨论这个问题,得到的答案也五花八门,我曾尝试过很多道路,也失败过很多次(绝对不是因为喜欢考试前一天晚上才开始预习),或许每个人都有每个人的方法,我只能说我的方法可能更适合我。
回归正题,如何快速上手一门技术?至少现在看来,我勉强算是找到了属于自己的一种方法。其实我们最大的痛点是什么?是我在啥也不会的时候翻完一遍书,以为自己都学会了,但是打开IDE却不知道该如何下手。我们该如何改变这一局面?方法五花八门,真的,每个人都有自己的心得。对于我而言,我更倾向于在学完最基本的知识之后去github
上找几个小项目或者比较完善的demo
,自己跑一跑,看看能不能理解他们的源码,这期间肯定会有看不懂的地方,也正是我进阶的好时机。
当然,这也会带来一些问题。并不是所有的demo
都是好的样例,也不可能是完善的demo
,这就会导致我自己的技术栈不可避免地受这些问题影响而留下暗伤,我深知这一点。
安卓
入门之路
对于安卓原生的入门,我认为至少需要掌握这几部分:
首先,最基础的,我们需要学会最基础的一些布局,这与web开发还是有很大区别,安卓会对布局有更多的支持,比如线性布局,相对布局,约束布局等等,并且至少要对线性布局有灵活的使用。一般来说,几乎大部分的界面都可以选用线性布局绘制。此外是安卓的常用组件,从按钮、到输入框、选择框、图片与视频、到表单、到高性能表单甚至瀑布流等等,有很多组件是我们常用的,我们可以选择记住,不过更多的可能是现用现查。
其次,掌握移动端的数据长期存储方式(数据库)。
- 最简单的,我们可以选择
SharedPreference
,这是一种基于文件系统的键值对存储方式,优点就是简单易用,缺点就是进程间不安全,而且没有任何加密可言。
- 最简单的,我们可以选择
- 除此之外我们可以选择
LitePal
,这是由郭霖大佬开发的数据库,是最简单的封装过的数据库,网上教程同样特别多(甚至你可以直接去翻郭霖写的文档),地址。- 再者,我们可以选用
Sqlite
,这是目前来说应用最为广泛的数据库,轻量化、高可用性,也是我最推荐的数据库选择。 - 最后,谷歌官方给出了最新的数据持久保存方案:
Room
,其在SQLite
的基础上提供了一个抽象层,让用户能够在充分利用SQLite
的强大功能的同时,获享更强健的数据库访问机制。作为谷歌官方强烈推荐的存储方案,Room
有更好的性能与更简化的数据库方案,链接。 - 这四种方案,由易到难,但还是更推荐
SQLite
。 - 经过这一部分,我们的应用至少有了本地的持久化存储,可以进行一些最基本的单机服务。
- 再者,我们可以选用
- 最后,了解一下网络。我们在移动端通常使用
okhttp
执行网络请求(当然也可以用HttpURLConnection
),当然在此之前需要保证我们对网络有足够的认识,同时对JSON
、类等概念有足够的了解。这一部分其实很简单,我在学线纳新培训时讲过,博客链接。- 经过这一部分,我们的APP就已经算是符合了主流APP最基本的标准。
- 传感器。这个有大量案例与现成API,调用就好,并无太大难度。
非安卓方面的知识
最基本的,我们在入门时至少要尽量保证代码有更高的可维护性、可阅读行,尽量保证高内聚低耦合,尽量保证更清晰的逻辑结构,那么如何去保证这几点呢?我认为首先是代码的规范性问题,一份规范的代码是程序员最基本的素养,也是可维护性的必要保证。其次是好的架构,从MVC
到MVP
到MVVM
,更好的架构往往意味着更高的可维护性,但架构还是事在人为,考验的更多的是我们的开发经验。我对这两部分也有过讲解,博客链接。当然,如果可以,我们也要尽量利用设计模式的一些基本原则去设计我们的代码,不过还是那句话,活学活用,博客连接。
写在最后
对于第一点,我个人认为并不难,难的是我们对界面的建立有足够清晰的思路。
对于第二点,我当初在这一部分踩过很多坑,但我觉得每一次踩坑都是我们成长、收获知识的机会,至少在我现在看来是如此。尤其对于没有任何编程经验的小白来讲,这一部分将是一道坎,会有各种各样的报错摆在我们面前使得我们束手无策,但我们必须学会解决问题,相对来说Sqlite
的社区建设更加完善,几乎所有问题都可以找到答案,这也是我更推荐的理由之一。
对于第三点,对于没有任何编程经验的小白来讲,这一部分将是更深的一道坎。虽然从我们现在来看,网络的种种似乎都是理所当然,但一切都不是那么美好。其实对于网络的部分,首先是对B/S
或者C/S
等等模型的了解,其次,我们应该认识到,网络是不可靠的、不能保证及时响应的,基于这最基本的两点,我们会对网络有更深刻的认识。
进阶
网络进阶。网络是不可靠的、不能保证及时响应的,不可靠我们可以通过异常的捕获等等手段解决,但我们如何处理响应的延时?这时就需要我们建立同步/异步模型,同时也考验我们多线程的理解。
- 主要困难还是在于,如何理解异步?说白了,在发现这时一个异步操作时,我们往往会继续执行其余的代码部分,等异步操作收到返回的结果时调用回调函数进行处理。博客链接
组件进阶:自定义组件。有很多时候,很多现有组件并不能满足我们的需求,对于这个问题我们可以使用自定义组件,这在很大程度上提高了我们APP的灵活性。自定义组件最大的困难在于理解安卓对于自定义组件的处理方式以及调用方式,与界面相同,组件也会有自己的布局逻辑,也会有自己的逻辑控制(.java/.kt),我们往往可以简单的记住整个流程,不过其实组件的自定义更深层次的理解会涉及到安卓更加底层的原理,后面会提到。
绘制原理与动画。对于这两方面,个人认为其实并不难,而且也很少有刚需。(
少了自定义绘制与好看的动画又不是不能活)当然,如果真的有兴趣的话可以去了解。- 对于绘制原理,可以帮助我们在没有好用的组件的时候自定义组件,这里的自定义并不是指通过现有组件的拼接,而是纯粹的自己画,然后加上事件的监听与处理就好。我曾有一段时间痴迷于绘制,到最后发现真正的用处并不多,但如果真的用到的时候,不会这些知识就很难受。博客链接
- 对于动画,说实话,就是数学计算,最重要的是我们要明确在哪两个状态之间执行动画,这个动画是以什么样的规律去执行的。
从
Activity
到Fragment
。如果对于每个界面都维护一个Activity
,那显然会带来不必要的性能损耗。相对于Activity
,Fragment
有更灵活的生命周期(多),可以作为可重用的部分,有更高的性能(更轻量),更好的屏幕适配性,更灵活的组建方式(你可以用Fragment
构建Fragment
)。链接架构进阶。前面曾提到过MVC,但其缺点较多,并不适用于大型项目。谷歌更加推荐MVP与MVVM,后者会更优。对于架构,其实我们可以发现,我们很难改变
Model
与View
,但这两者之间如何进行交互其实是区分不同架构方式的关键部分,或者说是不同架构着眼的关键之处,如果我们能对此有更深的了解与实践,就能获得更丰富的开发经验。当然,拘泥于某一架构是不必要的,我们所要做的是活学活用。博客链接基于
View
的体系。这一部分主要分为两方面,一部分是View
的事件体系及其消息传递机制,另一方面是其绘制原理,这方面又会牵涉到自定义组件的一些知识。- 对于事件,我们有点击、长按、拖拽、双击等等事件,对于组件尤其是自定义组件,需要处理那些事件,事件由谁监听、处理,如果出现冲突(尤其是滑动冲突)怎么办,这其实都是我们要考虑的一些问题。
- 对于组件的绘制流程,我个人认为是安卓原理中最难以掌握的一部分,我们的布局是如何呈现在界面上的,这背后涉及到安卓的绘制与一些渲染机制,难度非常高。
- 当然,除了这些,我们也可以考虑桌面小组件,其开发是简单的、模式化的。
- 最少要有下面的结构认识。
安卓四大组件:
activity、service、content provider、broadcast receiver
,第一个我们已经耳熟能详,但剩下的三个好像从没见过。其实对于我们开发来说也并不会经常用到。service
用于在后台完成用户指定的操作。Content Provider
使一个应用程序的指定数据集提供给其他应用程序。broadcast receiver
。。。。。就是广播,字面意思理解。java高级。这部分包括但不限于:JVM、GC与内存调度(后者偏向安卓)、高并发、高性能IO、动态代理等等。这将会成为安卓调优与高性能的必备。
SDK、NDK开发。相对来说,后者可能热度更高,因为近几年各类音视频及游戏软件大火,对于NDK开发的需求也水涨船高,而且相对于OpenCV for Android的臃肿,NDK开发就成了不二之选。网上的NDK介绍。
动态编译。这位更是重量级,大佬的博客。
平台架构。这个应该是安卓中最难的一环了,但也是走向大佬的必经之路(反正我不是),我对此知之甚少,而且其实网上的教程也大多雷同,或许未来会继续探索吧。
进阶推荐
《Android开发艺术探索》
《Android进阶指北》
《安卓进阶之光》
写在最后
安卓进阶其实永无止境,我所能写出来的其实也只是其中一部分,谷歌近几年大力强推kotlin
、Jetpack Compose
等等,给这一体系带来了更多的生机(与负担),如果真的有可能,我们可以试图了解,链接。此外,我并不觉得我对于安卓有多么高的水平,尤其是进阶的部分,愧对精通二字,但我一直在路上。
Flutter
两年时间中有很大一部分我都在做Flutter
,它也帮助我解决了很多课设,也会成为我技术栈的一部分。
这两年 Flutter 技术热度持续提高,整个 Flutter 生态和社区也发生了翻天覆地的变化,主要体现在:
- Flutter 稳定版发布到了3.0,现在已经支持移动端、Web端和PC端,通过Flutter 开发的应用程序能够轻松的在各个平台迁移并获得很好的性能。
- Flutter 在 Github Star 数上排名已经进入了前20,在跨端框架中已经成为稳稳的第一。
- 全球很多公司都已经在商业项目中使用 Flutter,比如Google、微软、阿里、字节、百度、京东等,已经有很多成功案例。
- Flutter 第三方库数量持续保持高速增长,有越来越多的人为Flutter生态贡献代码。Flutter 相关的教程、书籍数量也在高速增长。
综上,可以看见Flutter 技术从第一个测试版发布到现在短短 3 年多时间获得了巨大的成功。而之所以能获得成功的主要原因是:Flutter 既能保持很高的开发效率的又能获得丝滑的性能。根据近几年实践统计,Flutter 相比原生开发,人效能提高近一倍,而性能可以接近原生。
入门之路
组件与布局。与安卓相似,我们首先需要熟悉组件,但我们很少着重提及布局。或者说,这里使用“组件”二字是不太准确的,
Flutter
中一切皆Widget
。其布局逻辑也会由Widget
逻辑体现,我们可以使用Row+Column
实现线性布局,也可以使用Stack
实现层叠布局,或者Position
实现相对布局,等等。总而言之,这种统一给了布局上更高的灵活性,同时也更加考验开发者的布局逻辑思维与代码功底。(否则你写的代码真的就是一坨shit)路由管理。Flutter对于路由有更着重的强调,一方面是路由方式多种多样,另一方面是路由逻辑的正确性。有时候我们在进入下一个界面时需要将前面的界面销毁,甚至是前面的所有界面,这保证了我们APP界面跳转不会出现逻辑问题(原生也是如此)。另一方面,我们要跳向何方,携带什么参数,等等,也还是一个问题。
- 对于以上两方面,我们更多的是与安卓进行类比,而不是从头再来。我们应该认识到Flutter与安卓原生的区别,尤其是在语法上,但这并不妨碍我们对其原理与逻辑进行抽象的理解,先理解,后应用,边学边用往往可以事半功倍。
- 以上两方面,我的博客讲解链接
网络、同步与异步。这一部分跟原生相比不能说是大致相同,只能说是一模一样。不过细微的区别在于,Flutter引入了专门的异步函数与更加详细的异步机制,但思想是不变的。
写在最后
这部分实在少的可怜,因为Flutter本身就是一个可以快速上手的框架,对比而言你会发现Flutter并没有数据持久层,这当然是一个问题,但Flutter的优势在于你可以方便地使用各种第三方库并进行管理(如sqflite
),相对于原生会更便捷。
Flutter有太多强大的第三方库,很多其实都是对多端库的封装。比如我使用过qrcode
,其安卓端就是基于zxing
。当然还有很多非常强大的库是原创的,比如状态管理的GetX
、Provider
,图片加载flutter_svg
等等。
当然如果你想找到更全的Flutter样例,我会推荐这个仓库,会节省一些不必要的入门时间
进阶
JNI
、ffi
与PlatformChannel
。有些功能可能是安卓才能实现的,我们就需要在Flutter中调用安卓原生的一些方法,最简单的,我们可以直接调用PlatformChannel
,但如果对平台有特定的需求,比如,我想与安卓通信,那么JNI
会是一个好的选择,如果是与windows交互,ffi
会有更高的效率,至少避免了数据传递时的复制。但其实,PlatformChannel
就已经很方便(不太追求性能的话)。博客讲解高级组件与自定义组件及其事件。高级组件包括但不限于滚动类如
PageView
、各种ScrollView
等,其最主要的事件关键点就在于滑动,开发过程中经常要处理其滑动冲突问题,除此之外还有各种功能类组件等。自定义组件也有两种思路,一种是对现有组件进行建造、封装,另一种是自绘,自定义组件。Flutter中也独立了事件处理机制,同时也会有事件分发问题。事件处理动画。同样地,动画作为APP非刚需需求,
Flutter
动画有专门的类进行封装,对于动画控制有更清晰的逻辑,只要能控制好Controller
并确定动画过渡逻辑就能解决大部分问题(个人观点)。状态管理与事件循环机制。安卓当然没有太强调组件或者布局的状态,但也会有类似于状态管理的界面更新机制,无论是
runOnUIThread
还是Handler
,但个人认为这与Flutter的定位还是有一定区别,安卓来讲会更侧重与事件的处理,我们甚至可以把一些非UI改动交给它们去执行,对于Flutter,则会更加强调组件状态,这与其对Widget
的定位不无关系,但究其本质也不过是EventLoop
(尤其是异步组件更新),只不过更加突出了其组件的地位。- 简单的状态管理你可以使用
GetX
来协助,其最大优点是部分更新组件树以提高重绘渲染性能,但如果我们能力足够,完全可以自己实现这样的功能。(但是既然有轮子为什么还要重复造呢)
- 简单的状态管理你可以使用
高级关键字。这里主要指
key
与context
,在原生中也有类似的context
,但一般也不太会过分强调(对于入门级以及中级开发),但对于Flutter,这两个关键字是高频出现但却好像深藏功与名,只要不乱改,程序就不会崩的那种,但如果我们涉及到Flutter组件绘制原理等方面又难免会涉及。总的来说,这两个关键字并不是特别容易理解,尤其是对于没有多少经验的开发者来说可能更是云里雾里,但这两个关键字在特定的小场景下会有最为便捷高效的功能,如动态组件位置变更等等。- 我对这两个关键字也有过讲解,但在开发过程中,更多的还是作为后手留作备用,很少会去主动使用。
- 我的博客:flutter-key,我的博客:flutter-context
库开发。我们常常使用很多好用的第三方库,有时候也会遇到一些问题并没有太理想的第三方库可用,与其坐以待毙为什么不自己动手写一个呢?
Flutter Engine
与Flutter基本原理。Dart 运行时和编译器支持 Flutter 的两个关键特性的组合:- 基于 JIT 的快速开发周期:Flutter 在开发阶段采用,采用 JIT 模式(即时编译),这样就避免了每次改动都要进行编译,极大的节省了开发时间;
- 基于 AOT 的发布包: Flutter 在发布时可以通过 AOT 生成高效的机器码以保证应用性能。而 JavaScript 则不具有这个能力。
AOT 程序的典型代表是用 C/C++ 开发的应用,它们必须在执行前编译成机器码;而JIT的代表则非常多,如JavaScript、python等,事实上,所有脚本语言都支持 JIT 模式。但需要注意的是 JIT 和 AOT 指的是程序运行方式,和编程语言并非强关联的,有些语言既可以以 JIT 方式运行也可以以 AOT 方式运行,如Python,它可以在第一次执行时编译成中间字节码,然后在之后执行时再将字节码实施转为机器码执行。也许有人会说,中间字节码并非机器码,在程序执行时仍然需要动态将字节码转为机器码,这不应该是 JIT 吗 ? 是这样,但通常我们区分是否为AOT 的标准就是看代码在执行之前是否需要编译,只要需要编译,无论其编译产物是字节码还是机器码,都属于AOT。
此外,如果你对UI渲染感兴趣,可以尝试了解Flutter渲染机制与渲染管线,我相信在此可以收获很多高性能渲染的心得。
最后,也是最为重量级的,Flutter之所以高效,与其架构模式不无关系,可能所有技术栈的最终奥义都是理解其架构与源码吧。
写在最后
Flutter相对于安卓来说要更加简单,整个过程中比较大的困难可能就是适应Dart的语法习惯与界面构建以及状态管理,其余的知识点其实都可以跟安卓类比去学,而且总的来说,Flutter知识点相对更少,入门也就更快。
但回归本质,如果我们想学好Flutter,安卓的基础(或者说原生移动开发基础)是必不可少的。同时,Flutter也并非万能,虽然能够几乎覆盖全平台,但并不意味着平台原生就会因此不必要。
当然,Flutter还有好多高级特性是我很少触及的,未来或许会有更加深入的认识。
结语
想不到能敲这么多字,林林总总大概敲了五个多小时。两年前的懵懂如今历历在目,两年后的我也算不上什么技术精通,可能说两年前96个志愿中来到了软院是个意外,但我还是尽我所能做了自己喜欢做的事,这其中有得有失,但我不希望自己患得患失。