在以往的开发中,咱们一般会使用MVC的模式进行开发,这样致使了Activity处理的逻辑很是的复杂,并且耦合度很是高,代码结构混乱、层次不清,各业务技术方案不统一,冗余代码充斥项目的各个角落;甚至连基本的包结构也是胡乱不堪,项目架构更是无从谈起。你们只不过是不停地往上堆砌代码添加新功能罢了。前端
其中业务层是一种非标准的 MVC 架构,Activity 和 Fragment 承担了 View 和 Controller 的职责:android
为了适应项目快速开发以及项目中代码的复用,解决项目中的耦合度。咱们不断引入了 Retrofit、UniversalImageLoader、OkHttp、ButterKnife 等一系列成熟的开源库,同时咱们也开发了本身的 UI 组件库 UIComponent、基础工具库 CommonUtils、基于第三方地图封装的 MapSDK、即时聊天模块 ChatLibrary 等等。这样就由基础组件层、业务组件层和业务层组成的三层架构。以下图:git
前面这种分层的架构自己是没太大问题的,即便到了如今咱们的业务项目也已然是基于这种分层的架构来构建的,只不过在不断的迭代中咱们作了些许调整(分层架构后面在介绍组件化和模块化的时候会详细介绍)。可是随着业务的不断迭代,咱们慢慢发现业务层这种非标准的 MVC 架构带来了种种影响团队开发效率的问题:github
Activity 和 Fragment 愈来愈多的同时承担了 Controller 和 View 的职责,致使他们变得及其臃肿且难以维护;算法
因为 Controller 和 View 的揉合,致使单元测试起来很困难;数据库
回调嵌套太多,面对负责业务时的代码逻辑不清晰,难以理解且不利于后期维护;缓存
各层次模块之间职责不清晰等等安全
总体项目架构以下图:微信
View Layer: 只负责 UI 的绘制呈现,包含 Fragment 和一些自定义的 UI 组件,View 层须要实现 ViewInterface 接口。Activity 在项目中再也不负责 View 的职责,仅仅是一个全局的控制者,负责建立 View 和 Presenter 的实例;网络
Model Layer: 负责检索、存储、操做数据,包括来自网络、数据库、磁盘文件和 SharedPreferences 的数据;
Presenter Layer: 做为 View Layer 和 Module Layer 的之间的纽带,它从 Model 层中获取数据,而后调用 View 的接口去控制 View;
Contract: 咱们参照 Google 的 Demo 加入契约类 Contract 来统一管理 View 和 Presenter 的接口,使得某一功能模块的接口能更加直观的呈现出来,这样作是有利于后期维护的。
模块层
业务逻辑层
基础组件层
为何要这么分?首先从总体模式上看跟微盘的架构大同小异,不少模块都是相同的,可能UI展示跟接口数据不大同样,业务逻辑是差很少的,可是目前很难再从之前的微盘中直接重用。因此分红单独的模块。
第一利于团队多模块开发,第二利于开发速度,只针对单个模块进行编译,编译速度提高了。第三每一个模块均可以单独成为APK运行,方便代码调试。第四就是易用性和重用性高。
对整个APP进行功能拆分,单独成立模块,每个模块都独立依赖基础组件和业务组件。咱们能够把 Basic Component Layer 和 Business Component Layer 放在一块儿看作是一层SDK,新的业务或者项目只须要依赖 SDK 就好。甚至咱们能够作得更极致一些,开发一套本身的组件管理平台,业务方能够根据本身的需求选择本身须要的组件,定制业务专属的SDK。业务端和SDK 的关系以下图所示:
封装了与模块层的数据交和UI回调,实际上就至关于Presenter的职责。调用接口以及数据处理都在这一层里面作,最终把结果回调给界面。
另外的职责就是封装经常使用的公共模块如数据库操做,缓存操做,HTTP请求等。
这一层可使用RxJava,能够很好的解决嵌套回调的问题。RxJava系列的文章能够参考这里
各 Layer 间严禁反向依赖:每一个层要进行依赖,先画好层于层直接的调用关系,禁止相互依赖。有相互依赖的把公共部分单独拆分。
这一层比较好理解,封装基础组件,好比模块化须要用到的Router来链接,而且能够管理Activity的生命周期。还有一些基础UIWidget库,好比股票的图表。
项目间的依赖经过私有maven库进行管理,特别是sdk跟presenter这一层,强制把UI跟逻辑分离。使用maven的一个弊端就是须要频繁的上传跟从新build。前期能够在项目用complie project(':sdk') 来依赖。
API接口做为核心的一层,每一个模块都须要调用该层,咱们采用分功能来设计接口,并提供统一的接口工厂来获取接口的实例。UML图以下:
接口层编码的时候要注意:
全站使用HTTPS证书
如何避免回调Listener的内存泄漏?(缘由:回调都是经过onSuccess()方法去处理,很容易引用到Context,而致使Http线程没办法退出)
封装成基础类去发请求,能够控制token失效从新发请求的过程。
采用Retrofit2 +okhttp3
接口缓存策略(参考Volley的缓存):
若是缓存中存在,先从缓存读取。
每个请求都有缓存时间,缓存过时或者失效后从新获取。
能够配置每一个请求启用或者禁用缓存,好比一些增删改查操做就不须要用到缓存。列表的形式通常要缓存。
缓存默认是关闭的,根据须要来给请求缓存。
通常状况下咱们的实体层(entity、bean、model)这些都是跟sdk处于一层的,为了不每一个模块为了使用实体层而引用sdk,因此要把这个实体层单独一层出来,避免相互之间有反向依赖的可能性。
除了经常使用的实体层以外,Model层还具备负责检索、存储、操做数据,包括来自网络、数据库、磁盘文件和 SharedPreferences 的数据的功能,只是都归根到Model这一块来。实际上他们都是单独开来的。
View划分红若干小模块,不仅仅可使用当前项目,更为了之后方便集成到其余项目当中去。
组件化和模块化使得程序更加灵活,为了不在app对各个模块以及组件的依赖当组件发生改变的时候代码修改很大。因此由RouterManger去统一管理各个模块组件之间的跳转。
实现方式能够采用ARouter,支持Url方式的跳转。
接口使用HTTPS加密证书进行传输,并进行用户鉴权,用户鉴权方面则打算采用Token方式。用户登陆以后分配一个accessToken和一个refreshToken,accessToken用于发起用户请求,refreshToken用于更新accessToken。accessToken会设置有效期,能够设为24小时。而用户退出登陆以后,accessToken和refreshToken都将做废。从新登陆以后会分配新的accessToken和refreshToken。
而后,我还打算在App层级分配AppKey和AppSecret,Android和iOS分别分配一对。每次向服务端发送请求时,AppKey都必须带上,服务端会对相应的AppKey进行校验。而AppSecret则须要安全保存在客户端,也不能在网络上进行传输,防止泄露。AppSecret只用于加密一些安全性级别较高的数据,以及为URL生成签名。URL签名算法步骤以下:
将全部参数按参数名进行升序排序;
将排序后的参数名和值拼接成字符串stringParams,格式:key1value1key2value2...;
在上一步的字符串前面拼接上请求URI的Endpoint,字符串后面拼接上AppSecret,即:stringURI + stringParams + AppSecret;
使用AppSecret为密钥,对上一步的结果字符串使用HMAC算法计算MAC值,这个MAC值就是签名。
鉴权流程以下:
最后总结设计为:
接口采用HTTPS和签名证书进行传输
接口参数经过排序 & URL & AppSecret 最终经过HMAC加密生成sign参数
利用底层JNI的so库提供接口进行加密,保证密钥的安全性
敏感字段so加密传输,好比设计密码、银行卡号、身份证号码等有关用户安全信息的字段
数据安全利用底层JNI实现,打包成so库提供JAVA接口调用。把公钥信息、加密算法放到so库里面防止反编译信息泄漏。
APK安全
android的apk文件其实是压缩文件,很容易被反编译工具进行反编译,像微信这些apk都能被反编译。为了增强APK的安全性,设计以下:
发布时对代码进行混淆编译
第三方APK文件加固
第三方加固网站:
行情模块(Quotation Module)
行情使用WSS安全传输。
行情之后台服务的形式,在Application onCreate() 的时候就开始创建WebSocket链接。没有订阅行情的时候关闭行情链接,当订阅的时候再次开启行情链接。目的为了节省流量以及手机电量。
由于行情基本上每一个页面都会用到,因此采用发布订阅的观察者模式进行设计,实现原理:
每一个Presenter均可以注册本身须要的行情(Quotation Filter),当WebSocket 接收到对应的Quotation Filter的时候调用EventBus中间件发送消息。最终会回调到该类的定义的事件方法中去。
把数据处理好前端须要展现的数据实体返回。如行情的状态是上涨仍是下跌的状态。
统一使用QuotationManager来管理行情的链接、订阅;抽象成QuotationAction 来管理注册不一样的行消息,解析返回。
行情服务接口方法:
start() 启动服务
stop() 中止服务
getStatus() 获取当前状态
register(object handler, QuotationAction action) 订阅行情
unregister(); 反注册行情
依赖项:API
说明:
User Manager :用户管理,管理当前用户的登陆状态、用户信息、用户操做(退出登陆,状态维持,Token刷新)
User Module:用户模块,跟用户相关的各个功能的业务处理、数据处理
依赖项:API、行情模块
其余模块须要调用交易模块都是经过路由跳转的方式调用不会直接调用到内部方法里面,因此交易模块重点仍是调用API进行数据处理:
本文参考连接: