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將會在本系列文章中最後一篇給出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章