Netty在Android开发中的应用实战系列(二)——— Encoder | Decoder | Handler 的使用

阅读本文建议从第一篇开始往后看

本系列文章

一、Encoder的作用

将发送的数据进行编码成需要的数据格式,然后发送出去

二、Decoder的作用

将收到的数据根据数据协议进行解码,然后处理

三、Handler的作用

将解码好的数据进行处理

四、下面们通过一个简单的一个示例进行收发数据

  • 定义一个传输的数据包格式
包头 命令字 数据长度 数据区 包尾
0x2A 一个字节 一个字节 数据字符串 0x2A
  • 根据上面定义的数据包,便可以生成对应的数据实体类;如下:
  • 数据实体类
public class PkgDataBean {
    //命令字
    private byte cmd;
    //数据长度
    private byte dataLength;
    //数据
    private String data;
	
	//省略get/set函数
}
  • 客户端发送数据就可以直接写入PkgDataBean对象,然后在ClientEncoder中编码
  • 发送数据
//获取与服务端的连接通道
Channel channel = NettyClient.getChannel();
PkgDataBean bean = new PkgDataBean();
bean.setCmd((byte) 0x01);
bean.setData(etContent.getText().toString());
bean.setDataLength((byte) bean.getData().getBytes().length);
//写入数据
channel.writeAndFlush(bean);
  • 对数据进行编码,将对象转成字节数组。可以注意到这里我们将泛型直接使用的是定义的实体类
public class ClientEncoder extends MessageToByteEncoder<PkgDataBean> {

    private static final String TAG = "ClientEncoder";

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, PkgDataBean data, ByteBuf byteBuf) throws Exception {
        //根据数据包协议,生成byte数组
        byte[] bytes = {0x2A, data.getCmd(), data.getDataLength()};
        byte[] dataBytes = data.getData().getBytes();
        //将所有数据合并成一个byte数组
        byte[] all = ByteUtil.byteMergerAll(bytes, dataBytes, new byte[]{0x2A});

        //发送数据
        byteBuf.writeBytes(all);
    }
}
  • 程序执行的效果如下:
    在这里插入图片描述

五、在客户端发送数据我们使用了Encoder进行编码,那么服务端要接收这些数据就需要在Decoder中进行解码,这样就能够拿到正确的数据了

  • 服务端的Decoder实现,将数据解码生成PkgDataBean实体
public class ServerDecoder extends ByteToMessageDecoder {
    private static final String TAG = "ServerDecoder";

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        int length = byteBuf.readableBytes();
        //收到的数据包
        byte[] data = byteBuf.readBytes(length).array();
        //判断数据包是不是一个正确的数据包
        if (data[0] == 0x2A && data[0] == data[data.length - 1]) {
            PkgDataBean bean = new PkgDataBean();
            bean.setCmd(data[1]);
            bean.setDataLength(data[2]);
            byte[] bytes = Arrays.copyOfRange(data, 3, 3 + bean.getDataLength());
            bean.setData(new String(bytes));
            Log.d(TAG, "收到了客户端发送的数据:" + bean.toString());
        }
    }
}

  • 运行的效果
    在这里插入图片描述

六、上面我们说到的都是客户端发送数据,服务端接收数据;在实际开发中肯定都是双方都是有数据发送和接收的,所以双方都会有EncoderDecoder进行编解码,用法都是一样的这里就不累赘阐述了

七、在解码器中我们将数据解析出来了,那么就需要使用到业务中了;也就是在Handler中处理,所以Handler可以说是我们的数据处理中心、包括重连、心跳、客户端上下线等等…(这些后面的文章会讲到)

  • 先介绍一下重点的几个方法

    • channelRead0()——>当收到数据的回调
    • channelActive()——>有客户端连接过来的回调
    • channelInactive()——>有客户端断开了连接的回调
    • userEventTriggered()——>空闲事件的回调
    • exceptionCaught()——>发生异常的回调
  • Handler中的数据从哪传过来的呢?这个当然是从Decoder中来的了,如下:

@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
    int length = byteBuf.readableBytes();
    //收到的数据包
    byte[] data = byteBuf.readBytes(length).array();
    //判断数据包是不是一个正确的数据包
    if (data[0] == 0x2A && data[0] == data[data.length - 1]) {
        PkgDataBean bean = new PkgDataBean();
        bean.setCmd(data[1]);
        bean.setDataLength(data[2]);
        byte[] bytes = Arrays.copyOfRange(data, 3, 3 + bean.getDataLength());
        bean.setData(new String(bytes));
        Log.d(TAG, "收到了客户端发送的数据:" + bean.toString());
        //将数据传递给下一个Handler,也就是在NettyServer给ChannelPipeline添加的处理器
        list.add(bean);
    }
}
  • 将数据解析完毕后使用list.add(),将数据传递至Handler
  • Handler的代码如下:
public class ServerHandler extends SimpleChannelInboundHandler<PkgDataBean> {

    private static final String TAG = "ServerHandler";

    /**
     * 当收到数据的回调
     *
     * @param channelHandlerContext 封装的连接对像
     * @param bean
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, PkgDataBean bean) throws Exception {
        Log.d(TAG, "收到了解码器处理过的数据:" + bean.toString());
    }
}
  • 运行的效果
    在这里插入图片描述
  • 这里也同样接受一个泛型需要注意的是这里指定的泛型必须与Decoder中list.add()的类型一致,否则会出错!

八、下一篇文章将着重对断线重连、心跳处理进行讲解说明

Demo将会在本系列文章第四篇文章中给出

发布了140 篇原创文章 · 获赞 546 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/a_zhon/article/details/100831777