参考资料:
判断什么时候使用缓存还有压缩格式:
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://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的错误了.