对于目前的状况来说,移动终端的网络状况没有PC网络状况那么理想。在一个Android应用中,如果需要接收来自服务器的大容量数据,那么就不得不考虑客户的流量问题。本文根据笔者的一个项目实战经验出发,解决大容量数据的交互问题,解决数据大小会根据实际情况动态切换问题(服务器动态选择是否要压缩数据,客户端动态解析数据是否是被压缩的),还有数据交互的编码问题。
1 package com.chenjun.utils.compress;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.InputStream;
6 import java.io.OutputStream;
7 import java.util.zip.GZIPInputStream;
8 import java.util.zip.GZIPOutputStream;
9
10 public class Compress {
11 private static final int BUFFER_LENGTH = 400;
12
13
14 //压缩字节最小长度,小于这个长度的字节数组不适合压缩,压缩完会更大
15 public static final int BYTE_MIN_LENGTH = 50;
16
17
18 //字节数组是否压缩标志位
19 public static final byte FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY = 0;
20 public static final byte FLAG_GBK_STRING_COMPRESSED_BYTEARRAY = 1;
21 public static final byte FLAG_UTF8_STRING_COMPRESSED_BYTEARRAY = 2;
22 public static final byte FLAG_NO_UPDATE_INFO = 3;
23
24
31 public static void compress(InputStream is, OutputStream os)
32 throws Exception {
33
34 GZIPOutputStream gos = new GZIPOutputStream(os);
35
36 int count;
37 byte data[] = new byte[BUFFER_LENGTH];
38 while ((count = is.read(data, 0, BUFFER_LENGTH)) != -1) {
39 gos.write(data, 0, count);
40 }
41
42 gos.finish();
43
44 gos.flush();
45 gos.close();
46 }
47
48
49
56 public static void decompress(InputStream is, OutputStream os)
57 throws Exception {
58
59 GZIPInputStream gis = new GZIPInputStream(is);
60
61 int count;
62 byte data[] = new byte[BUFFER_LENGTH];
63 while ((count = gis.read(data, 0, BUFFER_LENGTH)) != -1) {
64 os.write(data, 0, count);
65 }
66
67 gis.close();
68 }
69
70
77 public static byte[] byteCompress(byte[] data) throws Exception {
78 ByteArrayInputStream bais = new ByteArrayInputStream(data);
79 ByteArrayOutputStream baos = new ByteArrayOutputStream();
80
81 // 压缩
82 compress(bais, baos);
83
84 byte[] output = baos.toByteArray();
85
86 baos.flush();
87 baos.close();
88
89 bais.close();
90
91 return output;
92 }
93
94
95
102 public static byte[] byteDecompress(byte[] data) throws Exception {
103 ByteArrayInputStream bais = new ByteArrayInputStream(data);
104 ByteArrayOutputStream baos = new ByteArrayOutputStream();
105
106 // 解压缩
107
108 decompress(bais, baos);
109
110 data = baos.toByteArray();
111
112 baos.flush();
113 baos.close();
114
115 bais.close();
116
117 return data;
118 }
119 }
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
JjjzForm jjjzForm = (JjjzForm) form;
//基金净值历史走势信息
ArrayList jjjzs = null;
//得到基金净值历史走势的方法省略了
Gson gson = new Gson();
String jsonStr = gson.toJson(jjjzs, jjjzs.getClass());
byte[] resultOriginalByte = jsonStr.getBytes();
//组织最后返回数据的缓冲字节数组
ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
OutputStream os = null;
try {
os = response.getOutputStream();
//如果要返回的结果字节数组小于50位,不将压缩
if(resultOriginalByte.length < Compress.BYTE_MIN_LENGTH){
byte flagByte = Compress.FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY;
resultBuffer.write(flagByte);
resultBuffer.write(resultOriginalByte);
}
else{
byte flagByte = Compress.FLAG_GBK_STRING_COMPRESSED_BYTEARRAY;
resultBuffer.write(flagByte);
resultBuffer.write(Compress.byteCompress(resultOriginalByte));
}
resultBuffer.flush();
resultBuffer.close();
//将最后组织后的字节数组发送给客户端
os.write(resultBuffer.toByteArray());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
byte[] receivedByte = EntityUtils.toByteArray(httpResponse.getEntity());
String result = null;
//判断接收到的字节数组是否是压缩过的
if (receivedByte[0] == Compress.FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY) {
result = new String(receivedByte, 1, receivedByte.length - 1, EXCHANGE_ENCODING);
}
else if (receivedByte[0] == Compress.FLAG_GBK_STRING_COMPRESSED_BYTEARRAY) {
byte[] compressedByte = new byte[receivedByte.length - 1];
for (int i = 0; i < compressedByte.length; i++) {
compressedByte[i] = receivedByte[i + 1];
}
byte[] resultByte = Compress.byteDecompress(compressedByte);
result = new String(resultByte, EXCHANGE_ENCODING);
}
这里最后得到的result就是服务器实际要发送的内容。
缺陷反思:任何设计都是有缺陷的。我这样做已经将Http协议做了进一层封装。Http的数据部分的第一个字节并不是实际数据,而是标识字节。这样,降低了这个接口的可重用性。统一发送Json字符串的Action能被网页(Ajax)或者其他客户端使用,经过封装压缩之后,只有能识别这个封装(就是能进行解析)的客户端能使用这个接口。网页(Ajax)就不能解析,那么这个Action就不能被Ajax使用。