文档章节

Android网络图片缓存

 等待流星
发布于 2014/04/24 13:40
字数 1234
阅读 944
收藏 1
点赞 0
评论 0

获取一张图片,从三个地方进行获取,首先是内存缓存,然后是文件缓存,最后才从网络中获取。

//内存缓存

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();

}

}



//文件缓存,缓存到SD卡上

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;

}

}

}



//=============================获取图片========================================

/*** 获得一张图片,从三个地方获取,首先是内存缓存,然后是文件缓存,最后从网络获取 ***/

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;

}


本文转载自:http://blog.163.com/ppy2790@126/blog/static/1032422412013112374922801/

共有 人打赏支持
粉丝 5
博文 73
码字总数 10995
作品 0
崇明
腾讯技术分享:Android版手机QQ的缓存监控与优化实践

本文内容整理自公众号腾讯Bugly,感谢原作者的分享。 1、问题背景 对于Android应用来说,内存向来是比较重要的性能指标。内存占用过高,会影响应用的流畅度,甚至引发OOM,非常影响用户体验。...

JackJiang2011 ⋅ 04/08 ⋅ 0

Android 性能优化:手把手教你优化Bitmap图片资源的使用

前言 在 开发中,性能优化策略十分重要 本文主要讲解性能优化中的Bitmap 使用优化,希望你们会喜欢 目录 1. 优化原因 即 为什么要优化图片资源,具体如下图:

Carson_Ho ⋅ 04/24 ⋅ 0

Android性能优化:手把手教你如何让App更快、更稳、更省(含内存、布局优化等)

前言 在 开发中,性能优化策略十分重要 因为其决定了应用程序的开发质量:可用性、流畅性、稳定性等,是提高用户留存率的关键 本文全面讲解性能优化中的所有知识,献上一份 性能优化的详细攻...

Carson_Ho ⋅ 05/30 ⋅ 0

android 读取WebView缓存及清理WebView缓存

1.缓存的分类: 首先要说的一点是缓存的分类,我们缓存的数据分为:页面缓存和数据缓存 页面缓存:加载一个网页时的html、JS、CSS等页面或者资源数据,这些缓存资源是由于浏览器 的行为而产生...

keller.zhou ⋅ 2016/11/15 ⋅ 0

Design Patterns in Android:责任链模式

前言 非常抱歉,本系列博客长达半年没更新了,今日偶得灵感,更新一波《设计模式Android篇:责任链模式》。点击此处查看《Design Patterns in Android》系列其他文章。 本文原创作者MichaelX。...

MichaelX ⋅ 05/14 ⋅ 0

Android Flutter 内存机制初探

阿里妹导读:闲鱼技术团队一直在探索如何使用Flutter来统一移动App开发。移动设备上的资源有限,内存使用成了日常开发中的常见问题。那么,Flutter是如何使用内存,又会对Native App的内存带...

b0q8cpra539hafs7 ⋅ 05/23 ⋅ 0

Google推荐——Glide使用详解

零、前言 本文所使用的Glide版本为3.7.0 「推荐」设计模式系列 设计模式(零)- 面向对象的六大原则 设计模式(一)- 单例模式 设计模式(二)- Builder模式 设计模式(三)- 原型模式 设计模...

丁佳辉 ⋅ 06/12 ⋅ 0

Android RxJava: 这是一份全面的 操作符 使用汇总 (含详细实例讲解)

前言 ,由于其基于事件流的链式调用、逻辑简洁 & 使用简单的特点,深受各大 开发者的欢迎。 如果还不了解RxJava,请看文章:Android:这是一篇 清晰 & 易懂的Rxjava 入门教程 如此受欢迎的原...

Carson_Ho ⋅ 05/31 ⋅ 0

Glide高级详解—缓存与解码复用

Glide 使用简明的流式语法API,大多数情况下,可能完成图片的设置你只需要: 在调用into之后,Glide会首先从Active Resources查找当前是否有对应的活跃图片,没有则查找内存缓存,没有则查找...

codeGoogle ⋅ 05/07 ⋅ 0

Xiaolei123/OKBook

OK小说 开源地址:https://gitee.com/xcodexiao/OKBook APK文件下载:传送门 kotlin + 协程 + MVVM 模式来编写的看小说APP。 主要框架 Lifecycle 传送门 ViewModel 传送门 LiveData 传送门 ...

Xiaolei123 ⋅ 05/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

005. 深入JVM学习—Java堆内存参数调整

1. JVM整体内存调整图解(调优关键) 实际上每一块子内存区域都会存在一部分可变伸缩区域,其基本流程:如果内存空间不足,则在可变的范围之内扩大内存空间,当一段时间之后,内存空间不紧张...

影狼 ⋅ 31分钟前 ⋅ 0

内存障碍: 软件黑客的硬件视图

此文为笔者近日有幸看到的一则关于计算机底层内存障碍的学术论文,并翻译(机译)而来[自认为翻译的还行],若读者想要英文原版的论文话,给我留言,我发给你。 内存障碍: 软件黑客的硬件视图...

Romane ⋅ 今天 ⋅ 0

SpringCloud 微服务 (七) 服务通信 Feign

壹 继续第(六)篇RestTemplate篇 做到现在,本机上已经有注册中心: eureka, 服务:client、order、product 继续在order中实现通信向product服务,使用Feign方式 下面记录学习和遇到的问题 贰 or...

___大侠 ⋅ 今天 ⋅ 0

gitee、github上issue标签方案

目录 [TOC] issue生命周期 st=>start: 开始e=>end: 结束op0=>operation: 新建issueop1=>operation: 评审issueop2=>operation: 任务负责人执行任务cond1=>condition: 是否通过?op3=>o......

lovewinner ⋅ 今天 ⋅ 0

浅谈mysql的索引设计原则以及常见索引的区别

索引定义:是一个单独的,存储在磁盘上的数据库结构,其包含着对数据表里所有记录的引用指针. 数据库索引的设计原则: 为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索...

屌丝男神 ⋅ 今天 ⋅ 0

String,StringBuilder,StringBuffer三者的区别

这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面。 首先说运行速度,或者说是, 1.执行速度 在这方面运行速度快慢为:StringBuilder(线程不安全,可变) > StringBuffer...

时刻在奔跑 ⋅ 今天 ⋅ 0

java以太坊开发 - web3j使用钱包进行转账

首先载入钱包,然后利用账户凭证操作受控交易Transfer进行转账: Web3j web3 = Web3j.build(new HttpService()); // defaults to http://localhost:8545/Credentials credentials = Wallet......

以太坊教程 ⋅ 今天 ⋅ 0

Oracle全文检索配置与实践

Oracle全文检索配置与实践

微小宝 ⋅ 今天 ⋅ 0

mysql的分区和分表

1,什么是mysql分表,分区 什么是分表,从表面意思上看呢,就是把一张表分成N多个小表,具体请看mysql分表的3种方法 什么是分区,分区呢就是把一张表的数据分成N多个区块,这些区块可以在同一...

梦梦阁 ⋅ 今天 ⋅ 0

exception.ZuulException: Forwarding error

错误日志 com.netflix.zuul.exception.ZuulException: Forwarding error Caused by: com.netflix.hystrix.exception.HystrixRuntimeException: xxx timed-out and no fallback available. Ca......

jack_peng ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部