前面已经把功能都写清楚了,这里稍做总结,并介绍一下使用方法。
小结
整个模块主要写了五个文件,如下:
- BaseRequest
- BaseResponse
- ResponseCallback
- ConnectService
- ConnectManager
其中 ResponseCallback 时回调接口,另外是请求基类和回复基类,ConnectService 负责 socket 的接收发送工作,ConnectManager 负责向外暴漏,并提供线程转换、超时控制等功能。
简单使用
这里简单说一下使用,涉及到数据协议的定义,无论是请求还是回复,类型如下:
- 0 - 4 消息类型
- 4 - 8 消息流水号
- 8 - 12 后面数据域长度
- 12 + 具体数据
由上面自定义的数据协议,我们来设计请求类、回复类,并简单使用下。
请求类
public class SwitchFanRequest extends BaseRequest {
public SwitchFanRequest(ResponseCallback presenter) {
this.requestMsgType = ID_FAN_REQ ;
this.responseMsgType = ID_FAN_RSP;
this.data = getByteData();
this.callback = presenter;
setStartTime(System.currentTimeMillis());
setThreshold(5000);
//isSendOut = false; //仅注册
//wantNumb = -1; //表示接收无限制
}
@Override
public byte[] getByteData() {
byte[] bytes = new byte[16];
ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.putInt(requestMsgType);
buffer.putInt(Utils.generateSerialNumber());
buffer.putInt(4);
//风扇开关 0 - 关 1 - 开
int open = MyApplication.config.isFanOpen ? 1 : 0;
buffer.putInt(open);
//log...
return bytes;
}
}
这是一个打开设备开关的请求,构造函数中设置数据,重写 getByteData 函数将数据转换成字节数组,可以在这打印日志.
回复类
public class SwitchFanResponse extends BaseResponse {
public int isOpen;
public String code;
public static SwitchFanResponse parseResponse(BaseResponse response) {
SwitchFanResponse switchFanResponse = new SwitchFanResponse();
switchFanResponse.responseMsgType = response.responseMsgType;
switchFanResponse.serialNumber = response.serialNumber;
switchFanResponse.data = response.data;
//转换数据
switchFanResponse.parseData();
return switchFanResponse;
}
@Override
protected void parseData() {
resultCode = 1;
//获取 int 型数据
isOpen = ByteBuffer.wrap(data,0, 4).getInt();
//获取字符串数据,包含中文
try {
Charset charset = Charset.forName("GB2312");
CharsetDecoder decoder = charset.newDecoder();
code = decoder.decode(ByteBuffer.wrap(data, 4, 100)).toString();
if (code.contains("\0")){
code = code.substring(0, code.indexOf("\0"));
}
} catch (CharacterCodingException e) {
e.printStackTrace();
}
//log...
}
}
这是一个打开设备开关的回复,因为我不想每个回复类都去序列化,所以基类序列化后,通过基类得到对应子类就可以。使用静态的 parseResponse 方法,将基类转化为子类,使用的时候自己注意下别用错子类了就可以。
为了演示,我这假设会回复一些数据,大致就是 int 型和字符串类型吧,使用方法看代码。
初始化
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
bindConnectService();
}
@Override
public void onTerminate() {
unbindConnectService();
super.onTerminate();
}
private void bindConnectService() {
ConnectManager.getInstance().bindConnectService(this);
//HxzSimulateUtil.startSimulate();
}
private void unbindConnectService() {
ConnectManager.getInstance().unbindConnectService(this);
HxzManager.getInstance().unbindConnectService(context);
}
直接再 Application 中启动 ConnectService,这样就可以发送消息了。
发送消息
ConnectManager manager = ConnectManager.getInstance();
//获取初始化参数
manager.setUpSocketConfigure(getSocketConfigure());
manager.sendMessage(new ConnectRequest(new ResponseCallback() {
@Override
public void onResponse(BaseResponse response) {
//从基类回复转换,得到具体子类回复
ConnectResponse response = ConnectResponse.parseResponse(response);
mLoginView.onLogin(response.xxx);
}
//默认方法,可写可不写,看需要
// @Override
// public void onError(int requestMsgType) {
// mLoginView.onError();
// }
//
// @Override
// public void onTimeout(int requestMsgType) {
// mLoginView.onTimeout();
// }
}));
//获取初始化参数
private Bundle getSocketConfigure() {
Bundle bundle = new Bundle();
bundle.putString("IP", "192.168.1.110");
bundle.putInt("PORT", 2050);
bundle.putInt("WAIT", 5000);
byte[] bytes = new byte[12];
ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.putInt(ID_CONNECT_REQ);
buffer.putInt(Utils.generateSerialNumber());
buffer.putInt(0);
bundle.putByteArray("ConnectData", bytes);
return bundle;
}
这里就是拿到 ConnectManager 后,初始化连接参数(一次就好),并发送请求,上面是连接时候的用法,后面就简单多了:
ConnectManager.getInstance().sendMessage(new SwitchFanRequest(response -> {
SwitchFanResponse res = SwitchFanResponse.parseResponse(response);
int isOpen = res.isOpen;
String code = res.code;
...
}));
结合默认方法可写可不写,onError、onTimeout 不写之后,配合 lambda 表达式,几行代码就可以搞定 socket 通信,看上面代码简单不简单!!!
结语
这里就是整个 socket 通信模块的使用了,我们一步一步将各个功能实现,最终将这个 socket 通信缩短为几行代码,还是挺有成就感的。
end