本篇接着上一篇讲解WebSocket客户端发送请求和服务端主动通知消息,还没看过第一篇的小伙伴,这里附上第一篇链接WebSocket安卓客户端实现详解(一)–连接建立与重连.
本篇依旧干货十足内容很多所以我们还是先热身下
客户端发送请求
为了方便我们协议使用json格式,当然你也可以替换成ProtoBuffer.
这里先展示下发送请求的流程图,大体流程在第一篇已经讲过了这里不再赘述,直接搂细节.
既然是请求,那么得有一个请求协议.这里我们不直接展示协议,而是通过http请求,引导到websocket请求协议.
一般http请求我们会需要如下几个参数
-
url
-
请求参数
-
回调
而websocket是一个长连接,我们所有的请求都是通过这个连接发送的,也就是说我们没法像http那样通过一个url来区分不同请求,那么我们请求协议中就必须要有一个类似url的东西去区分请求,这里我用action字段来表示url,为了方便协议的扩展,我们在加一个参数req_event,由action和req_event共同组成url,下面是一个简单的例子.
{
"action": " login",
"req_event": 0
}
由于请求是异步的所以当我们请求的时候需要把回调加入集合保存起来,等到服务端响应的时候我们需要从集合中找到对应回调调用对应的方法,那么为了能够找到对应回调我们需要给每个请求增加一个唯一标识,这里我用的seq_id字段表示,当请求的时候我们把客户端自己生成的seq_id传给服务端,然后当服务端响应的时候在回传给我们,这样我们就能通过seq_id作为key找到对应的回调,那么现在协议将变成下面这样.
{
"action": " login",
"req_event": 0,
"seq_id":0
}
在加上每个请求都有的请求参数,这里我用req字段表示,最终协议如下.
{
"action": " login",
"req_event": 0,
"seq_id":0,
"req":{
"aaa":"aaa",
"bbb":0
}
}
协议有了,那我们谈谈代码的实现,对于请求方法我们对外暴露如下参数.
- Action
这里我把action、req_event、响应实体统一用一个枚举类Action来存储,调用者只需根据不同请求传入对应Action即可.
- Req
请求参数
- Callback
回调
- timeout(可选参数,默认给的10秒)
请求超时时间.
show code
Action一个枚举类,把action、req_event、响应实体统一封装在一起,action和req_event用来组装url,响应实体在反序列化的时候会用到.调用者只需根据不同请求传入对应Action即可.
public enum Action {
LOGIN("login", 1, null);
private String action;
private int reqEvent;
private Class respClazz;
Action(String action, int reqEvent, Class respClazz) {
this.action = action;
this.reqEvent = reqEvent;
this.respClazz = respClazz;
}
public String getAction() {
return action;
}
public int getReqEvent() {
return reqEvent;
}
public Class getRespClazz() {
return respClazz;
}
}
ui层回调
public interface ICallback<T> {
void onSuccess(T t);
void onFail(String msg);
}
协议对应的请求实体类Request,这里还额外添加了一个没有参加序列化的参数reqCount来表示该请求的请求次数,在后面请求失败情况下对该请求的处理会用上.
public class Request<T> {
@SerializedName("action")
private String action;
@SerializedName("req_event")
private int reqEvent;
@SerializedName("seq_id")
private long seqId;
@SerializedName("req")
private T req;
private transient int reqCount;
public Request(String action, int reqEvent, long seqId, T req, int reqCount) {
this.action = action;
this.reqEvent = reqEvent;
this.seqId = seqId;
this.req = req;
this.reqCount = reqCount;
}
....
//这里还有各个参数对应get、set方法,为节省篇幅省略了
public static class Builder<T> {
private String action;
private int reqEvent;
private long seqId;
private T req;
private int reqCount;
public Builder action(String action) {
this.action = action;
return this;
}
public Builder reqEvent(int reqEvent) {
this.reqEvent = reqEvent;
return this;
}
public Builder seqId(long seqId) {
this.seqId = seqId;
return this;
}
public Builder req(T req) {
this.req = req;
return this;
}
public Builder reqCount(int reqCount) {
this.reqCount = reqCount;
return this;
}
public Request build() {
return new Request<T>(action, reqEvent, seqId, req, reqCount);
}
}
}
WsManager中发送请求代码
public class WsManager{
....跟之前相同代码省略.....
private static final int REQUEST_TIMEOUT = 10000;//请求超时时间
private AtomicLong seqId = new AtomicLong(SystemClock.uptimeMillis());//每个请求的唯一标识
public void sendReq(Action action, Object req, ICallback callback) {
sendReq(action, req, callback, REQUEST_TIMEOUT);
}
public void sendReq(Action action, Object req, ICallback callback, long timeout) {
sendReq(action, req, callback, timeout, 1);
}
/**
* @param action Action
* @param req 请求参数
* @param callback 回调
* @param timeout 超时时间
* @param reqCount 请求次数
*/
@SuppressWarnings("unchecked")
private <T> void sendReq(Action action, T req, ICallback callback, long timeout, int reqCount) {
if (!isNetConnect()) {
callback.onFail("网络不可用");
return;
}
Request request = new Request.Builder<T>()
.action(action.getAction())
.reqEvent(action.getReqEvent())
.seqId(seqId.getAndIncrement())
.reqCount(reqCount)
.req(req)
.build();
Logger.t(TAG).d("send text : %s", new Gson().toJson(request));
ws.sendText(new Gson().toJson(request));
}
....跟之前相同代码省略.....
}
sendReq(Action action, T req, ICallback callback, long timeout, int reqCount)
中有reqCount参数是因为在后面请求失败情况下会根据该请求进行请求的次数执行不同的策略.
超时和回调的处理
发送请求ok了接下来需要处理回调的问题了.虽然在方法sendReq(Action action, T req, ICallback callback, long timeout, int reqCount)
中我们已经传入了ui层的回调ICallback,但这里还需要在封装一层回调处理一些通用逻辑,然后再调用ICallback对应方法.
需要处理的通用逻辑有如下三个.
- 请求成功
将服务端返回数据从子线程传到主线程,然后调用ui层回调.
- 请求失败
将失败信息从子线程传到主线程,然后调用ui层回调.
- 请求超时
该请求的reqCount <= 3再次通过websocket发送请求,reqCount > 3通过http通道发送请求,根据结果直接调用对应回调.
中间层回调定义如下
public interface IWsCallback<T> {
void onSuccess(T t