自定义 Socket 通信模块简单使用

自定义安卓 socket 通信模块系列

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

小结

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

  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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值