android 网络图片双缓存
本文转自:http://blog.csdn.net/a79412906/article/details/10180583
Android每次加载图片很浪费时间。所以设计了一个图片缓存技术来解决每次android手机加载图片的问题
内存的读取速度是最快的,然后是文件的读取速度,最后是网络资源的读取
既然内存的读取时间最快,我们好好利用内存资源。将内存再分两层缓存
强引用缓存不会轻易被回收,来保存常用数据,不常用的资源放入软引用缓存中。
对于硬引用和软引用的介绍:
⑴强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
⑵软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存
1,对于强引用和软引用的使用,我们首先去强引用缓存中去找图片资源,当没有发现时,就去软引用缓存中。当强引用的超额时,将最后使用的资源放入软引用缓存中,使用到软引用的资源时 ,则将资源重新放回强引用缓存池中。
2,内存缓存池中找不到,就去文件中查找,
3,再找不到就只好去网络上下载了,下载到以后,分别将资源放入文件缓存和内存缓存中
Android对于InputStream流有个小bug在慢速网络的情况下可能产生中断,可以考虑重写FilterInputStream处理
skip方法来解决这个bug。 BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法获取完整的数据,这里我 们通
过继承FilterInputStream类的skip方法来强制实现flush流中的数据,主要原理就是检查是否到文件末端,告诉http类是否继续。
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int b = read();
if (b < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
主界面读取图片
- public class MainActivity extends Activity {
- private ImageMemoryCache memoryCache;
- private ImageFileCache fileCache;
- private ImageView imageView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- memoryCache=new ImageMemoryCache(this);
- fileCache=new ImageFileCache();
- imageView=(ImageView) findViewById(R.id.img);
- Bitmap b=getBitmap("http://f.hiphotos.baidu.com/album/w%3D2048/sign=7aa167f79f2f07085f052d00dd1cb999/472309f7905298228f794c7bd6ca7bcb0b46d4c4.jpg");
- imageView.setImageBitmap(b);
- }
- public Bitmap getBitmap(String url) {
- // 从内存缓存中获取图片
- Bitmap result = memoryCache.getBitmapFromCache(url);
- if (result == null) {
- // 文件缓存中获取
- result = fileCache.getImage(url);
- if (result == null) {
- // 从网络获取
- result = ImageGetFromHttp.downloadBitmap(url);
- if (result != null) {
- fileCache.saveBitmap(result, url);
- memoryCache.addBitmapToCache(url, result);
- }
- } else {
- // 添加到内存缓存
- memoryCache.addBitmapToCache(url, result);
- }
- }
- return result;
- }
- }
内存中读取
- public class ImageMemoryCache {
- /**
- * 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。
- * 硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。
- */
- private static final int SOFT_CACHE_SIZE = 15; //软引用缓存容量
- private static LruCache<String, Bitmap> mLruCache; //硬引用缓存
- private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; //软引用缓存
- public ImageMemoryCache(Context context) {
- int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
- int cacheSize = 1024 * 1024 * memClass / 4; //硬引用缓存容量,为系统可用内存的1/4
- mLruCache = new LruCache<String, Bitmap>(cacheSize) {
- @Override
- protected int sizeOf(String key, Bitmap value) {
- if (value != null)
- return value.getRowBytes() * value.getHeight();
- else
- return 0;
- }
- @Override
- protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
- if (oldValue != null)
- // 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存
- mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
- }
- };
- mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {
- private static final long serialVersionUID = 6040103833179403725L;
- @Override
- protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
- if (size() > SOFT_CACHE_SIZE){
- return true;
- }
- return false;
- }
- };
- }
- /**
- * 从缓存中获取图片
- */
- public Bitmap getBitmapFromCache(String url) {
- Bitmap bitmap;
- //先从硬引用缓存中获取
- synchronized (mLruCache) {
- bitmap = mLruCache.get(url);
- if (bitmap != null) {
- //如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除
- mLruCache.remove(url);
- mLruCache.put(url, bitmap);
- return bitmap;
- }
- }
- //如果硬引用缓存中找不到,到软引用缓存中找
- synchronized (mSoftCache) {
- SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
- if (bitmapReference != null) {
- bitmap = bitmapReference.get();
- if (bitmap != null) {
- //将图片移回硬缓存
- mLruCache.put(url, bitmap);
- mSoftCache.remove(url);
- return bitmap;
- } else {
- mSoftCache.remove(url);
- }
- }
- }
- return null;
- }
- /**
- * 添加图片到缓存
- */
- public void addBitmapToCache(String url, Bitmap bitmap) {
- if (bitmap != null) {
- synchronized (mLruCache) {
- mLruCache.put(url, bitmap);
- }
- }
- }
- public void clearCache() {
- mSoftCache.clear();
- }
- }
- public class ImageFileCache {
- private static final String CACHDIR = "ImgCach";
- private static final String WHOLESALE_CONV = ".cach";
- private static final int MB = 1024*1024;
- private static final int CACHE_SIZE = 10;
- private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;
- public ImageFileCache() {
- //清理文件缓存
- removeCache(getDirectory());
- }
- /** 从缓存中获取图片 **/
- public Bitmap getImage(final String url) {
- final String path = getDirectory() + "/" + convertUrlToFileName(url);
- File file = new File(path);
- if (file.exists()) {
- Bitmap bmp = BitmapFactory.decodeFile(path);
- if (bmp == null) {
- file.delete();
- } else {
- updateFileTime(path);
- return bmp;
- }
- }
- return null;
- }
- /** 将图片存入文件缓存 **/
- public void saveBitmap(Bitmap bm, String url) {
- if (bm == null) {
- return;
- }
- //判断sdcard上的空间
- if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
- //SD空间不足
- return;
- }
- String filename = convertUrlToFileName(url);
- String dir = getDirectory();
- File dirFile = new File(dir);
- if (!dirFile.exists())
- dirFile.mkdirs();
- File file = new File(dir +"/" + filename);
- try {
- file.createNewFile();
- OutputStream outStream = new FileOutputStream(file);
- bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
- outStream.flush();
- outStream.close();
- } catch (FileNotFoundException e) {
- Log.w("ImageFileCache", "FileNotFoundException");
- } catch (IOException e) {
- Log.w("ImageFileCache", "IOException");
- }
- }
- /**
- * 计算存储目录下的文件大小,
- * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
- * 那么删除40%最近没有被使用的文件
- */
- private boolean removeCache(String dirPath) {
- File dir = new File(dirPath);
- File[] files = dir.listFiles();
- if (files == null) {
- return true;
- }
- if (!android.os.Environment.getExternalStorageState().equals(
- android.os.Environment.MEDIA_MOUNTED)) {
- return false;
- }
- int dirSize = 0;
- for (int i = 0; i < files.length; i++) {
- if (files[i].getName().contains(WHOLESALE_CONV)) {
- dirSize += files[i].length();
- }
- }
- if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
- int removeFactor = (int) ((0.4 * files.length) + 1);
- Arrays.sort(files, new FileLastModifSort());
- for (int i = 0; i < removeFactor; i++) {
- if (files[i].getName().contains(WHOLESALE_CONV)) {
- files[i].delete();
- }
- }
- }
- if (freeSpaceOnSd() <= CACHE_SIZE) {
- return false;
- }
- return true;
- }
- /** 修改文件的最后修改时间 **/
- public void updateFileTime(String path) {
- File file = new File(path);
- long newModifiedTime = System.currentTimeMillis();
- file.setLastModified(newModifiedTime);
- }
- /** 计算sdcard上的剩余空间 **/
- private int freeSpaceOnSd() {
- StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
- double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
- return (int) sdFreeMB;
- }
- /** 将url转成文件名 **/
- private String convertUrlToFileName(String url) {
- String[] strs = url.split("/");
- return strs[strs.length - 1] + WHOLESALE_CONV;
- }
- /** 获得缓存目录 **/
- private String getDirectory() {
- String dir = getSDPath() + "/" + CACHDIR;
- return dir;
- }
- /** 取SD卡路径 **/
- private String getSDPath() {
- File sdDir = null;
- boolean sdCardExist = Environment.getExternalStorageState().equals(
- android.os.Environment.MEDIA_MOUNTED); //判断sd卡是否存在
- if (sdCardExist) {
- sdDir = Environment.getExternalStorageDirectory(); //获取根目录
- }
- if (sdDir != null) {
- return sdDir.toString();
- } else {
- return "";
- }
- }
- /**
- * 根据文件的最后修改时间进行排序
- */
- private class FileLastModifSort implements Comparator<File> {
- public int compare(File arg0, File arg1) {
- if (arg0.lastModified() > arg1.lastModified()) {
- return 1;
- } else if (arg0.lastModified() == arg1.lastModified()) {
- return 0;
- } else {
- return -1;
- }
- }
- }
- }
网络下载图片
- public class ImageGetFromHttp {
- private static final String LOG_TAG = "ImageGetFromHttp";
- public static Bitmap downloadBitmap(String url) {
- final HttpClient client = new DefaultHttpClient();
- final HttpGet getRequest = new HttpGet(url);
- try {
- HttpResponse response = client.execute(getRequest);
- final int statusCode = response.getStatusLine().getStatusCode();
- if (statusCode != HttpStatus.SC_OK) {
- Log.w(LOG_TAG, "Error " + statusCode + " while retrieving bitmap from " + url);
- return null;
- }
- final HttpEntity entity = response.getEntity();
- if (entity != null) {
- InputStream inputStream = null;
- try {
- inputStream = entity.getContent();
- FilterInputStream fit = new FlushedInputStream(inputStream);
- return BitmapFactory.decodeStream(fit);
- } finally {
- if (inputStream != null) {
- inputStream.close();
- inputStream = null;
- }
- entity.consumeContent();
- }
- }
- } catch (IOException e) {
- getRequest.abort();
- Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
- } catch (IllegalStateException e) {
- getRequest.abort();
- Log.w(LOG_TAG, "Incorrect URL: " + url);
- } catch (Exception e) {
- getRequest.abort();
- Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
- } finally {
- client.getConnectionManager().shutdown();
- }
- return null;
- }
- /*
- * An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
- */
- static class FlushedInputStream extends FilterInputStream {
- public FlushedInputStream(InputStream inputStream) {
- super(inputStream);
- }
- @Override
- public long skip(long n) throws IOException {
- long totalBytesSkipped = 0L;
- while (totalBytesSkipped < n) {
- long bytesSkipped = in.skip(n - totalBytesSkipped);
- if (bytesSkipped == 0L) {
- int b = read();
- if (b < 0) {
- break; // we reached EOF
- } else {
- bytesSkipped = 1; // we read one byte
- }
- }
- totalBytesSkipped += bytesSkipped;
- }
- return totalBytesSkipped;
- }
- }
- }
权限
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
android 网络图片双缓存的更多相关文章
- Android中用双缓存技术,加载网络图片
最近在学校参加一个比赛,写的一个Android应用,里面要加载大量的网络图片,可是用传统的方法图片一多就会造成程序出现内存溢出而崩溃.因为自己也在学习中,所以看了很多博客和视频,然后参照这些大神的写源 ...
- android 双缓存机制
废话不多说,直接贴代码! 所谓的双缓存,第一就是缓存在内存里面,第二就是缓存在SD卡里面,当你需要加载数据时,先去内存缓存中查找,如果没有再去SD卡中查找,并且用户可以自选使用哪种缓存! 缓存内存和缓 ...
- Android 框架修炼-自己封装双缓存管理框架库
一.概述 Android开发中,网络请求是很重要的一部分,而缓存网络请求来的图片或者响应结果字符串或者结果流,既可以省流量,同时也可以帮助我们 解决无网或弱网情况下加载情况,当然也可以提升程序性能效率 ...
- wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)
wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之L ...
- Android为TV端助力 双缓存机制
废话不多说,直接贴代码! 所谓的双缓存,第一就是缓存在内存里面,第二就是缓存在SD卡里面,当你需要加载数据时,先去内存缓存中查找,如果没有再去SD卡中查找,并且用户可以自选使用哪种缓存! 缓存内存和缓 ...
- Android自定义view双缓存技术
自定义一个写字板来帮助理解双缓存.如果不使用双缓存那么用户只能看到绘制的最后一笔的效果.因为在不断调用invalidate(),未保存的绘制过程会消失. package newviews; impor ...
- (BUG已修改,最优化)安卓ListView异步加载网络图片与缓存软引用图片,线程池,只加载当前屏之说明
原文:http://blog.csdn.net/java_jh/article/details/20068915 迟点出更新的.这个还有BUG.因为软引应不给力了.2.3之后 前几天的原文有一个线程管 ...
- picasso_强大的Android图片下载缓存库
tag: android pic skill date: 2016/07/09 title: picasso-强大的Android图片下载缓存库 [本文转载自:泡在网上的日子 参考:http://bl ...
- android图片的缓存--节约内存提高程序效率
如今android应用占内存一个比一个大,android程序的质量亟待提高. 这里简单说说网络图片的缓存,我这边就简单的说说思路 1:网络图片,无疑须要去下载图片,我们不须要每次都去下载. 维护一张表 ...
随机推荐
- Linux下如何查看系统启动时间和运行时间(转)
1.uptime命令输出:16:11:40 up 59 days, 4:21, 2 users, load average: 0.00, 0.01, 0.00 2.查看/proc/uptime文件计算 ...
- PLSQL连接Oracle 数据库配置详解
1. 下载instantclient-basic-win32-11.2.0.1.0 (oracle官网下载地址:http://www.oracle.com/technetwork/topics/win ...
- [Android Pro] AndroidX了解一下
cp : https://blog.csdn.net/qq_17766199/article/details/81433706 1.说明 官方原文如下: We hope the division be ...
- ESLint 配置说明
ESLint 有什么用,为什么要使用? ESLint是一套可自定义规则的JS代码检查与修复工具 目标是保存团队代码的一致性和避免错误并且修复错误.减少团队沟通成本 "no-alert ...
- iOS 7设计备忘单
With the release of iOS 7, app designers and developers will need to adjust their visual language to ...
- 浅谈常用的几种web攻击方式
一.Dos攻击(Denial of Service attack) 是一种针对服务器的能够让服务器呈现静止状态的攻击方式.有时候也加服务停止攻击或拒绝服务攻击.其原理就是发送大量的合法请求到服务器,服 ...
- java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁(转载)
之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比>,当时对这个测试结果很疑惑,反复执行过多次,发现结果是一样的: 1. 单线程下synchronized效率最高 ...
- PNG、 JPG图片压缩方法
参考链接 https://tinypng.com/developers/reference/python 1.安装 pip install --upgrade tinify 2.使用python脚本压 ...
- 最好的Python机器学习库
参考链接:http://www.csdn.net/article/2015-12-10/2826435
- T-Pot平台cowrie蜜罐暴力破解探测及实现自动化邮件告警
前言:Cowrie是基于kippo更改的中交互ssh蜜罐, 可以对暴力攻击账号密码等记录,并提供伪造的文件系统环境记录黑客操作行为, 并保存通过wget/curl下载的文件以及通过SFTP.SCP上传 ...