AMF序列化技巧
AMF主要是在存储配置上很牛,但是还有很多小技巧可以让它更好。今天此文就是告诉你如何自定义对象的序列化和解序列化来让文件更小并且获得最大的控制。
AMF格式产生的文件比JSON和XML格式的都要小的多,但它主要不是因为减小文件大小而出名。AMF在一般对象上处理的还是不错的,但是很多情况我们需要关于特殊对象的具体信息。例如:一个元素的类Element。
1 class Element
2 {
3 public var symbol:String;
4 public var atomicNumber:uint;
5 }
很不幸的是AS3不允许我们使用小整型, 32位足够元素数量的范围(0-119)。即使考虑新发现的元素,一个字节就可以处理0-255的范围并提供足够的增长空间。
虽然AS3不提供小整型,我们可以在Bytearray或者IDataOutPut里存储小的整数值。我们可以使用任何类来实现flash.utiles.IExternalizable接口和自定义任何类的序列化。我们不需要依赖AMF默认的序列化操作,相反我们可以提供一个public function writeExternal(out:IDataOutput):void函数来写入任何数据,一个public function readExternal(in:IDataInput):void函数来读入任何需要解序列化的数据。
这样我们就有一个很方便的方式来自定义任何类的序列化格式并仍然可以使用AMF的ByteArray.writeObject/readObject。当然我们不需要自定义所有的类,只定义我们选择的即可。而你的类的大小跟默认AMF格式的也差不多,但是当你想优化的时候只需要自定义一个或两个需要的类即可。
在Element类中,我们自定义了方式,这样只会在序列化元素数值这少量的字节。
1 class ElementIEByte implements IExternalizable
2 {
3 public var symbol:String;
4 public var atomicNumber:uint;
5
6 public function writeExternal(output:IDataOutput): void
7 {
8 output.writeUTF(symbol);
9 output.writeByte(atomicNumber);
10 }
11
12 public function readExternal(input:IDataInput): void
13 {
14 symbol = input.readUTF();
15 atomicNumber = input.readUnsignedByte();
16 }
17 }
理论上这可以为每个序列化的Element节省三个字节。来测试下,然后在另一个版本里使用IExternalizable并写全32为的整型值。
1 package
2 {
3 import flash.net.registerClassAlias;
4 import flash.utils.ByteArray;
5 import flash.display.*;
6 import flash.text.*;
7
8 public class TestIExternalizable extends Sprite
9 {
10 private var __logger:TextField = new TextField();
11 private function row(...cols): void
12 {
13 __logger.appendText(cols.join(",")+"\n");
14 }
15
16 public function TestIExternalizable()
17 {
18 stage.align = StageAlign.TOP_LEFT;
19 stage.scaleMode = StageScaleMode.NO_SCALE;
20
21 __logger.autoSize = TextFieldAutoSize.LEFT;
22 addChild(__logger);
23
24 row("Serializing to check size...");
25 row();
26 row("Class", "Size");
27
28 registerClassAlias("ElementNormal", Element);
29 registerClassAlias("ElementIExByt", ElementIEByte);
30 registerClassAlias("ElementIExInt", ElementIEInt);
31
32 var elem:Element = new Element();
33 elem.symbol = "H";
34 elem.atomicNumber = 1;
35 var bytesElement:ByteArray = new ByteArray();
36 bytesElement.writeObject(elem);
37 row("Element size", bytesElement.length);
38
39 var elemIEInt:ElementIEInt = new ElementIEInt();
40 elemIEInt.symbol = "H";
41 elemIEInt.atomicNumber = 1;
42 var bytesElemInt:ByteArray = new ByteArray();
43 bytesElemInt.writeObject(elemIEInt);
44 row("ElementIEInt size", bytesElemInt.length);
45
46 var elemIEByte:ElementIEByte = new ElementIEByte();
47 elemIEByte.symbol = "H";
48 elemIEByte.atomicNumber = 1;
49 var bytesElemByte:ByteArray = new ByteArray();
50 bytesElemByte.writeObject(elemIEByte);
51 row("ElementIEByte size", bytesElemByte.length);
52
53 row();
54 row("Deserializing to check integrity...");
55 row();
56
57 bytesElement.position = 0;
58 var elemCheck:Element = bytesElement.readObject() as Element;
59 row(
60 "Element",
61 (elem.symbol == elemCheck.symbol
62 && elem.atomicNumber == elemCheck.atomicNumber)
63 ? "pass"
64 : "fail"
65 );
66
67 bytesElemInt.position = 0;
68 var elemIntCheck:ElementIEInt = bytesElemInt.readObject() as ElementIEInt;
69 row(
70 "ElementIEInt",
71 (elemIEInt.symbol == elemIntCheck.symbol
72 && elemIEInt.atomicNumber == elemIntCheck.atomicNumber)
73 ? "pass"
74 : "fail"
75 );
76
77 bytesElemByte.position = 0;
78 var elemByteCheck:ElementIEByte = bytesElemByte.readObject() as ElementIEByte;
79 row(
80 "ElementIEByte",
81 (elemIEByte.symbol == elemByteCheck.symbol
82 && elemIEByte.atomicNumber == elemByteCheck.atomicNumber)
83 ? "pass"
84 : "fail"
85 );
86 }
87 }
88 }
89 import flash.utils.IDataInput;
90 import flash.utils.IDataOutput;
91 import flash.utils.IExternalizable;
92
93 class Element
94 {
95 public var symbol:String;
96 public var atomicNumber:uint;
97 }
98
99 class ElementIEInt implements IExternalizable
100 {
101 public var symbol:String;
102 public var atomicNumber:uint;
103
104 public function writeExternal(output:IDataOutput): void
105 {
106 output.writeUTF(symbol);
107 output.writeUnsignedInt(atomicNumber);
108 }
109
110 public function readExternal(input:IDataInput): void
111 {
112 symbol = input.readUTF();
113 atomicNumber = input.readUnsignedInt();
114 }
115 }
116
117 class ElementIEByte implements IExternalizable
118 {
119 public var symbol:String;
120 public var atomicNumber:uint;
121
122 public function writeExternal(output:IDataOutput): void
123 {
124 output.writeUTF(symbol);
125 output.writeByte(atomicNumber);
126 }
127
128 public function readExternal(input:IDataInput): void
129 {
130 symbol = input.readUTF();
131 atomicNumber = input.readUnsignedByte();
132 }
133 }
在最后通过时所有的解序列化都会检查。下面是大小的比较结果:
1 Class
2
3 Size
4
5 Element size
6
7 41
8
9 ElementIEInt size
10
11 23
12
13 ElementIEByte size
14
15 20
很明显,不使用默认的AMF序列化,直接写IDataOutput本身可以提供更小的文件大小。上图中,最后一个版本中使用字节而不是int,我们可以看到就像理论上预测的一样,我们介绍了额外的三个字节。
总结下,如果你使用AMF来序列化类对象,并且想缩小文件大小,可以考虑使用IExternalizable来自定义序列化和解序列化处理过程。
发现bug或者有什么疑问跟建议都可以评论哦!
原文链接:http://jacksondunstan.com/articles/2248