691 字
3 分钟
Kotlin中操作二进制数据

因为自己写的kwsify是通过过纯文本json发送数据帧的如果想发送二进制数据的话就没办法了于是我就了解了一下kotlin中操作二进制数据的方法下面就是具体思路

  1. 首先需要编码/解码二进制需要一个数据载体这个载体就是ByteBuffer(当然也可以是DataOutputStream)这里为了更清楚的操作二进制我就使用ByteBuffer来操作
  2. 还需要一个数据类来存放解码后/编码前的人类可读数据
data class OPCodePacket(
    val op: Int,
    val body: String
) {
    fun toByteArray(): ByteArray {
        val bodySize = body.toByteArray().size
        val body = body.toByteArray()
        val totalLength = 4 + 4 + bodySize
        val buffer = ByteBuffer.allocate(totalLength).apply {
            putInt(op)
            putInt(bodySize)
            put(body)
        }
        return buffer.array()
    }

    companion object {
        fun fromByteArray(buffer: ByteBuffer): OPCodePacket {
            buffer.flip()
            val op = buffer.int
            val bodySize = buffer.int
            val body = ByteArray(bodySize).apply {
                buffer.get(this)
            }
            return OPCodePacket(op, String(body))
        }
    }
}

ByteBuffer放入和存取数据, 内存指针/游标: position都会自动偏移到放入大小的位置, 假如 一个ByteBuffer的容量capacity8, Int类型占用的字节数是4那么放入一个Int类型后指针就会偏移到 4, 还剩下4个字节的容量

编码#

上面的toByteArray方法是将一个构造好的数据类变成ByteArray, 其中需要计算一个ByteBuffer的容量, 由于需要放入op,并且op是Int类型所以占用了4个字节, 还需要计算出body占用的字节的长度这个长度还是一个Int,所以还需要+4, 最后放入若干长度的body占用的字节的长度最后就是4 + 4 + body.toByteArray().size 然后分配一个空的ByteBuffer容器,最后依次按照顺序放入对应的数据

解码#

上面说了因为ByteBuffer存/取数据都会自动偏移指针, 所以不需要手动指定偏移位置, 只需要按照顺序取出对应的类型即可。 先将ByteBuffer设置为模式, 使用flip()方法设置, 然后取出Int类型的op, 再取出Int类型的body的长度,然后创建一个空的ByteArray容量就是上一个步骤取出的body的长度,最后直接使用get方法取出对应长度的二进制数据并自动将数据放入刚才新建的ByteArray数组内, 最后创建一个数据类实体存入读取后的数据

上面的解码代码直接使用int getter是因为kotlin自动把getInt()这个getter优化成了int所以还是等价于getInt()方法的

工具函数#

再给出一个对ByteBuffer拓展了放入Boolean值和取出Boolean值的拓展函数

fun ByteBuffer.putBoolean(value: Boolean) {
    put(if (value) 1.toByte() else 0.toByte())
}

fun ByteBuffer.getBoolean(): Boolean {
    return get() == 1.toByte()
}