什么是MessagePack
官方msgpack官网用一句话归来:It’slikeJSON.butfastandsmall.
浅陋来讲,它的数据口头与json访佛,可是在存储时对数字、多字节字符、数组等都做了许多优化,减少了无须的字符,二进制口头,也保证不消字符化带来特等的存储空间的加多。以下是官网给出的浅陋示例图:
图上这个json长度为27字节,可是为了清楚这个数据结构,它用了9个字节(即是那些大括号、引号、冒号之类的,他们是白白多出来的)来清楚那些特等添加的无真义数据。msgpack的优化在图上展示的也比拟了了了,省去了颠倒标志,用特定编码对多样类型进行界说,比如上图的A7,其中前四个bitA即是清楚str的编码,况且它清楚这个str的长度只用半个字节就不错清楚了,也即是背面的7,因此A7的真义即是清楚背面是一个7字节长度的string。
有的同学就会问了,对于长度大于15(二进制1111)的string怎样清楚呢?这就要看messagepack的压缩旨趣了。
MessagePack的压缩旨趣
中枢压缩方式可参看官方领会messagepackspecification
空洞来讲即是:
true、false之类的:这些太浅陋了,胜利给1个字节,(0xc3清楚true,0xc2清楚false)
不消清楚长度的:即是数字之类的,他们自然是定长的,是用一个字节清楚背面的内容是什么,比如用(0xcc清楚这背面,是个uint8,用oxcd清楚背面是个uint16,用0xca清楚背面的是个float32)。对于数字做了进一步的压缩处理,字据大小遴遴聘更少的字节进行存储,比如一个长度
不定长的:比如字符串、数组、二进制数据(bin类型),类型背面加1~4个字节,用来存字符串的长度,淌若是字符串长度是256以内的,只需要1个字节,MessagePack能存的最长的字符串,是(2^32-1)最长的4G的字符串大小。
高等结构:MAP结构,即是k-v结构的数据,和数组差未几,加1~4个字节清楚背面有若干个项
Ext结构:清楚特定的小单位数据。也即是用户自界说数据结构。
咱们看一下官方给出的stringformat暗示图
对于上头的问题,一个长度大于15(也即是长度无法用4bit清楚)的string是这样清楚的:用指定字节0xD9清楚背面的内容是一个长度用8bit清楚的string,比如一个160个字符长度的字符串,它的头信息就不错清楚为D9A0。
这里值得一提的是Ext膨大口头,恰是这种结构才保证了messagepack的完备性,因为施行的数据接口中自界说结构曲直常常见的,浅陋的已知数据类型和高等结构map、array等并不行称心需求,因此需要一个膨大口头来与之合作。比如一个底下的接口口头:
{
"error_no":0,
"message":"",
"result":{
"data":[
{
"datatype":1,
"itemdata":
{//共有字段45个
"sname":"微医",
"packageid":"330611",
…
"tabs":[
{
"type":1,
"f":"abc"
},
…
]
}
},
…
],
"hasNextPage":true,
"dirtag":"soft"
}
}
怎样把tabs中的子数据动作一个举座写入itemdata这个结构中呢?itemdata又怎样写入它的表层数据结构data中?这时Ext出马了。咱们不错自界说一种数据类型,指定它的Type值,当融会际遇这个type时就按咱们自界说的结构去融会。具体怎样已毕背面咱们在代码示例的工夫会讲到。
MessagePack的源码
Github地址
从这里也能看到它对多样说话的扶植:c、java、ruby、python、php...
感酷好的不错我方阅读,比拟浅陋易懂,这里不再赘述,色性欧美底下要点讲一下具体用法。
Androidstudio中如何使用MessagePack
领先需要在app的gradle剧本中添加依赖
compile'org.msgpack:msgpack-core:0.8.11'
Java版块用法的sample不错在源码的/msgpack-java/msgpack-core/src/test/java/org/msgpack/core/example/MessagePackExample.java中看到。
值得一提的是官方的领会文档还停留在1.x版块,忽视全球胜利去看最新demo。
通过MessagePack这个facade赢得用户可用的对象packer和unpacker。
1.数据打包
主要有两种用法:
通过MessageBufferPacker将数据打包到内存buffer中
MessageBufferPackerpacker=MessagePack.newDefaultBufferPacker;
packer.packInt(1).packString("leo")
//packarrays
int[]arr=newint[]{3,5,1,0,-1,255};
packer.packArrayHeader(arr.length);
for(intv:arr){
packer.packInt(v);
}
//packmap(key->value)elements
packer.packMapHeader(2);//thenumberof(key,value)pairs
//Put"apple"->1
packer.packString("apple");
packer.packInt(1);
//Put"banana"->2
packer.packString("banana");
packer.packInt(2);
//packbinarydata
byte[]ba=newbyte[]{1,2,3,4};
packer.packBinaryHeader(ba.length);
packer.writePayload(ba);
packer.close;
以上分歧展示了对基本数据类型、array数组、map、二进制数据的打包用法。
通过MessagePacker将数据胜利打包输出流
FiletempFile=File.createTempFile("target/tmp",".txt");
tempFile.deleteOnExit;
//Writepackeddatatoafile.NoneedexiststowrapthefilestreamwithBufferedOutputStream,sinceMessagePackerhasitsownbuffer
MessagePackerpacker=MessagePack.newDefaultPacker(newFileOutputStream(tempFile));
//以下是对自界说数据类型的打包
byte[]extData="customdatatype".getBytes(MessagePack.UTF8);
packer.packExtensionTypeHeader((byte)1,extData.length);//typenumber[0,127],databytelength
packer.writePayload(extData);
packer.close;
领先通过packExtensionTypeHeader将自界说数据类型的type值和它的长度写入,这里指定这段数据的type=1,长度即是转为二进制数据后的长度,这里官方demo里有个装假,写了固定长度10,其实是有问题的,这里进行了修正写入extData的施行长度。然后用writePayload尺度将byte[]数据写入。收尾。可能这个Demo的展示还有点不太好清醒,咱们就上头的json面目进行进一步领会:假定我要将tabs下的数据面目界说为一个膨大类型,怎样去写呢?
领先界说一个这样的数据结构:
publicclassTabsJson{
publicinttype;
publicStringf="";
}
然后指定TabsJson对象的typeExtType.TYPE_TAB=2,官方对自界说数据类型的收尾是0~127。
然后对TabsJson对象进行动手化和赋值:
TabsJsontabsjson=newTabsJson;
tabsjson.type=199;
tabsjson.f="abc";
然后构造MessagePacker进行写入
privatestaticvoidpackTabJson(TabsJsontabsJson,MessagePackerpacker)throwsIOException{
MessageBufferPackerpacker1=MessagePack.newDefaultBufferPacker;
packer1.packInt(tabsJson.type);
packer1.packString(tabsJson.f);
intl=packer1.toByteArray.length;
packer.packExtensionTypeHeader(ExtType.TYPE_TAB,l);
packer.writePayload(packer1.toByteArray);
packer1.close;
}
packer1的作用即是将tabsjson对象打包成二进制数据,然后咱们将这个二进制数据写到packer中。处罚。那解包的工夫怎样做呢,背面咱们会讲到。
这样通过自界说数据结构层层打包就完好解决了上濒临于怎样将数据打包为复杂json面目的问题了。
必须凝视打包收尾后必须进行close,以收尾这次buffer操作八成关闭输出流。
2.数据解包
两种用法与上头打包是对应的:
胜利对二进制数据解包
MessageUnpackerunpacker=MessagePack.newDefaultUnpacker(bytes);
intid=unpacker.unpackInt;//1
Stringname=unpacker.unpackString;//"leo"
intnumPhones=unpacker.unpackArrayHeader;//2
String[]phones=newString[numPhones];
for(inti=0;i
phones[i]=unpacker.unpackString;//phones={"xxx-xxxx","yyy-yyyy"}
}
intmaplen=unpacker.unpackMapHeader;
for(intj=0;j
unpacker.unpackString;
unpacker.unpackInt;
}
unpacker.close;
需要凝视的是解包法必然须与打包法例一致,不然会出错。也即是说契约口头的爱戴要靠两头手写代码进行保证,而这是很不安全的。
对输入流进行解包
FileInputStreamfileInputStream=newFileInputStream(newFile(filepath));
MessageUnpackerunpacker=MessagePack.newDefaultUnpacker(fileInputStream);
//先将自界说数据的讯息头读出
ExtensionTypeHeaderet=unpacker.unpackExtensionTypeHeader;
//判断讯息类型
if(et.getType==(ExtType.TYPE_TAB)){
intlenth=et.getLength;
//按长度读取二进制数据
byte[]bytes=newbyte[lenth];
unpacker.readPayload(bytes);
//构造tabsjson对象
TabsJsontab=newTabsJson;
//构造unpacker将二进制数据解包到java对象中
MessageUnpackerunpacker1=MessagePack.newDefaultUnpacker(bytes);
tab.type=unpacker1.unpackInt;
tab.f=unpacker1.unpackString;
unpacker1.close;
}
unpacker.close;
以上例子展示了对自界说数据类型的完整解包经由,终末不要健忘关闭unpacker。
除此除外用户还不错自界说packconfig和unpackconfig,指定打包息争包时的设置,比如内存缓存byte[]数据大小等等。
3.其他杂谈
淌若想省去如斯繁琐的pack、unpack动作,而又想用messagepack,不错做到么?自然不错,咱们不错愚弄javabean的序列化功能,将对象序列化为二进制,然后总计写入到messagepack中。
比如以上的TabsJson对象,在android中咱们已毕Parcelable接口以达到序列化的联想
到了马重洋这里,三个儿子的脾性各不相同,想要的东西也大不一样,不论马重洋怎么分配,始终无法让三人达成共识。不仅如此,本来和平共处的三兄弟还经常因为这件事掐架,搅得家中不得安宁。
publicclassTabsJsonimplementsParcelable{
publicinttype;
publicStringf="";
publicTabsJson{
}
protectedTabsJson(Parcelin){
this.type=in.readInt;
this.f=in.readString;
}
@Override
publicvoidwriteToParcel(Parceldest,intflags){
dest.writeInt(this.type);
dest.writeString(this.f);
}
@Override
publicintdescribeContents{
return0;
}
publicstaticfinalCreatorCREATOR=newCreator{
@Override
publicTabsJsoncreateFromParcel(Parcelin){
returnnewTabsJson(in);
}
@Override
publicTabsJson[]newArray(intsize){
returnnewTabsJson[size];
}
};
}
打包息争包经由是这样的
MessageBufferPackerpacker=MessagePack.newDefaultBufferPacker;
Parcelpc=Parcel.obtain;
tabsjson.writeToParcel(pc,Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
byte[]bytes=pc.marshall;
//先写入数据长度
packer.packInt(bytes.length);
//写入二进制数据
packer.writePayload(bytes);
packer.close;
pc.recycle;
//解包
MessageUnpackerunpacker=MessagePack.newDefaultUnpacker(packer.toByteArray);
byte[]bytes1=newbyte[unpacker.unpackInt];
unpacker.readPayload(bytes1);
Parcelpp=Parcel.obtain;
pp.unmarshall(bytes1,0,bytes1.length);
pp.setDataPosition(0);
TabsJsonij=TabsJson.CREATOR.createFromParcel(pp);
pp.recycle;
unpacker.close;
这种方式固然省去了我方手写打包息争包的经由,可是不推选使用。
笔者对第一部分示例的json数据,合并个itemdata数据段两种方式打包后文献大小对比如下:
parcel方式胜利操作Json数据
数据大小(byte)361926444090
可见parcel方式在压缩后果上比原始的json数据口头并无较大耕作,因此不忽视使用。
一句话归来一下Messagepack
浅陋好用夜夜爽夜夜叫夜夜高潮,支配旨趣后不错想怎样用怎样用。是比Json更简短更纯果真一种数据契约。