volley 那些细节

文件上传

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 缓存原理
表示缓存的请求或者响应头报文

  1. Cache_Control
  2. 在请求头里面If-None-Match / Etag
  3. If-Modified-Since /Last-Modified.
  4. Expires 和 Max-Age,date
  5. 响应状态吗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 内存和磁盘缓存

LinkedHashMap

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中取出一块已经分配的内存区域, 不必每次存数据都要进行内存分配,而是先查找缓冲池中有无适合的内存区域,如果有,直接拿来用,从而减少内存分配的次数 ,所以他比较适合大量的数据量少的网络数据交互情况。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,006评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,647评论 18 139
  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,359评论 8 265
  • 当我们需要创建一个类,来接收任意数据类型的数值,再做一些处理时,泛型的通配符提供了很便捷的语法。 首先,我们声明一...
    BestFei阅读 122评论 0 0
  • 伴我长大的,是小镇上的酒香, 浓浓的,像化不开的乳汁。 记忆中,金黄的柴火垛, 是我儿时的游乐场。 扛在肩上的糖葫...
    宁宝儿阅读 378评论 0 0