Android应用架构的一些思考-基础版架构的整体搭建
2016-11-03 13:15
323 查看
系列文章导航:
1. Android应用架构的一些思考-从零开始
2.Android应用架构的一些思考-基础版架构的整体搭建
3.Android应用架构的一些思考-框架模块化
之前的文章总结了一下自己从实习到现在摸索出的一点应用框架设计的经验,更多的是从整体框架设计上来说分层和模块划分的,这两天整理项目代码,可以从更细的方面总结一下一个应用框架应该具备哪些基本的要素。
建议在开发中尽可能遵循 Android开发最佳实践
内存泄漏分析:LeakCanary
基础网络库:OKHttp3
JSON序列化/反序列化:Gson
异步网络调用框架:volley、Retrofit2,如果选择Retrofit的话一般是配合RxJava来使用的
组件间通信:EventBus
图片框架的话,如果应用对图片管理的需求并不高,用Glide,如果经常加载图片或者说要做图片处理则使用Fresco。
视频相关目前并没有找到什么比较强悍的框架,原生调用相机录制视频比较蛋疼的地方就在于其视频质量只有2档,要么糊的不能看,要么拍出来的文件大的不能看。如果项目有视频录制的需求还是建议自己实现较好。
我自己目前在项目中是基于Retrofit + RxJava实现整个调用链,整体简化了应用内异步调用的处理,RxJava目前来说学习曲线还是比较陡峭,而且配合Retrofit使用的时候也会有一些隐含的Bug,如果没有什么经验支撑的话建议还是不要初学就在公司项目上使用比较好,不然没人填坑就麻烦了=。=
GlobalConfig - 应用全局的一些配置参数
base - 应用基本组件的一些基类
core - 应用核心层,包含Manager、DatabaseHelper、JavaBean对象等
event - EventBus 用于派发的事件类定义
net - 应用网络层,包含Retrofit的异步调用的封装,网络异常定义,Session/Cookie的管理等,得益于Retrofit对Gson的集成,在使用Retrofit进行Http请求时可以直接拿到反序列化的Java对象,因此网络层也承担了JSON解析的工作。
thirdsdk - 绝大部分第三方SDK能作为Module集成的我都会作为Module集成,一方面是保证了三方SDK的独立性,方便复用到其他项目,另一方面也使得App主工程更为干净,降低维护成本,实在对App工程依赖太高的SDK(比如推送实现需要根据推送信息打开某个页面),则放在thirdsdk包中方便管理。
ui - 无需赘述,UI层相关
util - 项目相关工具类
widget - 项目相关自定义控件
整个Project结构如下:
总的来说就是能用Library的尽量用Library,减少App主工程中的项目无关资源,所有项目无关的工具类和自定义控件全部存放在commondevlib这个module里面,目前这个项目无关的常用资源合集我也在Github上分享 CommonDevLibrary。
finish。
以下为我的一个BaseActivity示例:
关于Manager层,需要明确的一点就是Manager是UI无关的。不需要Manager对UI做出任何操作,哪怕是弹一个Toast。Manager层主要负责根据上层的请求协调内存缓存,数据库、文件缓存和网络调用,我自己是根据数据模型来划分Manager的,比如一个应用的User模型一般是只有一个的,那么就可以建立一个单例模式的UserManager来管理User,不过这样也会有问题,即假如User的数据很多,UserManager又会变得非常庞大,而且很多数据是一次性使用的,并不需要与User一样长期存储(再次打开页面需要重新请求网络)。因此后期我的做法是需要长期存储、关联度高、被全局使用的核心数据模型(User,购物车等)编写在一个Manager,其它数据单独编写Manager(如用户订单数据,收藏等),而一些工具型接口单独编写一个Manager。
对于Manager来说,编写基类也是必要的,比如对分页加载的处理,可能每个项目分页加载数据的逻辑不同,这里我仅以我目前使用的分页加载逻辑作基类编写示例:
服务端返回分页数据为
剩下的工具模块大部分来源于代码的积累,定制控件也是项目针对性比较强的,关于数据库部分,如果项目比较简单,对数据库缓存的依赖不高,那么自己在DatabaseHelper的基础之上实现一些简单的增删改查即可,如果依赖较高的话,可以考虑集成ORM框架,比如GreenDAO。
封装请求参数,将其转换为服务端所需请求数据(XML、JSON、form表单等)
解析返回数据,将其转换为Java对象返回上层
统一处理异常,包括三类异常:服务端定义的返回异常(登录失效,参数错误等),Http请求异常(404,500等),客户端本地的异常(请求超时,数据解析错误等)
Cookie/Session缓存
上传,下载等需求的支持
.....
其实如果能够为框架选定确定的网络模块,那么在此基础之上还可以积累相当的项目无关代码。
由于我的项目使用了RxJava,通过RxLifeCycle实现取消事件流订阅使得这一问题简单解决。
以上是我对一个正经商业应用起步应该如何架构的一点理解,根据最近的趋势看,native app至少是国内的native app,大部分正在变得平台化,即针对某一项或者某几项业务而开发的app将会越来越少,大部分都由WebApp和微信公众号取代;有开发原生App需求的公司,大部分都是以其核心业务为主干,将移动客户端作为其在移动端的业务承载平台。对于开发者来说,这样的趋势就将会考验应用架构的可扩展性,组件化/插件化的移动端架构预计在未来将会为更多公司在开发App中首先选择。不过这类架构相对而言面向的层次更高,对于应用的每个业务模块,基础的架构设计还是行之有效的。
1. Android应用架构的一些思考-从零开始
2.Android应用架构的一些思考-基础版架构的整体搭建
3.Android应用架构的一些思考-框架模块化
之前的文章总结了一下自己从实习到现在摸索出的一点应用框架设计的经验,更多的是从整体框架设计上来说分层和模块划分的,这两天整理项目代码,可以从更细的方面总结一下一个应用框架应该具备哪些基本的要素。
建议在开发中尽可能遵循 Android开发最佳实践
0.框架和技术的选择
其实现在Android应用开发应该说是比较简便了,大量的开源库使得开发者能够快速在这些开源库的基础之上快速搭建一个功能强大的应用框架。我们都在说,不要重复造轮子,那么应该说,熟悉常用的开源框架就是一个初学者进阶的必经之路了。这里说一下我在应用框架搭建中使用的第三方框架:内存泄漏分析:LeakCanary
基础网络库:OKHttp3
JSON序列化/反序列化:Gson
异步网络调用框架:volley、Retrofit2,如果选择Retrofit的话一般是配合RxJava来使用的
组件间通信:EventBus
图片框架的话,如果应用对图片管理的需求并不高,用Glide,如果经常加载图片或者说要做图片处理则使用Fresco。
视频相关目前并没有找到什么比较强悍的框架,原生调用相机录制视频比较蛋疼的地方就在于其视频质量只有2档,要么糊的不能看,要么拍出来的文件大的不能看。如果项目有视频录制的需求还是建议自己实现较好。
我自己目前在项目中是基于Retrofit + RxJava实现整个调用链,整体简化了应用内异步调用的处理,RxJava目前来说学习曲线还是比较陡峭,而且配合Retrofit使用的时候也会有一些隐含的Bug,如果没有什么经验支撑的话建议还是不要初学就在公司项目上使用比较好,不然没人填坑就麻烦了=。=
1. 整体包结构划分
一般应用的包结构划分有两种,一种是按组件类型划分,比如activity, fragment,widget,util......另一种是按业务场景划分,比如home(应用首页),usercenter(个人中心),settings(设置)......两种划分方式各有优劣。不过这样的划分建议只针对UI层,第二种划分方式建议只放业务场景相关的页面和专属工具类,以免导致整个包结构混乱。目前我最近的一个项目包结构如下:GlobalConfig - 应用全局的一些配置参数
base - 应用基本组件的一些基类
core - 应用核心层,包含Manager、DatabaseHelper、JavaBean对象等
event - EventBus 用于派发的事件类定义
net - 应用网络层,包含Retrofit的异步调用的封装,网络异常定义,Session/Cookie的管理等,得益于Retrofit对Gson的集成,在使用Retrofit进行Http请求时可以直接拿到反序列化的Java对象,因此网络层也承担了JSON解析的工作。
thirdsdk - 绝大部分第三方SDK能作为Module集成的我都会作为Module集成,一方面是保证了三方SDK的独立性,方便复用到其他项目,另一方面也使得App主工程更为干净,降低维护成本,实在对App工程依赖太高的SDK(比如推送实现需要根据推送信息打开某个页面),则放在thirdsdk包中方便管理。
ui - 无需赘述,UI层相关
util - 项目相关工具类
widget - 项目相关自定义控件
整个Project结构如下:
总的来说就是能用Library的尽量用Library,减少App主工程中的项目无关资源,所有项目无关的工具类和自定义控件全部存放在commondevlib这个module里面,目前这个项目无关的常用资源合集我也在Github上分享 CommonDevLibrary。
2. 基类编写
一般Activity,Fragment,Dialog等基本组件都会编写基类继承,用以实现一些共有的功能,对于有特殊功能的部分组件可以再编写二级基类集成自一级基类,建议不要超过三级。编写基类可以解决一些很经典的问题,比如在页面层级很深的时候怎么退出应用回到桌面,自己编写Activity栈的行为会使Activity的引用被长期持有从而导致内存泄漏。如果Activitiy继承自基类的话就可以解决这个问题:利用BroadcastReceiver或者EventBus发送广播,通知所有未被销毁的Activityfinish。
以下为我的一个BaseActivity示例:
/** * Activity 基类 */ public abstract class BaseActivity extends AppCompatActivity { protected static String TAG; //用于打印Log的TAG标识 private CommonReceiver mReceiver = new CommonReceiver(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TAG = this.getClass().getSimpleName(); IntentFilter filter = new IntentFilter(); filter.addAction("exit"); registerReceiver(mReceiver, filter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(mReceiver); } public void showToast(CharSequence message) { Toast.makeText(BaseActivity.this, message, Toast.LENGTH_SHORT).show(); } public void showToast(CharSequence message, int time) { Toast.makeText(BaseActivity.this, message, time).show(); } /** * 简化findViewById流程,省略强制转型步骤 * <p>由于T extends View, 因此不存在转型的类型检查问题</p> * */ @SuppressWarnings("unchecked") protected <T extends View> T findView(@IdRes int resId) { return (T) findViewById(resId); } @SuppressWarnings("unchecked") protected <T extends View> T findView(@IdRes int resId, View containerView) { return (T) containerView.findViewById(resId); } /** * 基础类Activity的BroadcastReceiver */ protected class CommonReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("exit")) { finish(); } } } }Fragment和Dialog同理,Adapter涉及到使用RecyclerView/ListView的选择,RecyclerView的Adapter可以实现相当强大的功能,在此就不赘述了。
关于Manager层,需要明确的一点就是Manager是UI无关的。不需要Manager对UI做出任何操作,哪怕是弹一个Toast。Manager层主要负责根据上层的请求协调内存缓存,数据库、文件缓存和网络调用,我自己是根据数据模型来划分Manager的,比如一个应用的User模型一般是只有一个的,那么就可以建立一个单例模式的UserManager来管理User,不过这样也会有问题,即假如User的数据很多,UserManager又会变得非常庞大,而且很多数据是一次性使用的,并不需要与User一样长期存储(再次打开页面需要重新请求网络)。因此后期我的做法是需要长期存储、关联度高、被全局使用的核心数据模型(User,购物车等)编写在一个Manager,其它数据单独编写Manager(如用户订单数据,收藏等),而一些工具型接口单独编写一个Manager。
对于Manager来说,编写基类也是必要的,比如对分页加载的处理,可能每个项目分页加载数据的逻辑不同,这里我仅以我目前使用的分页加载逻辑作基类编写示例:
服务端返回分页数据为
{ pageInfo: { pageSize: 10, pageIndex: 1, pageTotal: 5, itemTotal: 48 }, data:[......] }分页加载数据Manage基类:
/** * 其实这里还可以用泛型封装的更加完善,待后续处理 */ public abstract class BasePageLoadManager extends BaseManager { /** 总页数 */ protected int totalPageNum = 0; /** 每页条数 */ protected int pageSize = 0; /** 当前页 */ protected int currPageIndex = 0; /** 总条数 */ protected int totalItemNum = 0; public boolean hasMore() { return currPageIndex < totalPageNum; } public BasePageLoadManager() { setPageSize(configPageItemSize()); } /** * 设置分页加载每页条目数量 * * @return 每页条目数量 * */ protected abstract int configPageItemSize(); public int getTotalItemNum() { return totalItemNum; } public int getCurrPageIndex() { return currPageIndex; } public int getPageSize() { return pageSize; } public int getTotalPageNum() { return totalPageNum; } protected void setTotalPageNum(int totalPageNum) { this.totalPageNum = totalPageNum; } protected void setPageSize(int pageSize) { this.pageSize = pageSize; } protected void setCurrPageIndex(int currPageIndex) { this.currPageIndex = currPageIndex; } protected void setTotalItemNum(int totalItemNum) { this.totalItemNum = totalItemNum; } }
剩下的工具模块大部分来源于代码的积累,定制控件也是项目针对性比较强的,关于数据库部分,如果项目比较简单,对数据库缓存的依赖不高,那么自己在DatabaseHelper的基础之上实现一些简单的增删改查即可,如果依赖较高的话,可以考虑集成ORM框架,比如GreenDAO。
3. 网络模块的编写
这一个部分的项目针对性和框架针对性都比较强,整个数据请求的封装和返回数据的解析,都依赖于服务端的接口设计,使用volley、Retrofit或者自己封装实现的网络模块样子也大相径庭,可以在后面针对常用的框架单独讲,这里总结一下网络模块主要要做的事情:封装请求参数,将其转换为服务端所需请求数据(XML、JSON、form表单等)
解析返回数据,将其转换为Java对象返回上层
统一处理异常,包括三类异常:服务端定义的返回异常(登录失效,参数错误等),Http请求异常(404,500等),客户端本地的异常(请求超时,数据解析错误等)
Cookie/Session缓存
上传,下载等需求的支持
.....
其实如果能够为框架选定确定的网络模块,那么在此基础之上还可以积累相当的项目无关代码。
4. 其它小细节
在网络框架搭建完成之后,一个应用的基本骨架就已经成型,剩下的工作主要决定于产品原型设计以及项目的针对性需求了,不过在各个层之间的协调上,也需要有一定的考虑,有一些用户在UI层的操作是会关联到整个应用的,比如弱网络环境,网络信号很差的情况下,获取数据耗时很长,绝大部分用户往往会不耐烦而关闭页面,如果这个时候不对已经发出的请求取消订阅,在请求返回时界面已经销毁,则很有可能引起空指针异常而导致应用崩溃。因此对于涉及到网络请求的模块,一定要提供取消回调的接口。由于我的项目使用了RxJava,通过RxLifeCycle实现取消事件流订阅使得这一问题简单解决。
以上是我对一个正经商业应用起步应该如何架构的一点理解,根据最近的趋势看,native app至少是国内的native app,大部分正在变得平台化,即针对某一项或者某几项业务而开发的app将会越来越少,大部分都由WebApp和微信公众号取代;有开发原生App需求的公司,大部分都是以其核心业务为主干,将移动客户端作为其在移动端的业务承载平台。对于开发者来说,这样的趋势就将会考验应用架构的可扩展性,组件化/插件化的移动端架构预计在未来将会为更多公司在开发App中首先选择。不过这类架构相对而言面向的层次更高,对于应用的每个业务模块,基础的架构设计还是行之有效的。
相关文章推荐
- Android应用架构的一些思考-从零开始
- Android应用架构的一些思考-框架模块化
- 关于互联网应用前端架构的一些思考
- Android App整体架构设计的思考
- Android App整体架构设计的思考
- Android App整体架构设计的思考
- Android App整体架构设计的思考(一)
- Android App整体架构设计的思考
- android 应用架构随笔一(架构搭建)
- Android App整体架构设计的思考(一)
- Android App整体架构设计的思考
- Android App整体架构设计的思考
- Android App整体架构设计的思考
- Android App整体架构设计的思考
- android 应用架构随笔一(架构搭建)
- 互联网应用服务器总架构的一些思考
- [转]Android App整体架构设计的思考
- Android App整体架构设计的思考
- Android App整体架构设计的思考
- Android App整体架构设计的思考