在实际项目开发中,我们曾无数次地吐槽刚接手的app太乱没法维护,心里曾有无数只草泥马走过。作为高素质时刻为他人着想的高端人才,我们当然要为接手者铺好路,毕竟大家都不容易啊,程序员何苦难为程序员?所以我们一定要注重app架构的优化,而且是在app刚开发时就应该考虑到架构的设计,那么应该如何去设计一个app的架构呢?作者给大家简单分享一下自己的心得,以及一些经验tips,希望能对大家有所帮助。


一、满足solid原则

在我们面向对象的开发者看来,外物皆对象。其实一个app也不例外,这里面有很多的共通性,我们也可以把它当做一个大的对象。那么我们在设计一个app架构的时候就应该考虑到面向对象的五大solid原则,它们分别是:单一职责原则、开放封闭原则、里式替换原则、接口分离原则、依赖倒置原则。

单一职责原则:一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类,这是解耦的重要步骤

开放封闭原则:对扩展开放,对修改关闭,比如使用代理模式

里式替换原则:当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系,也就是说不能模糊化类的概念,类的设计一定要严谨

接口分离原则:不要强迫使用不需要的接口,也就是说使用多个功能专一的接口比使用一个总接口要更好

依赖倒置原则:在需要依赖关系的地方尽量依赖接口和抽象类,而不是具体类


二、视图、数据、逻辑分离

将视图、数据、逻辑分离分离有很多好处,比如便于维护、提升复用性、增加容错性等,这里常用的分离架构有MVC、MVP、MVVM等。如果不进行分离,最后的结果会是所有非静态布局代码都在Activity中,出现一个文件打天下的情况,最后维护起来兼职就是噩梦,这是我们要避免的,好的app架构不应该出现这种情况。

MVC:获取数据的操作放到Model层,xml布局文件相当于是V层,Activity/Fragment相当于是C层,所有业务代码和界面都放到Controller中。虽然把获取数据抽象出了Model层,但C层依然太臃肿,这种模式一般只应用于很简单业务逻辑不多的界面,优点是写起来简单。

MVP:获取数据的操作放到Model层,Activity/Fragment此时相当于是V层,这是和MVC第一个不一样的地方,第二个不一样的地方是新增了Presenter来处理具体的业务逻辑,而V层只负责界面的显示相关逻辑,瞬间简洁了不少。当前MVP也有瑕疵,就是如果业务逻辑复杂的话,Presenter里要处理的内容过多,会导致Presenter很臃肿,不堪重负。

MVVM:获取数据的操作放到Model层,Activity/Fragment此时相当于是V层,然后新增了一个ViewModel层,通过DataBinding来将ViewModel跟布局文件进行绑定,从而将填充数据等逻辑直接废除,缺点是上手较慢一些,因为DataBinding还是有一定学习成本的。

总结:在实际业务中具体使用哪一种应该根据实际情况来定,如果是很简单的业务可以用MVC,一般是用MVP或者MVVM更好一些。


三、模块化架构

我们应该将那些固定的模块进行抽离,将重要的模块进行解耦,所以必须对app中的代码进行模块化架构。常见的分层方式是分为7层,分别是硬件、操作系统、JNI、Base、网络、common、业务模块。当前这并不代表最优,但是也是比较常见的划分模块的方式。前面3个是底层模块这里就不阐述,下面说一下其他4个模块的内容和依赖关系。

Base:存放接口抽象类util类等基础的类,只要后期稳定了基本都不需要修改,但是牵一发动全身,一般由架构师或高级程序员来维护。Base就是最基本的模块,不依赖任何模块。

网络:这里是存放网络请求用到的一些类,比如网络框架代码、网络错误处理、添加固定header等,网络层依赖于base层,因为需要base层的一些基类

common:这里是存放各个业务模块都需要用到的类,常见的比如自定义view,可以在各个业务中复用的时候,可以存放到common层,common模块也依赖于base模块。

业务模块:实际的业务代码,依赖于common层和网络层,这里可以使用组件化进行架构。由于组件化通信比较复杂,具体后面会重新开一篇文章来详细说明。


四、重要数据内置和缓存

为了提升app在用户没网时的体验,在主流app中都会对数据进行缓存,在没网时显示缓存内容。然后在下次打开app或者前后台切换时更改缓存文件,达到更新的目的。也可以在界面显示时对界面进行更新,不过这样做可能会造成闪烁,非常影响用户体验,这里不推荐这种做法。

除此之外,为了防止新用户首次打开app就出现网络不佳的情况,我们需要对重要数据进行内置,如果是列表我们可以只内置一页数据。内置方式就是将数据以json的形式保存在asset下的文件里,然后运行时从asset下读取并且解析到内存中使用。内置内容一般只在第一次打开app时可能会用到,后面都是用缓存内容进行显示。


五、其他经验之谈

1.我们可以在网络层写好,debug模式下自动打印出请求参数和返回值,方便开发时进行调试

2.网络请求时不同的错误需要进行区分处理,并且向服务端打点,方便查找问题

3.base层应该支持在实际界面关闭时取消网络请求,防止做无用操作,节省内存开销

4.如果是MVVM业务结构的话,需要注意的是如果ViewModel持有view的对象时应该使用弱引用的方式,然后应该在view关闭的生命周期中对该持有进行清除操作,防止内存泄漏

5.在需要切换同一布局的内容显示时,为了避免重复的显示隐藏判断操作,可以使用TipsView

6.在一些简单app中没有splash倒计时功能,此时要解决打开app白屏问题的话,我们仍然可以新建一个SplashActivity,此时可以在splash的onpause中关闭当前页面


最后:以上几点是很重要的经验,但也不代表考虑了这些点就是最优秀的架构了。实际项目远比理论复杂,所以我们需要做的是借鉴别人的经验,来解决自己的实际问题。作者以后有其他的经验也会更新上去,希望能给大家带来一些帮助。