Wrapping up

终於来到最後一篇了!不经不觉已经写了三十篇文章。我们由 Ktor client 接驳 API 一直讲到 UI,然後再做 ViewModel 的 unit testing。中间加插了时间处理、Flipper 和 proxy server 的内容。其实这些内容有部分是以前工作上跟 Android 同事定期会议分享的内容。那时已经有想法把内容放到自己的 blog 上,但最後只是放了小许。现在有这个机会就加插这些内容进去。除了用来填满三十篇之外,就是把一些不会直接在 Android 开发教学找到但又实用的东西放进去。我在八月尾才决定题目,然後开始写开首的文章,并且准备示范 project 的 code。初初写的时候以为三十篇是很多,所以开头写的内容不够充实。但到了多 code snippet 的部分就发觉光是 code 就很长,要分拆做好几篇。所以三十篇入面光是不同的 unit testing 都占了十篇。由於我是一边写文一边准备示范 project,所以内容分配是头轻尾重,尤其是後段做 UI 的部分一篇的长度比开首的文章长几倍。还有是内容可能有时会跟前一两篇重覆(好像 Dagger 某些内容有重覆提及)。本来还打算加插 Compose 的内容但发觉剩余篇数太少而且写完都不够完整,所以改做 ViewModel 测试作罢。

现在回顾那个示范 project 有个地方做错的是改变排序不应该触发 API call。要做到这个效果的话可以把排序的动作搬到 ViewModel 做,或者是 use case 是有自己的状态(即是会保留之前取得的 response)。前者的话就令到 domain layer 是多余;後者的话我又觉得班次这件事不会有其他东西触发它改变,把它的状态保留又有点怪。如果我们做的功能是类似 Facebook news feed,这样 domain layer 保留一份资料就很合理。因为可以在 domain layer 整合 push notification,当有 push notification 的时候就用 push payload 更新本地现有的 news feed,然後把 news feed 外露成 Flow。这样 ViewModel 就不用知道有甚麽情况那个 news feed 会被更新,只需要订阅 news feed 就可以了。

如果把排序的 logic 放到 ViewModel,这样 use case 就只是左手交右手(把 data layer 的东西转交予 presentation layer)。但我好难找到一个有充实 business logic 而且做完之後又可以抄到我自己的 side project 用的情景(因为选即时班次是因为我的 side project MetroRide app 要做这个功能)。其实不是没有,只是 business logic 太复杂,讲解它都用不少篇幅,变相整个系列不是在示范 Android 的东西。如果个别 use case 没有甚麽特别的 logic 的话,为了令整个 app 的做法统一还是建议写 use case

另外一个做得不好的地方是倒数分钟只是靠每十秒 call API 後更新而不是按实际时间更新。解决方法可以是再另加一个每秒触发的 Flow 来重新计算倒数分钟值,但因为我不想弄得太复杂而没有做到(因为写完又要示范写 test case,本身每十秒自动更新都要写一大堆 test case)。

如果要谈完成铁人赛有甚麽东西学到的话,我想主要是试用了一次 Ktor client、Kotlin Coroutine scope 和 Flow 测试快进时间。以前做的项目都是用 OkHttp 加 Retrofit,如果是要测试快进时间的话都是用 RxJava。Kotlin Coroutine 和 Flow 感觉上比 RxJava 易学,最起码没有 subscribeOnobserveOn 傻傻分不清楚的问题,而且 Coroutine 的 suspending function 可以令整个写法看起来像平常写 code 的形式,不用费神想如何把它们串联成一条 RxJava 的 Single/Completable 之类。加上现在 Android Jetpack 都有足够的配套,像是 lifecycle 的 scope、data binding 支援等等。用过之後就回不了 LiveData 和 RxJava 去。

整个系列有三分之一是做测试,这是我特别花篇幅做的。因为身边不少同事都不太有写自动化测试的经验,亦不明白为甚麽要用 dependency injection library。我觉得没有写过自动化测试的人是很难明白为甚麽要弄一大堆 interface,又要用 constructor injection。因为这些东西本来就可以用 singleton 就能做到。但如果你了解到写测试时需要 everything's under control 的话就自然明白到为甚麽要搞这麽多东西,为的就是能在测试时控制到自己想要的情景。还有是拣选 architecture 时可以用能否容易写 unit test 做参考准则,如果发觉很难写或者写得很古怪的话那个 architecture 应该都有点问题。现在有了 Dagger Hilt,Android Jetpack 又有配套,就算不完全明白全部 Dagger 的东西也可以轻松做到 dependency injection。

最後,感谢各位花时间阅读我的文章。如果想看其他的东西可以到我的 blog,如果有用 Medium 的话可以 follow 我的 Medium


顺带一提,有一个有趣的东西是我在做示范 app 的时候无意中发现港铁的抵站时间 API 居然会时光倒流。最初看到 UI 还以为我计算倒数出错所以显示不到倒数,但检查 API response 才发现他们 backend 的班次不会把日期进位,一直停留在同一日,直至全部班次都是翌日才会输出正确的日期。我想他们应该是把日期和时间分开处理,之後临到输出 response 时才把它们合并一起。

https://ithelp.ithome.com.tw/upload/images/20211015/20139666W4cOI3C2kG.png


<<:  Day 30 - 每日产生观察名单

>>:  Day 30 结语

Day17 NodeJS-Express II

今天要针对Routes和Middleware的部份进一步了解Express框架。 Express中的...

Day14 NiFi - NiFi Expression Language

今天要来介绍的是 NiFi Expression Language (以下简称NEL)。在前一篇我们...

Day27 - GitLab CI 如何让工作流程流水线跑快一点?之一 从 .gitlab-ci.yml 大部分解

在专案过程中,透过 GitLab CI 建立流水线,让研发过程中如编译、测试、打包、部署等工作都得以...

【领域展开 08 式】 WordPress 布景主题选择 (彷佛发现无底洞

有时寂寞太沉重,身边彷佛只是观众 Study 後必须套用一下 Tanya 无底洞的歌词,选完使用 W...

【Day13】数据展示元件 - Accordion/Collapse 摺叠面板

元件介绍 Accordion 是一个可折叠/展开内容区域的元件。主要是针对显示内容复杂或很多的页面进...