前面已经把功能都写清楚了,这儿稍做总结,并介绍一下运用办法。

小结

整个模块主要写了五个文件,如下:

  1. BaseRequest
  2. BaseResponse
  3. ResponseCallback
  4. ConnectService
  5. ConnectManager

其间 ResponseCallback 时回调接口,另外是恳求基类和回复基类,ConnectService 负责 socket 的接纳发送工作,ConnectManager 负责向外暴漏,并提供线程转化、超时操控等功能。

简略运用

这儿简略说一下运用,涉及到数据协议的界说,无论是恳求仍是回复,类型如下:

  1. 0 – 4 音讯类型
  2. 4 – 8 音讯流水号
  3. 8 – 12 后边数据域长度
  4. 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