今天我们来谈谈客户端对通讯协议的处理,主要分为三部分:约定响应数据格式,响应数据的自动映射以及错误处理三部分。由于数据协议采用json的居多,因此我们在此基础上进行说明。
约定响应数据格式
协议格式
通常来说,你拿到的设计文档中会存在通信协议的说明,对于客户端来说,一个良好的通信协议需要能描述操作状态(操作码+操作提示)以操作结果,因此,常见的响应数据的格式如下:
{
"code": 0,
"msg": "正常",
"data": {
"id": 1,
"account": "121313",
"accountName": "alipay",
"income": "600.000000"
}
}
code定义
code为我们自定义的操作状态码,首先来看我们常用的定义:
code | 说明 |
---|---|
0 | 操作成功的消息提示 |
1 | 客户端认证失败,一般是客户端被恶意修改 |
2 | 用户认证失败 |
3 | 提交参数错误:参数缺失、参数名不对等 |
4 | 提交参数校验失败,一般是提交给服务端的数据格式有误,多发生在表单提交的场景中 |
5 | 自定义错误,服务端发生不可恢复的错误等 |
msg定义
msg为服务器端返回的操作信息。
无论操作成功与否,客户端都应该根据业务给出准确的提示,客户端则根据实际情况选择展示与否。
data 定义
data则是请求返回的具体内容,通常data根据请求接口的不同最终会被解析成不同的实体类。
示例
下面我们以获取消息列表和消息详情两个接口返回的响应数据作为示例:
消息列表:
{
"code": 0,
"data": {
"list": [
{
"content": "你参加的活动已经开始了...",
"createtime": "2016-09-23 16:44:02",
"id": "4480",
"status": 0,
"title": "活动开始",
"type": "1"
},
{
"content": "你参加的活动已经结束...",
"createtime": "2016-09-19 14:30:02",
"id": "4444",
"status": 0,
"title": "活动结束",
"type": "1"
}
],
"total": 2
},
"msg": "正常"
}
消息详情
{
"code": 0,
"data": {
"detail":
{
"content": "你参加的活动已经开始了,请准时到你的活动中去执行",
"createtime": "2016-09-23 16:44:02",
"id": "4480",
"status": 0,
"title": "活动开始",
"type": "1"
},
},
"msg": "正常"
}
响应数据映射实体数据模型
当我们接受到如上格式的响应数据时,下面便是考虑如何应用的问题,也就是如何将协议转换?是在获取响应的时候自动转换还是手动转换?转换成java实体类还是String?
“偷懒”是程序员的天性,我们当然不希望花费时间在这种无创造性的工作上,所以我们考虑在收到响应的时候直接将其转换为java实体类。
确定了我们的目标之后,接下来,首要任务是对数据协议进行抽象?什么叫做数据协议抽象?
所谓的数据协议抽象就是根据聚合性,通用性,隔离性三原则将整个数据协议进行切分复用,以便更好的映射成我们需要的数据模型。
我们对刚才约定的数据协议格式进行协议抽象后,可以拿到类似以下的实体模型:
public class Result<T> {
private int code;
private String msg;
private T data;
//...set和get方法
}
Result做为所有响应模型的公共基类,其中的code,msg,data分别用来映射我们通信协议。其中,泛型化的data确保接受不同的实体模型,可以看出,我们通过数据协议抽象之后,最终得到了一个良好的数据模型。
为了下面的需要我们一同将消息列表和消息详情的实体类放上来:
public class message{
private String content;
private String createtime;
private String id;
private int status;
private String title;
private String type;
//...set和get方法
}
public class messageList {
private int total;
private List<Message> list;
//...set和get方法
}
现在来看看我们理想的获取消息列表和获取消息详情的接口应该是什么样的:
@GET("/user/message/list")
Call<Result<MessageList>> getMessageList(@Query("page") int page);
@GET("/user/message")
Call<Result<Message>> getMessage(@Query("mid") int