写点什么

十六年全栈开发者的 Android 开发踩坑实录

  • 2021-03-31
  • 本文字数:3234 字

    阅读完需:约 11 分钟

十六年全栈开发者的Android开发踩坑实录

这是一个完完全全马后炮的故事。身为拥有差不多十六年开发经验的全栈 web 开发者,作者对构建 web 应用所需要的各种技术可谓是了如指掌。而在最近几年的工作项目中,作者第一次成为了一名安卓开发者。在经过一段时间的磨合之后,作者才意识到,从 web 开发转型到安卓、移动端应用开发,开发者的思维也需要一定转换。


安卓开发的萌新们走错的路大多数都可以在项目后期通过重构或修改构建流程解决,不断打磨直到单元测试完美覆盖需要的所有情况也能处理一些小错误。但剩下的漏网之鱼就不是那么好解决了,这些足以在 app 的生命历程中造成持久影响、令人想要将整个项目推翻重来的错误中,有些甚至让作者羞于启齿自己曾经犯过它们。以下将提供一些防止你想要穿越回过去重做项目导致时间悖论(笑)的小 tips,希望能够帮助大家预防那些难以摆脱的糟糕麻烦。

添加应用内更新


立刻、马上。一直到出炉一年后,我们才把更新通知功能塞进我们的 app 里。内置的更新提醒功能在项目初始就添加的话,那么过程就还算简单,但如果拖到后期才做的话,难免会造成不少的问题,其中包括:必须手动搭建自定义流程,以及用户自行尝试跳过更新。


// Creates instance of the manager.val appUpdateManager = AppUpdateManagerFactory.create(context)// Returns an intent object that you use to check for an update.val appUpdateInfoTask = appUpdateManager.appUpdateInfo// Checks that the platform will allow the specified type of update.appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->    if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE        // For a flexible update, use AppUpdateType.FLEXIBLE        && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)    ) {        // Request the update.        appUpdateManager.startUpdateFlowForResult(        // Pass the intent that is returned by 'getAppUpdateInfo()'.        appUpdateInfo,        // Or 'AppUpdateType.FLEXIBLE' for flexible updates.        AppUpdateType.IMMEDIATE,        // The current activity making the update request.        this,        // Include a request code to later monitor this update request.        MY_REQUEST_CODE)        }}
复制代码


相信我,这项功能将会是你的 app 的突破式改变。app 的现有用户可能已经通过其他的 app 习惯了应用内更新功能,甚至会理所应当地认为这其实应该是移动端平台的一项特点之一。但实际上,直到我亲身经历了安卓开发,才知道原来这项功能还要开发者手动添加。当你的 app 不幸停止运行之后,用户并不会去找软件更新包,他们只会卸载再安装,甚至更糟的是,他们会在应用商城留下评论。

限制 API 密钥


先让程序跑起来,出了问题再去打补丁。或许你也有这个习惯,但请不要继续拖延了。指路一篇关于谷歌云平台上 API 密钥 的文章,但对于其他平台,这一点同样适用。


对于 GCP(谷歌云平台)来说,我们只需要在登录谷歌账号,选择要设置限制的 API 密钥后,系统便会跳转到密钥的属性界面。在“应用限制”里选择安卓应用,点击“+”添加软件包名称到需要添加限制的 API 密钥下即可。至于添加证书指纹,可以直接复制页面中的命令后,按照网页右侧的指示,只需要几分钟就可以完成。


我们在 app 出厂两年后才开始限制 API 密钥。然而在限制之后,app 的一个地图功能罢工了。回滚更改之后,我们费了好大一番功夫才找到问题所在。app 所使用的大部分谷歌官方软件包都可以完美适配限制 API 密钥后的代码,唯独其中一张地图需要重写另一套 API 调用代码。如果在项目初始我们能考虑到 API 密钥的限制问题,并将其写入源码,这无疑会增加开发时间,但到了后期我们就可以不用再担心限制的问题了。


故事并没有在这里结束。为了能在保证地图的正常运行并限制 API 密钥,我们不得不进行强制更新。我们有后台的统计数据可以监控用户的更新流程,而数据表明,有 90% 的用户在收到更新通知的几周后才进行更新,而另外 10% 的用户则在地图几乎彻底罢工的情况下依旧选择不更新,完全不晓得他们是怎么忍受这种 bug 的。

内部 API 版本控制


当我还在主攻 web 开发时,我一直都搞不太明白为什么有人会想这么做。在更新前端代码后,为什么还要留着旧版本的 API?怎么想都是无用的浪费。


但用户使用的软件版本不同时,API 的大更新可能会导致软件大范围的崩溃。应用内更新的方法可以帮忙缓解这种问题,但过程将会无比漫长。划分 API 版本更像是一种针对这类软件崩溃的,快捷简单的解决方案,而非是我曾经以为的过度工程。

万事先离线


我们的 app 是有实用目标的。当我们收到用户反馈的 app 反应卡顿、响应超时时,我还只是移动端应用开发的小白,刚刚接触到一个新的名词:优先离线(Offline First)。如果用户联网失败,所有未上传、未保存的东西都会丢失,等到连接恢复,他们将不得不重新输入所有的内容。


优先离线的结构会将更改内容写入本地数据库,等有网络连接时再进行同步。这样一来,用户得以在离线下使用 app,联网时响应也会更快,用户不用再干巴巴地等着服务器传回响应才能进行下一步操作。



离线优先的功能在项目后期可能会更难实现,难易度取决于 app 的数据的复杂程度。所以还请尽快决定 app 是否需要它。我们至今还在研究要如何在我们的“高龄”app 中更好地实现这项功能…

谨慎选择导航项


如果你的安卓 app 结构复杂、有很多界面的话,开发进程到后期再去修改导航项麻烦程度将超乎你的想象。我们的 app 在后期是直接改为了底部导航的形式。


在一些情境下,安卓开发中的 Activity 可以被看做是 app 中某块屏幕的代码;安卓 3.0 才有的 Fragments 则可以被理解子视图代码或是 app 中的部分代码。二者的 layout 都是通过 XML 定义的。


我们的导航指向的是 app 不同区域中的主要功能,这些导航小卡片又各自导向不同的子功能,一共连接起了三十余个 Activity。这些也不过是这款基于 Activity 的 app 中的四个 fragment。导航抽屉则是另一种常见导航形式,主要服务对象是 Activity 对接 Activity 形式的导航需求。


底部导航因为 app 的底边栏一直都是可见状态,所以它的设计对象是 fragment 式导航。在将底边栏添加到 Activity 后,接下来我们只需要它相关的代码敲进该 Activity,并把它的 view 添加到 Activity 的 layout 中。这样,通过点击底边栏的按钮,我们就可以把 fragment 加载到 Activity 中了。


所以,为了在 app 中添加底部导航栏,我试图将 Activity 转换为 fragment。结果很悲惨,过量的 bug 直接导致软件崩溃,浪费了我一个月的时间。如果我们只有五六个 Activity,那么解决起来可能还不算太难,但事实上我们的 app 足足有三十多个 Activity!


这直接导致了我在这一个月了放弃了其他工作,专注为每一个 Activity 添加导航功能。我还尝试过创建一个 helper 函数,但这并不能帮我省多少麻烦,到头来还是要一个个地为 Activity 写代码。同时,我还需要把底边栏添加到所有的 layout 中,并且在已有的 layout 中为这个小家伙腾地方。再加上还要对 Activity 栈进行编程操作,防止出现竞赛条件。虽然过程繁琐,但最后好歹还是成功了,并且效果还不错。只不过如果在项目最初我就能把底部导航栏加上去,并且从基于 fragment 的方向开始设计,那么将轻松很多。


这只是份不完全清单……


当然,在开始你的第一份安卓应用时,还有很多其他的事情需要考虑的,比如添加单元测试、确定一个 app 的模式后不要更改等等。但如果你之前有接触过其他类型的开发模式,这些应该都不陌生。或许你并不会遇到与文中提到的一模一样的问题,但恐怕不会相差太多。希望这些小 tip 能够帮你意识到安卓开发与其他的类型的开发是不甚相同的,这些开发决定的影响或许能持续相当长一段时间。


原文链接:


https://triplebyte.com/blog/everything-id-do-differently-if-i-could-go-back-and-rewrite-my-android-app-today

2021-03-31 15:132660

评论

发布
暂无评论
发现更多内容

人工智能 | 打造领域专属的大语言模型

测吧(北京)科技有限公司

测试

数据工程(四)数据架构设计:连接数据与战略,驱动业务增长

数造万象

数据架构 数字化 数据工程

2024巴黎奥运会:中国战绩报告分析

搞大屏的小北

数据分析 巴黎奥运会 中国队 金牌 奖牌

程序员喜欢的7个免费公共API

幂简集成

API 免费API

Java Agent 开发初探

FunTester

蜗牛游戏宣布2024年第二季度财报业绩

财见

智源研究院举办第一期数据与行业应用Workshop

智源研究院

斥巨资给自己买了个礼物,程序员专用显示器真香

王中阳Go

显示器 #程序员

水底下的云

脑极体

云计算

如何将文本转换为向量?(方法三)

DashVector

数据库 向量检索 大模型

数据资产入表:解锁企业价值新蓝海

郑州埃文科技

数据治理 数据要素 数据资产入表

云计算优化震撼828,华为云Flexus X实例邀请您分享数字化转型红利

YG科技

EZ先享官奔赴海外 见证马自达百年造车基因传承

极客天地

业务流程的数字化转型:观测云的实践与挑战

可观测技术

业务监控

1688商品详情API返回值中的供应商信息

技术冰糖葫芦

API Explorer API 接口 API 测试 API】

百度冯景辉:从数据清洗到安全围栏,深度解析大模型原生安全构建

百度安全

静态IP和动态IP哪个好?怎么选择?

Ogcloud

IP 静态IP 动态IP 海外原生IP 海外IP

企业跨国组网如何搭建?试试SD-WAN!

Ogcloud

SD-WAN 企业组网 SD-WAN组网 跨国组网

如何将文本转换为向量?(方法二)

DashVector

人工智能 数据库 大模型 向量检索服务

检索增强生成 (RAG),AI届的新星“厨师”

澳鹏Appen

rag 检索增强生成

大数据时代来袭,那么工程领域的数据科学如何成为行业的新超级英雄呢

Altair RapidMiner

人工智能 设计 仿真 altair

云服务应用就在828,精准优化企业管理,华为云Flexus X实例有你好看!

YG科技

跨部门协作:观测云在促进业务与技术团队合作中的作用

可观测技术

团队协作

五大联赛在即,能否用贝叶斯来预测足球比赛

Geek_a17c4b

AI 数据集 足球 贝叶斯算法

【AI 生图赢奖】用函数计算绘出「少年江湖」,与热播网剧梦幻联动

阿里巴巴云原生

阿里云 云原生 通义灵码

十六年全栈开发者的Android开发踩坑实录_语言 & 开发_Stephan Miller_InfoQ精选文章