项目分为了四个层级:模型层、接口层、核心层、界面层。模型层定义了所有的模型;接口层封装了服务器提供的API;核心层处理所有业务逻辑;界面层就处理界面的展示。几个层级之间的关系如下图所示:

层级关系:模型层,用于各层的数据交互;接口层为核心层提供实现API;核心层用于提供处理业务逻辑;界面层用于和界面交互。

下面展开说明具体的每个层次:

接口层

接口层封装了网络底层的API,并提供给核心层调用。刚开始,为了简单,该层的核心类我只定义了4个:

  1. PostEngine,请求引擎类,对请求的发送和响应结果进行处理;
  2. Response,响应类,封装了Http请求返回的数据结构;
  3. Api,接口类,定义了所有接口方法;
  4. ApiImpl,接口实现类,实现所有接口方法。

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}

event为返回码,0表示成功,msg则是返回的信息,obj是返回的单个数据对象,objList是返回的数据对象数组,currentPage表示当前页,pageSize则表示当前页最多对象数量,maxCount表示对象数据总量,maxPage表示总共有多少页。根据此结构,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用泛型则可以转化为相应的具体对象了。

Api接口类定义了所有的接口方法,方法定义类似如下:

public Response<Void> login(String loginName, String password);
public Response<VersionInfo> getLastVersion();
public Response<List<Coupon>> listNewCoupon(int currentPage, int pageSize);

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;向下,调用接口层向服务器请求数据。向上的Action中定义的方法类似如下:

public void getCustomer(String loginName, CallbackListener<Customer> 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。
后续扩展可以在这里添加缓存,但也要视不同情况而定,比如有些变化太快的数据,添加缓存就不太适合了。

界面层

界面层处于最上层,其核心就是负责界面的展示。因为公司有为不同商户定制不同app的需求,因此,这里就需要建立多个app的界面,这是一个很麻烦的事情,还好,Android Studio提供了很方便的方法可以大大减少工作量,主要通过设置Gradle,不同app可以添加不同的productFlavors。

界面层package的定义我也并不按照旧版的功能模块划分,而根据不同类型划分,主要分为以下几个包:

其中,activity、adapter、fragment各自都有一个基类,做统一的处理,比如定义了一些共用的常量、对象和方法等。
界面层是最复杂,最容易变得混乱不堪,最容易出问题的层级。所以,从架构到代码,很多东西都需要设计好,以及规范好,才能保证程序易维护、易扩展。后续的文章里将会详细分享下我在这方面的经验。

模型层

模型层横跨所有层级,封装了所有数据实体类,基本上也是跟json的obj数据一致的,在接口层会将obj转化为相应的实体类,再通过Action传到界面层。另外,模型层还定义了一些常量,比如用户状态、支付状态等。在Api里返回的是用1、2、3这样定义的,而我则用枚举类定义了这些状态。用枚举类定义,就可以避免了边界的检查,同时也更明了,谁会记得那么多1、2、3都代表什么状态呢。然而用枚举类定义的话,就必须能将1、2、3转化为相应的枚举常量。这里,我提供两种实现方式:
1.使用gson的@SerializedName标签,比如0为FALSE,1为TRUE,则可以如下定义:

public enum BooleanType {
@SerializedName("0")
FALSE,
@SerializedName("1")
TRUE
}

2.通过定义一个value,如下:

public enum BooleanType {
FALSE("0"),
TRUE("1"); private String value; BooleanType(String value) {
this.value = value;
} public String getValue() {
return value;
}
}

通过gson的方式,直接访问TRUE或FALSE就会自动序列化为1或0;如果通过第二种方式,因为没有序列化,则需要通过getValue方式获取1或0。

原文:http://keeganlee.me/post/android/20150605

Android 架构 1.架构的更多相关文章

  1. [转]Android App整体架构设计的思考

    1. 架构设计的目的 对程序进行架构设计的原因,归根到底是为了提高生产力.通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点, ...

  2. Android入门(二):Android的系统架构

    android的系统架构和其操作系统一样,采用了分层的架构.从架构图看,android分为四个层,从高层到低层分别是应用程序层.应用程序框架层.系统运行库层和linux核心层.   从技术方面看,An ...

  3. Android系统的架构

    android的系统架构和其操作系统一样,采用了分层的架构.从架构图看,android分为四个层,从高层到低层分别是应用程序层.应用程序框架层.系统运行库层和linux核心层. 1.应用程序 Andr ...

  4. fir.im Weekly - iOS/Android 应用程序架构解析

    假如问你一个iOS or Android app的架构,你会从哪些方面来说呢? 本期 fir.im Weekly 收集了关于  iOS/Android 开发资源,也加入了一些关于 Web 前端方面的分 ...

  5. Android 控件架构及View、ViewGroup的测量

    附录:示例代码地址 控件在Android开发的过程中是必不可少的,无论是我们在使用系统控件还是自定义的控件.下面我们将讲解一下Android的控件架构,以及如何实现自定义控件. 1.Android控件 ...

  6. Android 的系统架构

    Android 的系统架构 Android其本质就是在标准的Linux系统上增加了Java虚拟机Dalvik,并在Dalvik虚拟机上搭建了一个JAVA的application framework,所 ...

  7. Android App的架构设计:从VM、MVC、MVP到MVVM

    随着Android应用开发规模的扩大,客户端业务逻辑也越来越复杂,已然不是简单的数据展示了.如同后端开发遇到瓶颈时采用的组件拆分思想,客户端也需要进行架构设计,拆分视图和数据,解除模块之间的耦合,提高 ...

  8. Android系统四层架构分享

    Android系统四层架构 个人网站:http://www.51pansou.com Android视频下载:Android视频 Android源码下载:Android源码 如果把Android系统看 ...

  9. Android的系统架构

    转自Android的系统架构 从上图中可以看出,Android系统架构为四层结构,从上层到下层分别是应用程序层.应用程序框架层.系统运行库层以及Linux内核层,分别介绍如下:     1)应用程序层 ...

  10. 实时Android语音对讲系统架构

    本文属于Android局域网内的语音对讲项目系列,<通过UDP广播实现Android局域网Peer Discovering>实现了局域网内的广播及多播通信,本文将重点说明系统架构,音频信号 ...

随机推荐

  1. 前端面试:js闭包,为什么要使用闭包

    要理解闭包,首先理解javascript特殊的变量作用域,变量的作用于无非就是两种:全局变量,局部变量. javascript语言的特殊处就是函数内部可以读取全局变量. 1.如何从外部读取局部变量? ...

  2. js点亮星星评分并获取参数的js代码

    点亮星星评分后,点击按钮,立即获得分数参数值,方便不想使用ajax传参的朋友 http://demo.jb51.net/js/2014/jsxxdf/demo2.html <!DOCTYPE h ...

  3. C++ Review

    #include "iostream" #include "iomanip" #include "cstdio" using namespa ...

  4. 经典DFS问题 oilland 连通块

    #include "iostream" #include "cstdio" using namespace std; ][]={{,},{,-},{,},{-, ...

  5. 【数据结构】bzoj3747Kinoman

    Description 共有m部电影,编号为1~m,第i部电影的好看值为w[i]. 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部. 你可以选择l,r(1<=l< ...

  6. JavaWeb中session创建与销毁的问题

    今天遇到一个奇怪的问题,自己添加了一个session的监听,用来监听在线的人数.但打开浏览器时一直没有走进这个监听中来.最后百度找到了原因: 我们一直存在一个误区,javaweb中的session什么 ...

  7. Kuangbin带你飞 AC自动机

    模板: struct Ac_Automation { int ch[MAXNNODE][SIGMA_SIZE]; int val[MAXNNODE]; int fail[MAXNNODE],last[ ...

  8. xdebug参数说明

    ;;;;;;;;;;;;;;;;;;;;;;;;; Basic Features; xdebug基本功能,如堆栈跟踪,递归错误安全输出,时间内存跟踪等;;;;;;;;;;;;;;;;;;;;;;;;; ...

  9. python模块之os.path

    对文件路径的操作 os.path.split(p)函数返回一个路径的目录名和文件名. os.path.splitext():分离文件名与扩展名 os.path.isfile()和os.path.isd ...

  10. ECharts问题--散点图中对散点添加点击事件

    1. 我们这次就没有先讲解怎么使用散点图了,这个跟之前的一些图还是很类似的,不会的可以去官网上面查看 API 使用.我们这次讲解的是为散点图中的散点添加点击事件,然后在图表之外的一个 div 里面显示 ...