Http请求优化

参考资料:

判断什么时候使用缓存还有压缩格式:

http://archive.oreilly.com/pub/post/optimizing_http_downloads_in_j.html

http://www.cnblogs.com/tankxiao/archive/2012/02/13/2342672.html

怎样启用缓存及相关处理:

http://developer.android.com/reference/android/net/http/HttpResponseCache.html

http://practicaldroid.blogspot.ru/2013/01/utilizing-http-response-cache.html

http://grepcode.com/file/repo1.maven.org/maven2/com.google.okhttp/okhttp/20120626/libcore/net/http/HttpResponseCache.java

http://developer.android.com/reference/java/net/CacheResponse.html

1、使用HttpConnection进行网络请求的时候可以设置缓存,那么怎么获取缓存以及存在哪里好呢?

    启用缓存功能(最好在程序的入口调用下面的方法):

 try
    {
        Object existingResponseCache = Class.forName("android.net.http.HttpResponseCache").getMethod("getInstalled").invoke(null);
        if (null == existingResponseCache) {
            long httpCacheSize = 10 * 1024 * 1024;
            File httpCacheDir = new File(dir, "http");
            Class.forName("android.net.http.HttpResponseCache")
                    .getMethod("install", File.class, long.class)
                    .invoke(null, httpCacheDir, httpCacheSize);
        }
    } catch(Exception httpResponseCacheNotAvailable) {
        // For versions before Android 4.0, the exception will be thrown.
        try {
            com.integralblue.httpresponsecache.HttpResponseCache existingResponseCache = com.integralblue.httpresponsecache.HttpResponseCache.getInstalled();
            if (null == existingResponseCache) {
                long cacheSize = 10 * 1024 * 1024;
                File httpCacheDir = new File(dir, "http");
                com.integralblue.httpresponsecache.HttpResponseCache.install(httpCacheDir, cacheSize);
            }
        } catch (Exception e) {
        }
    }

上面的代码中是直接是用dir来代表缓存目录,那么存在哪里比较好呢?最好选择默认的缓存目录:Context.getExternalCacheDir();

    接下来要怎么删除缓存呢?

try {
            Object existingResponseCache = Class.forName("android.net.http.HttpResponseCache").getMethod("getInstalled").invoke(null);
            if (null != existingResponseCache) {
                Class.forName("android.net.http.HttpResponseCache").getMethod("delete").invoke(existingResponseCache);
            }
        } catch (Exception httpResponseCacheNotAvailable) {
            try {
                com.integralblue.httpresponsecache.HttpResponseCache existingResponseCache = com.integralblue.httpresponsecache.HttpResponseCache.getInstalled();
                if (null != existingResponseCache) {
                    existingResponseCache.delete();
                }
            } catch (Exception e) {
            }
        }

上面两段代码就是开启和关闭缓存功能,分别在程序开启和结束的时候调用最好!(看情况处理)

接下来是获取某个uri对应的request的缓存:

 CacheResponse cache = null;
        URI uri = new URI(url);
        try {
            Object existingResponseCache = Class.forName("android.net.http.HttpResponseCache").getMethod("getInstalled").invoke(null);
            if (null != existingResponseCache) {
                cache = (CacheResponse) Class.forName("android.net.http.HttpResponseCache").getMethod("get", URI.class, String.class, Map.class).invoke(existingResponseCache, uri,”GET”, httpUrlConnection.getRequestProperties());
            }
        } catch (Exception httpResponseCacheNotAvailable) {
            com.integralblue.httpresponsecache.HttpResponseCache existingResponseCache = null;
            try {
                existingResponseCache = com.integralblue.httpresponsecache.HttpResponseCache.getInstalled();
                if (null != existingResponseCache) {
                    cache = existingResponseCache.get(uri, ”GET”, httpUrlConnection.getRequestProperties());
                }
            } catch (Exception e) {
            }
        }

再然后就是更新某个uri对应的request的缓存:

在查看源码的时候发现httpResponseCache是使用了LinkedMap来保存发送过请求,所以使用put方法就是更新缓存:

URI uri = new URI(url);
        try {
            Object existingResponseCache = Class.forName("android.net.http.HttpResponseCache").getMethod("getInstalled").invoke(null);
            if (null != existingResponseCache) {
                cache = (CacheResponse) Class.forName("android.net.http.HttpResponseCache").getMethod("get", URI.class, String.class, Map.class).invoke(existingResponseCache, uri,”GET”, httpUrlConnection.getRequestProperties());
            }
        } catch (Exception httpResponseCacheNotAvailable) {
            com.integralblue.httpresponsecache.HttpResponseCache existingResponseCache = null;
            try {
                existingResponseCache = com.integralblue.httpresponsecache.HttpResponseCache.getInstalled();
                if (null != existingResponseCache) {
                    cache = existingResponseCache.put(uri, urlConnection);
                }
            } catch (Exception e) {
            }
        }

2、接下说一下HttpConnction流式输出的缺陷和解决方案:

http://www.mzone.cc/article/198.html

首先是HttpConnection默认使用什么样的方式输出数据呢?经过查找源码发现其是利用System.copy的方式将数据缓存在数组中

然后再一次性发送到服务器,这样的操作就有可能造成OutOfMemoryError!

HttpConnection使用的OutPutStream是sun.net.www.http.PasterOutputStream,这个类是继承于ByteArrayOutputStream!

下面是ByteArrayOutputStream的代码片段:

public synchronized void write(int i)
        {
            int j = count + 1;
            if(j > buf.length) {
                byte abyte0[] = new byte[Math.max(buf.length << 1, j)];
                System.arraycopy(buf, 0, abyte0, 0, count);
                buf = abyte0;
            }
            buf[count] = (byte)i;
            count = j;
        }

        public synchronized void write(byte abyte0[], int i, int j)
        {
            if(i < 0 || i > abyte0.length || j < 0 || i + j > abyte0.length || i + j < 0) throw new IndexOutOfBoundsException();
            if(j == 0)  return;
            int k = count + j;
            if(k > buf.length) {
                byte abyte1[] = new byte[Math.max(buf.length << 1, k)];
                System.arraycopy(buf, 0, abyte1, 0, count);
                buf = abyte1;
            }
            System.arraycopy(abyte0, i, buf, count, j);
            count = k;
        }

    我们可以看到它是使用System.arraycopy的功能来将所有的输出流存放在一个数组中的.因此,在使用HttpURLConnection进行流式输出

的时候如果输出流比较大,那么就该考虑使用其他方式了。

     查看sun提供的HttpURLConnection的源码, 会发现其默认是采用上面提高的PosterOutputStream类来进行缓冲输出的,
即首先将所有的文件流在本地内存中进行缓存, 等到输出结束执行close的时候一次性输出到服务器端.同时我们看到sun的
HttpURLConnection中的getOutputStream() 中有如下代码:

if(streaming()) {
            if(fixedContentLength != -1)
                strOutputStream = new StreamingOutputStream(ps, fixedContentLength);
            else if(chunkLength != -1)
                strOutputStream = new StreamingOutputStream(new ChunkedOutputStream(ps, chunkLength), -1);
            return strOutputStream;
        }

         其中strmeanming() 方法是用来判断是否是流式的输出, 其代码为:return fixedContentLength != -1 || chunkLength != -1;它的

判断方法就是如果设置了输出流的固定长度或是设置了块的长度, 那么将采用流式输出.因此, 我们可以在输出的时候可以设置其长度来达

到流式输出这样的效果.另外,StreamingOutputString类是sun提供的HttpURLConnection的内部类, 继承自FilterOutputStream,

 而非ByteArrayOutputStream, 所以不会在本地内存中进行缓存

         而jdk中的HttpURLConnection并没有提供设置流的固定长度或块输出的长度方法, 所以我们需要显示的将new

URL(“url”).openConnection()返回的URLConnection转换成sun的HttpURLConnection, 从而我们就可以很方便的使用

setFixedLengthStreamingMode方法来设置流的固定长度, 那么也就会采用流式的输出了.

那么也就不会出现OutOfMemoryError的错误了.另外, ChunkedOutputStream也是不会在本地进行缓存的,

它是使用固定大小的数组来缓存输出流, 等缓存满的时候就自动的调用基础流进行输出, 这个主要是用在无法确定输出流的具体长度但是又不想

在本地进行缓存时用到.同理, 我们通过设置setChunkedStreamingMode就可以达到这样的效果, 三种方式的代码如下:

        第1种方式:使用直接输出流的方式(已知输出流的长度):

        import HttpURLConnection;
        ......
        HttpURLConnection con = (HttpURLConnection)new URL("url").openConnection();
        con.setFixedLengthStreamingMode(输出流的固定长度);
        ......
        第2种方式:使用直接输出流的方式(未知输出流的长度):

        import HttpURLConnection;
        ......
        HttpURLConnection con = (HttpURLConnection)new URL("url").openConnection();
        con.setChunkedStreamingMode(块的大小);
        ......
        第3种方式:本地缓存后一次性输出:

        import java.net.HttpURLConnection;
        ......
        HttpURLConnection con = (HttpURLConnection)new URL("url").openConnection();
        ......
        通过设置直接输出后,我传送文件的大小为200M的时候也不会出现OutOfMemoryError的错误,而之前使用第3种方式进行文件流输出的时候不到40M就出现了 OutOfMemoryError的错误了.




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值