1.分包
1.1.首先是其架构,是按功能模块进行划分的,但不必分得太细,最多五个模块就够了,很多类按其功能其实可以属于多个模块,这个时候就需要抽出来。
1.2.类定义要明确,权责单一
2.详解
2.1.模型层定义了所有的模型
2.2.接口层封装了服务器提供的API
2.3.核心层处理所有业务逻辑
2.4.界面层就处理界面的展示
接口层
1.接口层封装了网络底层的API,并提供给核心层调用
1.1.PostEngine,请求引擎类,对请求的发送和响应结果进行处理;PostEngine将请求封装好发送到服务器,并对响应结果的json数据转化为Response对象返回Response其实就是响应结果的json数据实体类,json数据是有固定结构的,分为三类,如下:
{"event": "0", "msg": "success"}
{"event": "0", "msg": "success", "obj":{...}}
{"event": "0", "msg": "success", "objList":[{...}, {...}], "currentPage": 1, "pageSize": 20, "maxCount": 2, "maxPage": 1}
1.1.1event为返回码,0表示成功
1.1.2.msg则是返回的信息
1.1.3.obj是返回的单个数据对象
1.1.4.objList是返回的数据对象数组
1.1.5.currentPage表示当前页
1.1.6.pageSize则表示当前页最多对象数量
1.1.7.maxCount表示对象数据总量
1.1.8.maxPage表示总共有多少页
1.2.Response,响应类,封装了Http请求返回的数据结构;
根据以上结构,Response基本的定义如下:
public class Response<T> {
private String event;
private String msg;
private T obj;
private T objList;
private int currentPage;
private int pageSize;
private int maxCount;
private int maxPage;
//getter和setter方法
...
}
每个属性名称都要与json数据对应的名称相一致,否则无法转化。obj和objList用泛型则可以转化为相应的具体对象了。
1.3.Api,接口类,定义了所有的接口方法;
public Response<Void> login(String loginName, String password);
public Response<VersionInfo> getLastVersion();
public Response<List<Coupon>> listNewCoupon(int currentPage, int pageSize);
1.4.ApiImpl,接口实现类,实现所有Api接口方法。
@Override
public Response<Void> login(String loginName, String password) {
try {
String method = Api.LOGIN;
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("loginName", loginName));
params.add(new BasicNameValuePair("password", EncryptUtil.makeMD5(password)));
TypeToken<Response<Void>> typeToken = new TypeToken<Response<Void>>(){};
return postEngine.specialHandle(method, params, typeToken);
} catch (Exception e) {
//异常处理
}
}
实现中将请求参数和返回的类型定义好,调用PostEngine对象进行处理。
核心层
核心层介于接口层和界面层之间,主要处理业务逻辑,集中做数据处理。向上,给界面层提供数据处理的接口,称为Action;向下,调用接口层向服务器请求数据。
1.向上的Action中定义的方法类似如下:
public void getCustomer(String loginName, CallbackListener callbackListener);
这是一个获取用户信息的方法,因为需要向接口层请求服务器Api数据,所以添加了callback监听器,在callback里对返回的数据结果进行操作。CallbackListener就定义了一个成功和一个失败的方法,代码如下:
public interface CallbackListener<T> {
/**
* 请求的响应结果为成功时调用
* @param data 返回的数据
*/
public void onSuccess(T data);
/**
* 请求的响应结果为失败时调用
* @param errorEvent 错误码
* @param message 错误信息
*/
public void onFailure(String errorEvent, String message);
}
接口的实现基本分为两步:
1)参数检查,检查参数的合法性,包括非空检查、边界检查、有效性检查等;
2)使用异步任务调用接口层的Api,返回响应结果。
需要注意的是,Action是面向界面的,界面上的数据可能需要根据不同情况调用不同的Api。后续扩展可以在这里添加缓存,但也要视不同情况而定,比如有些变化太快的数据,添加缓存就不太适合了。
界面层
1.界面层处于最上层,其核心就是负责界面的展示
2.界面层package的定义我也并不按照旧版的功能模块划分,而根据不同类型划分,这个地方是有待商榷的,其实也是可以在内部进一步细化
3.其中,activity、adapter、fragment各自都有一个基类,做统一的处理,比如定义了一些共用的常量、对象和方法等,界面层是最复杂,最容易变得混乱不堪,最容易出问题的层级。所以,从架构到代码,很多东西都需要设计好,以及规范好,才能保证程序易维护、易扩展。
4.如果需要为不同商户定制不同app的需求,Android Studio可以通过设置Gradle,不同app可以添加不同的productFlavors。
模型层
1.模型层横跨所有层级,封装了所有数据实体类,基本上也是跟json的obj数据一致的,在接口层会将obj转化为相应的实体类,再通过Action传到界面层。
2.另外,模型层还定义了一些常量,比如用户状态、支付状态等。在Api里返回的是用1、2、3这样定义的,而我则用枚举类定义了这些状态。用枚举类定义,就可以避免了边界的检查,同时也更明了,谁会记得那么多1、2、3都代表什么状态呢。然而用枚举类定义的话,就必须能将1、2、3转化为相应的枚举常量。这里,我提供两种实现方式:
2.1.使用gson的@SerializedName标签,比如0为FALSE,1为TRUE,通过gson的方式,直接访问TRUE或FALSE就会自动序列化为1或0;可以如下定义:
public enum BooleanType {
@SerializedName("0")
FALSE,
@SerializedName("1")
TRUE
}
2.2.通过定义一个value,因为没有序列化,则需要通过getValue方式获取1或0如下:
public enum BooleanType {
FALSE("0"),
TRUE("1");
private String value;
BooleanType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
更多建议
1. 如何让应用层代码合理的解耦内聚?
无论是iOS的controller还是android的activity,都容易变成fat MVC。怎么样找到一种方式去拆分这部分代码很重要。如果一个工程师能清楚的知道他每一个函数该放到哪一个类,这样应用层才好维护。 关于这一块有很多成熟的方案了,mvc,mvvm,mvp等等,但这些是方案,细节还需要架构师自己敲定。
2.要有清晰的data flow:
一个app说到底是关于data的变化和展现。从用户输入采集数据,上报服务器,加工再展示。这个流程能不能在你的架构里看出清晰的data flow很重要。工程师在遇到bug的时候第一反应是查数据是不是出了问题,头脑里有data flow就能快速的定位问题。
参考文档:
https://www.zhihu.com/question/27645587
http://keeganlee.me/post/android/20150605
http://blog.youkuaiyun.com/luyi325xyz/article/details/43482123
http://blog.youkuaiyun.com/luyi325xyz/article/details/43085409