写点什么

2020Android 开发架构思考及经验总结,深入理解 kotlin 协程 pdf

用户头像
Android架构
关注
发布于: 刚刚

3、Google 搜索关键字:架构设计

搜索引擎对于我们来说是最棒的学习工具,我通过搜索架构设计等关键字,阅读了一些文章,并仔细研读 Keegan 小钢的博客文章《小钢的架构思考》系列。这几篇文章在发表之初曾阅读过,但是当时并不怎么理解,大概是对架构还没有一个大概的认识。在请教一位前辈的时候,他和说了对架构的一个理解,并再次推荐了这几篇文章。所以我再次阅读了好几遍。下图是文中关于架构设计的知识概要。

(1)知识概要

(2)个人小结

架构分为三个阶段:规划、设计、构建,每个阶段架构的设计有不同职能。在规划阶段,考虑的是产品的需求、质量的需求,技术的可行性分析以及预研。在设计阶段,考虑的如果将一个复杂的系统拆分,并设计如何进行组织这些拆分的模块。在构建阶段,考虑的就是具体的实施问题,并且要保证一定的伸缩扩展性,因为架构是不断演进的。文中引用了《软件架构设计》一书的一个模型图,我觉得有必要在此贴出来。最近也在思考软件模块化的设计,模块化的设计也许各有理解,在此先不做讨论。如下图:



这张图我起初理解不是很透彻,我曾尝试自己去画一些图来表达我的一些想法。但是,当我再次回过头看到这张图的时候,才恍然大悟。 架构的设计可以从两个维度来考虑,一是架构思维,二是架构原则。思维是我们的思考方式,是我们解决问题的方法。原则是我们思考问题的方向,是我们解决问题的一些标准。

三、架构的定义

对于架构的定义,业界都各有看法,也曾微信私信请教过一些行业内有丰富经验的前辈。《软件架构设计》一书则将架构定义总结为组成派和决策派:


  • 组成派:架构=组件+交互:软件系统的架构将系统描述为计算组件及组件之间的交互。

  • 决策派:架构=重要决策集:软件架构是在一些重要方面所作出的决策的集合。


keegan 小钢在《小钢的架构思考:什么是架构》文中提到:软件架构是规划、设计和构建软件的过程和结果。《架构之美》一书在 1.1.3 架构的含义中提出:架构说明了设计和构建一个系统所使用的结构。《Software Architecture in Practice,Second Edition》中提出:一个程序或计算机的软件架构是系统的一种结构或一组结构,它包含软件元素、这些元素的外部可见的属性,以及元素之间的关系。


本人并没有经历过大型的软件系统,做过的也只是移动端 App 的开发,所以个人也只敢从移动端 app 的架构设计出发,给出我一个狭义的理解。我认为,移动端的 app 架构是一种基于产品和技术的进行统筹管理最终所形成的共识。可能大部分的 app 开发尤其是小团队 app 的开发大多是由产品驱动的开发,需求来了,那就技术实现。需求变了,那就改。毕竟,应用层的开发是对业务负责的,必须保证正常的发布。所以大多数的情况下,程序猿不得不在产品经理面前妥协,这样对于开发人员的工作就会变的很被动。所以,就出现了程序猿和产品狗的撕逼笑谈。这种现象的原因在于项目各个相关利益人员没有对产品和技术达成共识,这正是移动端架构设计所要解决的问题。

四、产品

产品,是我们产品经理们设计的结果,也是开发人员开发的最终成果,是前后两种人群的共同目标。作为软件的架构设计者应该充分理解产品的设计理念,除了明白已经设计的功能业务,还得具有一定的预见,掌握产品的发展趋势。下面主要从开发的角度来谈一谈产品。

1、产品设计(做什么)

作为一名开发人员,我不能很专业的来谈产品的设计,但是这里我还是希望以一个开发人员的角度来讲产品设计。什么是产品设计,我觉得可以从以下几个方面来思考。

(1)、用户群体(什么人用)

顾名思义,我们设计开发出来的产品最终是让人用的。所以首先我们得定位产品的用户是谁?用户群体代表了我们产品的市场。所以产品做的好不好,最终市场说了算,用户说了算。离开了用户,不理解用户,不注重用户的体验,一切都是无用功。

(2)、核心理念(要做什么)

你知道你最每天累死累活一行一行敲出来的是什么样的产品吗?可能对于我们开发人员很容易陷入到一个版本一个版本的迭代当中,这些永无止境的工作叫人忘记了思考,忘记了问下产品人员,我们最终是要做的什么?一年后我们的产品将是个什么样的状态,我们的最终愿景是什么?我们将会怎么一步步去实现我们最终的愿景?可能你会说,这些和我开发有什么关系。接到需求,我把他开发出来实现不就 ok 了。然而,在我们的小组里不乏有这些怨言,产品人员不断的修改,我 TM 代码改过来改过去。我们的 leader 在这方面很强调我们开发人员应该拥有自己的主动权,可以反驳产品不合理的设计。但是,前提是你至少理解产品的核心理念,我们最终要做什么。

2、迭代计划(计划怎么做)

对于一个产品,用户的需求是很多的,而且随着时间不断改变的。需求可以分两种,一种是人性本能的需求,还有一种,是我们的产品催生的需求。这两种需求都是合理的,也正是我们需要满足用户的。对于各式各样的需求我们怎么有计划的去实现呢?在敏捷开发中,我们将这些需求放到一个“需求池”中,然后进行计划安排在不同版本的迭代中。这个工作,不仅是产品人员去决定,开发人员也应该起一定的决策作用。产品人员需要从产品的角度去考虑,开发人员需要从技术实现的角度去考虑,最终的计划应该是两者的共同决策。特别注意的是,根据产品的特性,技术人员也应该提出技术方面的需求。合理的迭代计划可以保证正常的开发节奏,完成迭代目标。

3、开发资源(用什么做)

(1)、开发团队配置(人)

在《人件》一书中提到了软件开发中人的因素很重要,合理配置开发团队是非常重要的。一个 App 的开发团队至少需要 5 个角色,即产品、交互、UI、软件、测试。不同角色也分不同的层次,比如软件分初级、中级、高级。不同角色、不同层次合理搭配,才能够获得更高的工作效率,保证产品开发顺利进行。

(2)、数据内容配置(物)

产品最终呈现给用户的是数据,数据分两种。一种是私有的数据,是由开发商自己生产的数据。一种是平台自生的数据,是由用户生产的。如果是自己生产的数据,就得考虑数据来源,数据的覆盖率,数据的准确性,合法性等。如果是用户生产数据,就得考虑用户生产数据的动力、入口以及数据安全性、传播性等。

(3)、开发投入预算(钱)

万事俱备,只欠东风。完成一款 app 开发,需要一支专业的开发团队,这里的人力成本也是很高的。当然我这里只谈开发的预算,至于运营就不说了。我们得考虑,开发周期多长、需要多少人、后期维护怎么办。比如一个 APP 需要 5 个人开发,2 个月的时间,开发两个版本,按照每人 1W 的工资来计算的话,也需要 10W。这估计是最低级别的算法了。所以,如果是创业公司,我们在组建开发团队的时候,也得看看预算是多少,多少钱能办多大事,当然如果是那些拿到投资无所谓的老板来时得另算了,不过今年死的太多的公司以前都是大手笔。钱烧完了,也就没有了。如果是大公司,可能不带这么抠门的。不过也应该去考虑,开发团队会消耗公司多少资源,我们能否获得相应或者更高的产出。

(4)、第三方资源

目前的开发而言,很多资源是可以寻找第三方合作的。比如,服务器、云存储、支付接口、登录接口、内容数据以及开发过程中的一些开源框架等等。我们需要选择、商务谈判直到集成到自己的 APP 中。

4、产品质量(做的怎么样)

(1)、用户体验

现在对于我们来说,用户体验是一个说烂了的词。那是因为,用户体验真的很重要,决定了一个产品的成败。产品开发完成后,最终到达用户的手中。产品好不好,用户说了算。哪些因素影响到用户体验呢?我想大概可以从 5 个角色各自的职责出发来看,产品的设计是否直达用户痛点?交互是否符合人的喜好、习惯,UI 是否让用户觉得舒适?软件的性能好不好?软件的缺陷是否多不多?

(2)、软件性能

从技术的角度来讲,我们可以通过软件的性能来分析一个软件产品的质量。今年许多的技术文章都在谈性能优化,软件的性能主要从软件的启动速度、流畅度、内存、功耗、流量、apk 体积等几个方面来评判。如果想做好一个应用,性能优化应该纳入到日常的开发中持续进行。具体如何优化,这里就不再多说了。

(3)、产品安全

产品的安全性可以从两个角度来看,产品的生产商和产品的最终用户。对于生产商而言,有许多的内容是需要受到法律保护的,有许多的敏感信息,核心技术、网络接口等是不可以泄露的。对于用户而言,我们肯定在本地或者服务器存储了大量的用户信息,比如账号密码,一些信息一旦泄露将严重伤害到用户的个人利益。所以,为了保护自己以及用户利益,我们必须要生产一个安全可靠的产品。那么对于一个应用端的开发者而言,我们的编译出的 apk 最终会到用户手中。所以,我们需要通过代码混淆、数据加密、权限限制等一些技术手段来保护我们的应用。

(4)、质量评测

一个应用做的好不好,我认为可以主要从上述用户体验、软件性能、产品安全三个维度来进行评判。那么,我们该如何组织这些评判工作呢?我们有在进行这些工作吗?就目前而言,我相信大多数的产品、开发、测试人员都或多或少的参与到这些工作当中,但是也许没有将一些数据量化、没有系统的组织这些工作。目前大部分的应用都集成了行为采集,产品的下载量、用户的活跃度等也都是体现产品用户体验的主要参数。开发团队内部一直在进行性能优化的工作,比如异常修复、bug 修复、内容泄露,过度绘制,apk 瘦身。我们也进行了代码混淆、数据加密、apk 签名加密的工作。但是,你知道你的产品质量如何吗?相比同类产品来,你哪些做的好,哪些做的不好吗?所以,我觉得将上述这些零碎的工作有系统的组织起来,将一些影响因素进行量化,让我们更加清楚的了解我们的产品质量是一件非常有意义的事情。

5、风险规避

(1)、人力变动风险

人是善变的,尤其对于 IT 来说,人员的流动就更加的频繁了,公司内部的调整,员工跳槽等等。所以,对于一直开发团队,必须要考虑到人员变动的风险。如果,某某不在了,项目是否可以正常运行。开发团队之间是否能够交叉熟悉各自之间的业务。

(2)、上层决策风险

是否经历过一个项目做到一大半业务被停掉了的情况?而这个时候,你做的是个半吊子。如果出现了这种情况,我们该怎么办?假设就在刚才你的老板说你现在的项目不做了,那么如何才能最大程度的挽回损失?如何进行项目的收尾工作?而不至于在项目又突然重启的时候接收的是一个烂摊子。

(3)、项目延期风险

我们在项目开发的时候会进行评审,然后按照迭代计划开发,但是在开发过程中一定会有许多问题影响我们的预期,比如需求变动、技术难题等等。项目延期在软件项目的开发中是普遍存在的问题,对于某些迭代而言,可能并不对整个项目造成重大影响,但是这个问题是一定需要考虑的。并且,我们应该严格的掌控项目的进度,平衡这些问题,保证能够按时交付产品。

(4)、软件缺陷风险

我们应该随时能够提供一个稳定的版本,这是我们的 leader 所要求的。软件的缺陷存在是正常的,我们不停的写 bug,也在不停的修改 bug,对于那些隐藏很深的 bug 也许没有让测试测出来,最后流通到用户的手中,这个时候我们如何完成紧急修复?如何快速响应能给到用户一个稳定可靠的版本。这些是我们需要考虑的,任何时候,都应该有 PlanB。

(5)、人为失误风险

前段时间,公司内由于操作失误,上架更新一个 apk 的时候不小心发错了机型,导致使用该机型的用户升级后程序无法使用。然后,由于这个机型缺少维护,找不到代码,仅仅只能找到一个 apk 文件,然后只能考虑反编译升级等等。我想,类似于这类的人为失误还有很多,比如代码提交错误,集成路径出错等等。人总有一不小心的时候,所以,我们在设计的时候,应该将这些因素考虑进去,如何在出现失误的时候主动警告,如何在用户错误已经发生的时候启动紧急方案,将不良影响降到最低。

6、产品交付

(1)、测试版本

在敏捷迭代开发中,我们基本上能够一周提交两个测试版本。我们开发一部分、修复一部分,都可以提交一个可测试的版本,这样可以最大程度的降低开发风险,有利于软件的稳定性。

(2)、灰度机制

如果你产品的用户量够大,这个时候发布新的版本就得慎重考虑,用户才是你的产品的检验员。目前基本都是使用灰度发布的策


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


略,先给少量的用户发布,看看用户的反馈,而后逐步发布给所有用户。

(3)、版本管理

我们在开发过程中有许多的版本,也有很多分法。如 debug 和 release 版本,有的时候还需要给内容提供测试数据的 data 版本,还有的时候上一个版本还没有正式发布我们就需要开发下一个版本的功能。我们如何去管理各个版本的代码以及如何通过版本名来区分这些版本?我们需要制定一定的管理规范,并且这一规范是否在开发团队中达成共识,就显得非常重要。

五、技术

前面啰嗦了很多,终于写到这里了。对于一个开发人员来说,怎么做才是我们的关键问题所在。只会 Android 开发,所以以下只讨论 Android。我主要从以下几个方面来谈一谈怎么做这个问题。

1、技术选型

(1)、 开发平台

移动端的开发目前主要是两大阵营 Android、IOS,其他的就不多说了。

(2)、 开发工具

  • 编译工具:Eclipse&Ant、AndroidStudio&Gradle,作为 Android 开发者,目前毫无疑问应该选择 AndroidStudio&Gradle;

  • 代码仓库:Git 、SVN ,工具有海龟、AndroidStudio 也集成了 VCS;

  • Maven 仓库:可以使用 nexus 创建自己的 maven 私服;

  • 持续集成:Jinkens、Buildbot、Travis CI、Strider、Integrity;

(3)、 开发语言

Java、Kotin、Grovvy、SQL 等等;

(4)、 开发模式

MVC、MVP、MVVM、clean 等,各有优缺点,在此不做详细说明;

(5)、 开源框架

都说了不要重复造轮子,因为你造的轮子不一定不人家的好用,对于我们开发者而言,有一件非常好的事情就是我们有太多的开源免费的第三方库供我们使用,这样给我们省去了大量的工作,做到更加高效的开发。但是,如何选择,是否引入使我们需要考虑的一个问题。下面列出一些常用的第三方库,更多请点击。


  • 网络:okhttp、 android-asyn-http、 volley、 Retrofit

  • 事件总线:otto、 EventBus

  • 依赖注入:Dagger、 RoboGuice、 ButterKnife

  • 图片:Fresco、Glide、 picasso

  • 数据库:GreenDao、 Ormlite、LitePal

  • Json 解析: Gson、JackSon、 FastJson

  • 响应式编程: RxJava、 RxAndroid

  • 异常统计平台:腾讯 Bugly、Crashlytics

  • 性能优化: blockcanary、 leakcanary

(6)、 新兴技术

软件开发而言,新技术的发展相当迅速,然而我们实际落地到项目中却需要很长的时间,因为新的技术刚出来一是需要学习成本,二是需要承担新技术不够成熟,存在缺陷带来的一些风险。当然,我们应该积极的引入好的新的东西,跟得上时代的步伐才好。下面列举的一些也许都算不上新的东西,但是也是近年来大家所追捧的新技术。


  • AndroidSupport:DataBinding、MaterialDesign 等;

  • 混合开发:ReactNative、Hybrid、Weex 等;

  • 编程语言:Java8、Kotlin;

  • 热修复:AndFix、HotFix、Tinker 等;

  • 构建:InstantRun、Freeline

2、业务拆分

我们在进行业务拆分的时候,我认为可以将业务分成三类:

(1)、常用基础业务

基础业务主要是我们的 app 的一些基础功能,像我们公司有 BFC 团队给我们开发了文件上传下载、网络请求、行为采集、账号系统等 SDK,免除我们一些重复的劳动工作。怎么去定义什么业务才是基础业务呢?我觉得可以这么去区分。如果你的业务在行业普通的应用 app 都有需要,那么这些这些就是具有普遍适用性的基础业务。我们根据不同的功能进行拆分。

(2)、通用技术业务

通用技术业务我觉得是和自己 app 相关并且有技术性很强的业务,可能是你应用的核心技术部分,比如美颜这一类软件的图片处理,小猿搜题这类的图片识别等就是一项通用技术型业务。通用技术业务的特点就是在和你同一类的 app 都会有需要使用的技术,我们可以根据不同的技术领域进行拆分。

(3)、特定功能业务

特定功能业务就是属于你自己 app 的特定功能了,一般可以按照功能进行拆分成不同模块。比如说我目前的一键搜(类似于小猿搜题)主要有搜题、查单词、翻译三大功能。那么就可以分拆为三大块。搜题要经过拍照、框题、图片处理、网络请求等步骤,每个步骤都可以看成一块小业务,以此进行拆分。特定功能业务大部分仅适用于你自身的 APP。


以上的说法仅从自身的经验出发来进行描述,在我们实际的开发中可能会有一些特殊情况,或者有不同的拆分分方法。总之,业务的拆分还需要根据实际情况来。

3、架构设计(关注点分离、抽象)

(1)、核心概念

  • 关注点分离


世上本没有架构,关注点一分离就有了架构,我们将一个软件系统的开发从多个维度将我们的工作进行拆分,对于每个领域进行设计,将各个领域有系统的组织起来,这种组织结构就是架构。然而如何将一个复杂的系统将关注点进行合理的分离,这个是非常有挑战的。


  • 抽象


抽象,这是在请教一位前辈时最后给我强调的一点。如果你对 app 是跟着交互走、一个页面一个页面写的,那么很显然,你没有对你的业务进行抽象,而只是在实现。作为 java 的设计思想也很强调抽象的概念。那抽象到底是什么呢?抽象就是你要做什么!更简单的理解就是,写 interface 而不是 class。不知道大家有没有这样的经历,在我们的 MVP 的开发当中,我们有个 Model,也有一个 IModel,但是我们写完了 Model 才知道怎么写 IModel,最后成了粘贴复制的体力劳动。如果你是这么做的,你可以自己思考下,假如我们先写是 IModel,而不是 Model,那就是怎么样的体验呢?这就是将你的业务进行抽象。在架构的设计当中,你只需要知道你要做些什么?而不需要去过多的关注你具体怎么去实现它,这才是设计。

(2)、设计思维

  • 面向过程(Procedure Oriented)


众所周知,在 C 语言的开发中,我们的逻辑大多是根据任务的流程走。这是面向过程的典型例子。面向过程关注的是工作的流程、一步一步的完成任务。


  • 面向对象(Object Oriented)


Java 语言作为面向对象开发的典型代表,这是我们所熟知的。我们将计算机按照人的思维来进行设计,每一个对象都持有自己的属性,并且持有自己的操作方法。对象之间有继承,组合等关系,通过组织这些关系来完成我们的程序。就像社会的人和物一样,人与人之间各种复杂的关系组合完成了社会各项活动的运转。


  • 面向切面 (Aspect-Oriented)


面向切面是为弥补面向对象中的一些缺陷而生的,我们将某些功能封装到一起,提供对外的接口,方便在任何地方调用。就如 SharedPreferences, Json, Xml, File, Device, System, Log, 格式转换等,这些通常会在 until 包里边。它就相当于一个横截面,我们可以随时面向这个横截面完成操作,而自己的逻辑里边不再需要重复的设计。


  • 面向服务


面向服务是将系统进行拆分,分成一个个独立的程序或组件,并对外提供某一项服务。每项服务之间通过某种协议进行通信,并进行分开部署,如 HTTP,从而达到松耦合的目的。


以上四种思维重点在于看待问题的角度不同,不同的角度解决问题的方案就不一样,当然各种角度各有优劣。那么对于在 android 开发中是否都只是按照 OOP 原则来设计呢?很显然不是。面对不同的需求,不同的场景,我们需要及时调整自己的思维,灵活运用,寻找最适合的角度,拿出最优的设计方案,这才是我们所追求的。

(3)、设计原则

  • 高内聚


怎么理解高内聚?我认为我们在拆分时某一细分领域只完成单一的功能,其内部的事情自己处理。从表面来看比如一个 model 的 class,对外提供了一个接口,那么他有一个输入,一个输出。单独看这个接口而言,它是高内聚的。当然,其内部的组织结构有可能千差万别,所以内聚的形式又各有不同。所以我们将他们分类为功能内聚、顺序内聚、时间内聚等等。


  • 低耦合


耦合指的是模块之间存在依赖关系,关系相互依赖就会相互制衡,这是必然的。所以,如果耦合度太高的话,将会导致牵一发而动全身的后果,这个使我们不想看到的,也极大的影响的程序版本的迭代以及 bug 的修复。根据依赖关系的不同,我们分为了非直接耦合、数据耦合、内容耦合、开关耦合、控制耦合、外部耦合等等。我们要完成一个系统的开发,必须要将各个模块有效的组织起来,这种组织关系便无法避免存在了耦合,我们要做的是尽量减少这些依赖关系,尤其避免交叉依赖,将耦合度降低到最低,把我们的程序设计的更加的灵活。


  • 适度设计


我们在设计的时候如果考虑不周,那么设计不够,不能满足现有或者可预知的需求,从发展的眼光来看,会导致后期的开发中出现很多的问题。如果想的太多,很容易进行过度的设计,从而将一个简单的系统设计的很复杂,那么就给当前的开发将增加了许多无意义的工作,降低了开发效率。


那么怎样的设计才是合理的设计呢?我认为能够同时满足现有的需求和可预知的需求,并且面对架构的调整时能够很方便的进行扩展。这样的设计,是非常好的设计。如何才能达到这样的效果呢?我个人觉得在对系统进行设计时,关注点分离的颗粒度需要把握好,系统不过就是将不同单一小模块进行组织而已,那么这些细小的模块就是架构设计的基础,这就好比建房的那些砖头。


这些砖头是什么呢?他么可以是是一个对外提供接口的公共方法,也可以是私有的内部方法,也可以是某某持用的成员变量。当然往大里看,他可以是某一个功能模块。在上述行业内各个 app 的架构演进中,都很强调进行模块化的改造。所以,分离好你的系统,才能够灵活的组织起来,以不变应万变。

(4)、设计方案

  • 指导模型


下图在文中已经提到,这里再次引入,因为这张图对我的启发真的很大,也表达出了我心之所想。面对一个复杂的系统,我们怎么样去分离,怎么样去组织,我认为这张图已经传达出了其中的精髓,所以我认为这是架构设计的指导模型,无论你是什么 MVC、MVP、MVVM 之类的,都可以从中去理解。



  • 模型分解


根据实践开发中个人的理解,我将此图再次进行了简化如下:



横向分块


根据上图的简化模型,我们可以这么理解,在横向我们根据业务功能进行模块划分。比如主题商店,我们可以分为壁纸、铃声等等模块,每个模块间解耦。同时,在每一层的业务间再次进行分块,比如壁纸在数据层就有图片的请求、加载、缓存、裁剪处理等等。


纵向分层


接下来我们在对每个模块的业务根据职责分为展现层、业务层、数据层。数据层主要负责数据的获取、封装等工作,业务层主要更加上层的需要调配各数据层最终将数据返回给展现层,展现层的工作就是将数据展现在 UI 界面上,并且响应人的各种指令切换 UI,操作新的数据。


接口通信


在横向来看,我们将业务进行了分块,保证块与块之间相互之间没有任何依赖,保证了绝对的解耦。从纵向来看,每个层级之间的依赖很明显是无法避免的,所以我们可以保证上层仅依赖下层的接口,从而达到降低其耦合度的目的。



如上图所示,通过上述横向分块、纵向分层、接口通信这三大步骤之后,我们可以将一个系统进行了很好的分解,并得到一个理想模型。当然,这是一个理想的模型。在我们的实际开发中可能无法避免一些交叉等特殊情况,我们还需要从实际情况出发。但是有一点,我们可以保证接口的分离,已达到更低的耦合度的目的。


统一管理


统一管理,是对于我们的设计中有一些东西是需要统一管理起来的。通过上述原则,我们将一个复杂的系统进行了拆解,已达到架构设计中将关注点分离的目的。然而在实际的开发中,我们除了要进行业务的分拆,也需要对某些业务进行统一的管理。比如说一些模式的开关管理,比如说我们在进行网络请求时需要在测试环境和正式环境之间的切换,我们可以将这些模式切换的开关放到一个地方,方便我们进行管理,而不要去到各个地方去修改。再比如说我们的请求 url 地址,是否可以写到一起进行统一的管理。还有在某些应用中会通过一个中间人来进行统一管理数据的流通、页面的跳转,这也是一个可以尝试的方案,详细请看苏宁易购移动端的架构优化实践文中提到的模块管理器、Url 跳转管理器。统一管理的意思就是将分拆的某一类小的模块某一些特性放到某一处进行统一的管理。但是这样会存在一个问题,比如前面举例说到的统一开关管理,这造成了开关耦合,如何去避免呢?我觉得可以将开关默认写到自己的模块里边,并公开出修改的接口,方便上层进行统一的修改,以达到统一管理的目的。这样的话,即使这个模块拆离出来,也不会受到影响。但是,这样的话,其安全性受到了一定的影响。架构设计总是这样,你总需要选择一个折中适合自己的方案。


我们通过上述横向分块、纵向分层的方法将一个系统切成不同的小块,这些小块负责某一单一的职责,然后通过接口将块与块之间进行了间接性的连接,依赖的是接口而不是实例,以弱化这种模块间通信造成的耦合。当然,上述模型仅仅只是一个理想状态的模型,如果是一个非常复杂的系统,那么层级之间也能拆分出更多的层级。比如,在数据层,我们在 MVP 模式的开发下使用 Model 来完成,当 Model 层的业务变得非常复杂时,有部分人会考虑拆分出 Data 层放在最底层,最为最基础的数据操作等。最后,为了方便我们对模块进行组合并进行管理,我们可以考虑在小模块中开放出接口,供上层进行统一的控制管理。最后,我想说的时,我们在进行业务分离拆解时可以考虑按照上述的方案来做,最终还得根据实际情况来进行设计。

4、开发实现

当完成我们的设计工作后,我们进入了开发编码阶段,在这个阶段主要表达我们的设计,并最终取得实实在在的成果。当进入这个阶段之前,我们的设计不能仅仅是一份文档,而应该是开发人员和架构设计者达成的某种共识。再好的设计,也需要获得良好的表达和实现。下面主要谈一谈在实现过程中需要考虑的问题。

(1)、项目分包

项目的分包结构体现一个软件的架构,我们在进行分包的时候总有一种困惑。因为我们存在多种分法,比如我们可以分为根据类的功能分为 activity、fragment、adapter、util 等,有的时候,我们又根据功能模块分,比如一键搜中有查单词模块、有搜题模块,同时又存在网络请求,软件升级等小的外围通用功能模块。存在的问题就是模块之间又存在一些可以复用的东西,那么我们进行拆分明显出现了代码的冗余。如果按照两种方案同时分,那就肯定存在了架构的混乱。我们该如何达到这两种的平衡?我认为,这个也需要更加项目的大小而来,如果是非常小的项目,也不存业务扩展的可能,我们就可以采用上述的第一种方案,简单的分类就好。但是,对于较大的项目,我建议使用第二种方案。下面,我简单列一个模型仅供参考:



  • app+main+com.jfg+common //常用基础业务+util+wedget+base+function //通用技术业务+camera+sensor+moudule //特定功能业务+mouduleA+model+presenter+view+mouduleB+model+presenter+view+mouduleC+demo //主程序+app+activity


如上所示,我们根据开始的项目业务拆分分包如上,将常用的基础业务放到 common 包里边,这个包在大多数情况是不变的,并且为 app 提供基础性的服务,不过我们尽量不要放到这个 common 包里边,如果这个 common 包变得足够大的时候,就一定要思考是不是该拆分了。因为 common 给人的感觉就是什么都是,那就让我们无法快速认知这个包所担当的职责。我们可以这样理解,common 包是面向切面而设计的一些业务,但也不是绝对的。接下来我们先聊 module 这个包,实际这里是将业务进行了模块化的分拆,如上我们拆分出了 moudleA 和 moudleB,这两者之间要求没有任何的联系。但是,我们会存在一个问题,那就是 moudleA 和 moudleB 某些业务是一样的,我们拆开显得重复了许多体力活。这应该是大多数开发者面对的困扰,这种该怎么去平衡呢?我是这么考虑的。如果,moudle 和 moudleB 存在重叠的业务,我们将这些业务提取到 function 包或者 common 包中,这样降低了业务的层级。我们允许 moudle 包的各模块业务依赖于 function 和 common 为我们提供的基础服务。为了更好的区分模块 A 和模块 B 虽然重叠但在逻辑上是各自属于各自的,我们有两种方法来做。第一种是将两种业务进行一定的抽象,实现的过程还是放到各个 moudle 业务中。第二种方案定义两个接口类,各自定义各自的接口。在具体的实现类中实现了这两个接口类的方法,内部在进行相同的逻辑操作。这样,对外看来,逻辑上 moudleA 和 moduleB 是分离的。总之,如何分包还得权衡利弊,尽量以一种思维来进行划分,以避免设计混乱。

(2)、抽象接口

如果说在架构设计中抽象很重要,你可能有些迷糊,但是如果要你先写 interface 或者 abstract class 而不是 class 时,你就可能感觉得到抽象的意义。我们将一个系统分解成几个大的模块,一个模块查分成不同的层级,每个层级再次拆分成不同的细节业务。最后,我们很清晰的知道我们要完成某一项功能需要做哪些事?对的,做哪些事就就是一个个接口,我们在编码时先写接口再写实现有利于帮助我们对业务进行拆分和抽象。我们都知道做一件事情一般情况都需要提供一些条件,做完了会有返回结果。这些都可以在接口的设计中完成。我们需要注意是一个接口只做一件事情,如果有两件事非常相似也要尽量拆分而不是合并。在接口命名方面做到见名知意,怎么去评判,就是如果你的接口没有注释也同样能让人知道你的接口是做什么的就好。

(3)、数据存储

数据存储常用的有 SQLite、SharedPreference、文件等,缓存是否也可以算是一种。这里想强调的就是要注意数据存储的规范性以及安全性,如果是数据库还有必要考虑其扩展性,如果不满足需求将会需要进行升级。

(4)、性能管理

这里源自于对性能优化的一点体会,对于服务端的开发我们很珍惜服务器资源,应该是看的见的需要银子买的。然而,对于客户端的开发我们常常忽略了这一点。虽然手机设备现在拥有大内存,但是如何写出一个优秀的程序,性能也是一个非常重要的指标。性能优化处理,那是我们在更正错误,那么之后应该是少犯错误。性能体验不够好,无非就是对机器设备的内存、CPU、GPU 资源无节制的使用,造成资源的浪费,当机器设备无法承受时就会应用就会出现卡段、死机、异常等不良反应,严重影响了应用的体验。我们要做的就是要有很强的性能管理意识,对于内存、CPU、GPU 等资源按需借用,并做到有借有还,即用完后记得释放资源。

(5)、特殊处理

我们在开发的过程中,总有那么多问题并不是按照正常思路出牌的,这些得归功于我们强大的测试团队。不同的手法,就能得到不同的结果,然后就给了我们一堆的 bug。所以,我们在软件的开发中需要特别注意一些特殊情况的处理,这些最终往往还是逻辑上的死角。以下简单总结了一些:


  • 功能冲突


功能冲突可以分为两种,一个是应用内部的功能冲突,二是应用之间的功能冲突。应用内冲突比如 A 功能和 B 功能都使用了某资源文件,如果在同时使用就会出现问题,我们通常加同步锁来防止这种冲突。应用外的冲突有很多,比如多媒体、闹钟、日历、铃声、电话等都肯能引起这些冲突,比如你正在播放一段视频,这个时候来了一个电话,那我该优先哪一个呢?还有当闹钟响起的时候,弹出一个界面是竖屏的,那么他就会强制将当前的界面变为竖屏,而如果你这个时候如果是横屏的话该怎么办呢?类似于这类还有很多,以后再细细总结。


  • 极限操作

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
2020Android开发架构思考及经验总结,深入理解kotlin协程pdf