文件上传
http content-type
volley的整体实现流程,我就不写了,哎,看了一个上午主体还是没什么问题,关键在于实现细节,包含的东西才多,所以这里不做过多讨论,言归正传,我们看看volley的一些细节实现吧。
乍一看,我们找遍了整个框架源码,都没发现文件上传写好的API,并且你会惊讶的发现貌似request 实现中只有针对于content-type:application/x-www-form-urlencoded;我去感情这个框架没写好,其实呢?我们先看看下面这段代码.
/** Returns the content type of the POST or PUT body. */
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
/**
* Returns the raw POST or PUT body to be sent.
*
* <p>By default, the body consists of the request parameters in
* application/x-www-form-urlencoded format. When overriding this method, consider overriding
* {@link #getBodyContentType()} as well to match the new body format.
*
* @throws AuthFailureError in the event of auth failure
*/
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/** Converts <code>params</code> into an application/x-www-form-urlencoded encoded string. */
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
看到了吧,哎,照这么说其它的实现,我们需要自己实现,文件上传了,于是乎我们依葫芦画瓢,如果要实现文件上传,我们需要改变请求头content-type和body所以具体实现可参照如下,这里只贴关键代码
具体参考Android volley 解析(三)之文件上传篇
public String getBodyContentType() {
return "multipart/form-data;charset=utf-8;boundary=" + BOUNDARY;
}
public byte[] getBody(){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
StringBuffer sb = new StringBuffer();
sb.append("--" + BOUNDARY);
sb.append("\r\n");
//解释下name 这个你可以默认成key 是服务器解析的key,务必和服务器保持一致.
// filename 服务器保存的这个文件的名称.
sb.append("Content-Disposition:form-data;name=\"file1\";filename=\"test.jpg\"\r\n");
sb.append("content-type:image/jpg\r\n\r\n");
bos.write(sb.toString().getBytes("utf-8"));
bos.write(...图片数据byte[]...);
bos.write("\r\n".getBytes("utf-8"));
bos.write("--" + BOUNDARY + "--" + "\r\n".getBytes("utf-8"));
return bos.toByteArray();
}
以上我们必须实现Request 这个类,并且重写getBody()和getBodyContentType()方法
volley缓存实现.
1 HTTP 缓存
如果大家完全没有缓存这个概念,我推荐大家看这篇
从http开始说Volley缓存
http 缓存原理
表示缓存的请求或者响应头报文
- Cache_Control
- 在请求头里面If-None-Match / Etag
- If-Modified-Since /Last-Modified.
- Expires 和 Max-Age,date
-
响应状态吗304和200.
//这是StringRequest 的代码,解析网络结果.
@Override
@SuppressWarnings("DefaultCharset")
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
long serverDate = 0;
long lastModified = 0;
long serverExpires = 0;
long softExpire = 0;
long finalExpire = 0;
long maxAge = 0;
long staleWhileRevalidate = 0;
boolean hasCacheControl = false;
boolean mustRevalidate = false;
String serverEtag = null;
String headerValue;
//RFC格式.这个表示服务器发送这条消息的时间。
headerValue = headers.get("Date");
if (headerValue != null) {
serverDate = parseDateAsEpoch(headerValue);
}
//这个的值有很多种.以下就是private,public,no-cache和no-store,max-age.
headerValue = headers.get("Cache-Control");
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",", 0);
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i].trim();
if (token.equals("no-cache") || token.equals("no-store")) {
return null;
} else if (token.startsWith("max-age=")) {
try {
maxAge = Long.parseLong(token.substring(8));
} catch (Exception e) {
}
//这个被称为死缓,总缓存时间是 maxAge + staleWhileRevalidate.
} else if (token.startsWith("stale-while-revalidate=")) {
try {
staleWhileRevalidate = Long.parseLong(token.substring(23));
} catch (Exception e) {
}
//如果有这个说明must-revalidate.不会使用死缓,过了maxAge期限,则立即请求网络.
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
mustRevalidate = true;
}
}
}
//响应头中缓存的有效时间截止到如Date: Thu, 11 Jul 2015 15:33:24 GMT。是这种RFC格式.
headerValue = headers.get("Expires");
if (headerValue != null) {
//转换成毫秒.
serverExpires = parseDateAsEpoch(headerValue);
}
//这个时间也是RFC格式.
headerValue = headers.get("Last-Modified");
if (headerValue != null) {
lastModified = parseDateAsEpoch(headerValue);
}
serverEtag = headers.get("ETag");
// Cache-Control takes precedence over an Expires header, even if both exist and Expires
// is more restrictive.
if (hasCacheControl) {
softExpire = now + maxAge * 1000;
finalExpire = mustRevalidate ? softExpire : softExpire + staleWhileRevalidate * 1000;
//expires(1.0) 和Cache-Control(1.1)那些过去
} else if (serverDate > 0 && serverExpires >= serverDate) {
// 缓存消息
softExpire = now + (serverExpires - serverDate);
finalExpire = softExpire;
}
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = finalExpire;
entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers;
entry.allResponseHeaders = response.allHeaders;
return entry;
}
2 DiskBasedCache 内存和磁盘缓存
public class DiskBasedCache implements Cache {
/** 磁盘缓存,默认5M. */
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
//这个位置是内存缓存.LinkedHash 符合LRU 的规则.
private final Map<String, CacheHeader> mEntries = new LinkedHashMap<>(16, .75f, true);
put(){};
get(){};
//这个函数是说 如果剩余空间(超过了期望的最大缓存)不足,就会删除文件.删除文件规则,显示跟LRU算法有关.
pruneIfNeeded(int needSpace);
}
总结
有些东西写的不够完善后期有时间都会慢慢补上,请广大高手们手下留情啊,欢迎提出问题,一起讨论.
为什么volley 适合数据量小的频繁请求?
volley中为了提高请求处理的速度,采用了ByteArrayPool进行内存中的数据存储的,如果下载大量的数据,这个存储空间就会溢出,所以不适合大量的数据,但是由于他的这个存储空间是内存中分配的,当存储的时候优是从ByteArrayPool中取出一块已经分配的内存区域, 不必每次存数据都要进行内存分配,而是先查找缓冲池中有无适合的内存区域,如果有,直接拿来用,从而减少内存分配的次数 ,所以他比较适合大量的数据量少的网络数据交互情况。