系列文章导航:

1. Android应用架构的一些思考-从零开始

2.Android应用架构的一些思考-基础版架构的整体搭建

3.Android应用架构的一些思考-框架模块化


最近整理项目的代码,又回想到当初实习时一个人做公司项目的Android客户端,没有什么实践经验,又投路无门的状态,那一个月为了项目做的像点样子,又是在网上搜索,又是反编译别家的应用研究,真的时想破头皮。不过好歹,那段时间的折磨还是有那么点价值,现在想想,有必要整理总结,分享一下。

Android我也是看着网上的教材自学起来的,无论是Google 官方的 Training,还是各种教材,主要都着眼于各种控件和系统组件的使用,就像教小孩子玩积木说,这是长条,这是方块;诚然,这很重要,但是大部分培训和教材也就到此为止了,最多辅以一两个Demo一般的小练习,然后很多人就开始了找工作的碰壁之旅,这就像把积木都认识完了,只识得各种积木的孩子却要去搭一个摩天大楼,这个过程自然会比较艰辛。

这里我就整理一下我的自学经验分享出来,一方面是对自己工作学习的总结,另一方面也希望能够帮助到后来者 :)

ps: 最近网上流行MVP和MVVM,Google官方也推出了MVP的Sample:Android-Architecture,不过网上大部分Demo级别的写法我在尝试运用到项目时遇到了很大困难,Google-MVP也没大规模应用过,以后再分享学习心得。

ps: 最开始的思路来自于:Android应用架构 这篇文章,感谢作者和译者。


0. 基本思路

首先要了解一下我们一般做的商业应用都是什么,看过《App研发录》对于其中移动应用的类型划分是比较赞同的,即 一般的移动应用分为三种:数据展示类应用,手机助手类应用,游戏。 数据展示类应用特点是页面多,需要频繁调用后端接口进行数据交互,一般都涉及支付流程,考验弱网络环境下数据的正常获取,减少电量和流量消耗。 手机助手类应用则主要着眼于系统API的调用,达到辅助管理系统的目的。 我们一般做的应用都是数据展示类型应用,然而一般应用上了规模也或多或少会涉及到系统API的调用,暂且不表。

1. 从0开始的基本版应用框架

其实在学完大部分Android课程之后,是有一个最基础的应用框架的,这个框架的核心,就是 ActivityFragment,有人说Android应用的整体架构是MVC,M就是在应用中定义的Java Entities,V就是各种View,XML配置文件,C就是Activity和Fragment,但是在实际开发中,就会发现,Activity和Fragment简直是上帝一般的存在,它们不仅负责了Controller的功能,实际上也负责了View的管理,而且还持有诸如Context等系统资源。 实际上可以把这个架构看作是一个MV之间相互交互的架构
刚刚开始做Demo一般也是先从页面开始,一个页面建一个Activity或者Fragment,然后初始化数据,初始化控件,绑定监听......所有的这些操作都是在Activity和Fragment定义的类里面进行的。对于一些只有几个输入框,或者只有一个ListView的Demo来说,这样写并不会有什么问题。 但是,假如现在要做的不是Demo,而是一个商城应用的购物车页面呢?或者是商品详情页面,订单结算页面呢,这些页面都有大量的控件初始化,数据增删改查,手势操作,如果这些事情都交给Activity/Fragment来做,那么一个类的代码很快就会破千行。涉及到诸如用户数据之类的应用全局共享数据,又或是检查缓存之类的功能,Activity/Fragment的负担也会十分繁重。
对于这种问题, 解决方法就是要分担Activity和Fragment的工作,一般来说,页面无关的方法,我们都会将其放到工具类里面,比如验证手机号、邮箱之类的正则,对Log的封装,而网络调用一般是通过异步网络库来做的,比如volley,Retrofit,封装为一个个Request,Call,通过将这些代码抽出来,会小幅改善Activity和Fragment压力过大的情况,(如何使用和封装这些网络框架请参见相关博文,此处不赘述也不写示例了) 整个项目大概的结构是这个样子的:
我做的第一个应用就是这样的架构,应用比较简单,但是写起来还是比较蹩脚,因为除了网络调用,Activity/Fragment还需要操作当前的Java Entity对象,操作本地数据库,操作本地文件等。比如获取当前用户信息的缓存,或者获取文章列表,购物车列表之类的数据,有可能涉及到多个页面使用数据,而获取数据也有一定的检查,比如分页加载和下拉刷新的判断,是否使用缓存等。这些操作本身不应由Activity和Fragment来做,将这些操作放在网络模块或者Java Entities里面明显都不很合理。Java Entity本身就应该只是一个数据库数据映射的Java对象模型,赋予其管理缓存数据的职责只会让其变得混乱,功能不明确。比如涉及到分页加载的列表,如新闻列表,如果我在Model包中定义一个NewsList,那么这个NewsList到底是一个数据模型呢,还是一个数据管理者呢?想必以后看代码的时候,可能会困惑一下。User的数据一般是应用内全局使用的,如果我将其定义成一个单例模式,JSON反序列化之类的操作又会比较蛋疼了。而放在网络模块就更不合理了,为什么我注销用户会需要一个UserCall对象?

2. 基本版架构的改良之路

在有了上面的困惑之后,改良的方案已经呼之欲出了: 抽象出一个新的管理者,让它去协调缓存与网络调用。 其实在试图处理分页加载的数据缓存的时候,这个新的数据管理者就已经初步成形了,NewsList这个所谓的Model实际上就是一个数据管理者。只不过它的数据刷新需要依靠Activity/Fragment调用网络回调之后再set给它而已。先抛开细枝末节仔细回想一下,从客户端的UI点击响应向服务端发起请求,到服务端返回数据刷新UI,其实是有一个清晰的数据流的: UI发起请求 - 检查缓存 - 调用网络模块 - 解析返回JSON / 统一处理异常 - JSON对象映射为Java对象 - 缓存 - UI获取数据并展示

通过这个数据流,可以很明显的网络数据请求的一个三级分层: UI层,数据管理层(缓存处理,请求网络),网络调用层  继续以分页加载新闻列表这个例子来说: 之前只是声明了一个NewsList的Model,只有存储数据的功能,对于一个Java Entity来说,可能NewsList是这样的:
public class NewsList {//当前新闻列表private List newsList = new ArrayList();//当前页码private int currentPage = 1;//总页码private int totalPage = 1;public NewsList() {......}public void addToList(List list, int currentPage) {newsList.addAll(list);this.currentPage = currentPage;}public void setTotalPage(int totalPage) {this.totalPage = totalPage;}public void getTotalPage() {return totalPage;}/*以下各种set和get就不占篇幅了,这里也有一个上面所述的问题,即对于一个数据管理者来说,要开放get/set把私有数据给外部用么?如果不开放,它是Entity么?这也是职责混乱的一个体现吧...*/}

现在不妨将其提升为NewsListManager,也许它看上去就会更加合理了:
//首先定义一个获取数据的回调接口public interface ActionCallbackListener {void onSuccess(T data);void onFailed(Exception e, String message);//这里异常返回可以是处理过的异常id,也可以是原始的Exception对象,按照自己设计即可}
//Manager对象的实现public class NewsListManager extends BaseDataManager {//当前新闻列表private List mNewsList = new ArrayList();//当前页码private int currentPage = 1;//总页码private int totalPage = 1;public NewsListManager() {getCacheFromDatabase();}public List getCachedData() {return mNewsList;}public void pageLoadNewsList(boolean isRefresh, final ActionCallbackListener> mActionCallbackerListener) {if(isRefresh) {clearCache();mNewsList.clear();currentPage = 1;}NewsListRequest request = new NewsListRequest(); //假定这里是网络调用模块请求新闻列表的Request对象,细节不表request.setData(currentPage);request.request(new RequestCallback() {@Overridevoid onSuccess(JSONObject response) {totalPage = response.optInt("total_page");currentPage = response.optInt("current_page");//将网络数据存储到manager....... saveToDataBase()if(mActionCallbackerListener != null) {mActionCallbackListener.onSuccess(mNewsList);}}@Overridevoid onFailed(Exception e, String message) {if(mActionCallbackerListener != null) {mActionCallbackListener.onFailed(e, message);}}});}private void getCacheFromDatabase() {//将缓存数据从数据库中取出}private void saveToDatabase() {//缓存新闻数据至数据库}private void clearCache() {//清除数据库中的缓存}//......}
可以看到通过这样的封装,UI层根本不需要管理分页加载的逻辑,只需要调用NewsListManager的pageLoadNewsList()方法,告诉Manager是否需要刷新即可,与UI的处理逻辑(下拉刷新,分页加载)一致,这样就极大的简化了Activity和Fragment的工作。同理,这样的逻辑也可以应用于应用使用的用户数据,通过isRefresh去判断是否需要从服务端重新拉取,因为大部分应用修改用户数据的入口就那么几个,大部分情况下是不需要每次请求用户数据都用request从网络获取的,Manager实现的缓存机制就可以大幅减少不必要的接口调用,但是UI层请求数据的方法并没有任何改变。 通过Manager的封装,可以将整个应用分为三层:UI,Managers,NetModules。注意所谓分层并不是随便分几个package而已,而是有严格的职责和权限划分的,即每层都各有其职,每一层都向上层提供接口,封闭细节。比如UI层向Manager发起数据请求,并不需要关心Manager使用的是缓存还是网络请求,也不需要关心网络请求如何封装的报文参数。只需要根据Manager提供的接口参数发起请求,即可获得数据。这种架构大概是这个样子的:


由于绝大部分代码都从Activity和Fragment中剥离,现在 Activity/Fragment只负责UI控制与数据获取,其它的绝大部分代码都可以做到UI无关,如果在开发中尽力确保这一点,那么在接口设计合理的基础之上,现有的Managers,网络模块以及其它一些工具类完全可以构成一个AppSDK,为手机,平板应用提供支持,项目无关的工具类和通用控件,则可以划归到公共开发资源库模块,大幅减少重复工作量。
不得不说,不需要优秀,哪怕一个靠谱的架构设计,都可以极大提升代码质量,降低工作量。这种改良版的应用架构目前在我的几个项目上都工作良好,极大减少了重复代码,不过最重要的一点是,即使在赶工阶段,已经基本什么都顾不上的状态下,依旧保持了应用整体的架构和规模没有失控,因为没有比利用框架提供的方式更快的写法。。。
总是有人说过早优化乃万恶之源,东西先出来再说,但我个人觉得无论是敏捷开发还是快速出东西,都是有门槛的,如果达不到这个门槛,为了速度而牺牲质量导致项目架构失控,代码混乱,结果就只有重构时更为辛苦,或者给别人留坑。效率为王这种概念,是针对熟练工说的,在没有经验的时候,多花点时间保证代码质量还是十分重要的。



更多相关文章

  1. 一句话锁定MySQL数据占用元凶
  2. OpenGL ES 实现可视化实时音频
  3. Android将数据库保存到SD卡的实现
  4. android 增删查完整例子
  5. android网游开发之socket的简单设计和实现
  6. 【专访】爱图腾高级架构师 李鹏军:手机音频接口具有无限的应用潜
  7. Android/Java 实现PCM与G.711编码互转
  8. Android(安卓)数据持久化(一)之简单数据持久化
  9. 如何在Android实现桌面清理内存简单Widget小控件

随机推荐

  1. Android仿iphone开关按钮
  2. Android(安卓)DOC文档分析——ContentPro
  3. ContentPRovider示例
  4. 自定义数据适配器
  5. Android(安卓)Studio 添加Assets目录
  6. android fragment addToBackStack bug
  7. 关于android API中的android.app.Pending
  8. android 按钮点击事件
  9. android vlc compile error fix
  10. Activity嵌套ViewPager+Fragment