Android系统中图片一般用Bitmap对象表示,它支持png,jpg等常见格式。通常情况下图片的体积都比较大,单个应用允许使用的内存又是有限的,所以我们需要采取一些手段减少内存占用并提高加载速度。

1、图片加载

SDK提供了BitmapFactory类供我们加载图片,常用的方法有这么几个:

  • BitmapFactory.decodeFile :从文件加载。
  • BitmapFactory.decodeByteArray :从字节数组加载。
  • BitmapFactory.decodeStream :从输入流加载。
  • BitmapFactory.decodeResource :从资源文件加载。

假设我们用ImageView显示图片,通常它的尺寸要比图片的尺寸小很多,那么把图片整个加载进内存显然是没有必要的。在图形学上有个名词叫“下采样”,作用就是降低图像的分辨率,使其符合显示区域的大小。通过BitmapFactory.Options类,我们也可以实现同样的功能。这里主要用到了它的 inSampleSize 参数,如果它的值是1,那么采样后的图片跟原图一致,如果是2,那么采样后的图片长和宽都是原来的一半,占用的内存也就是原来的四分之一。

public static Bitmap decodeSampleBitmapFromBytes(byte[] data) {
final BitmapFactory.Options options = new BitmapFactory.Options();
// inJustDecodeBounds为true时仅解析图片原始信息,并不会真正加载图片。
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, options);
// 此时图片的宽高可以通过options.outWidth和options.outHeight获取到,我们
// 可以根据自己的需求计算出采样比。
options.inSampleSize = 1;
// inJustDecodeBounds设置为fales,加载图片到内存中。
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}

2、图片缓存

缓存在计算机领域使用非常广泛,如HTTP缓存,DNS缓存等等,缓存既可以提高响应速度,又能节省服务器带宽,在图片加载上它同样适用。Android开发中一般会对图片做两级缓存:内存缓存和文件缓存,而且它们都有库供我们使用,分别是LruCache和DiskLruCache。从名字就可以看出两者都使用了LRU算法,即优先淘汰那些近期最少使用的缓存。

2.1、LruCache

LruCache是Android提供的一个缓存类,一般用来管理内存缓存。

// #1:确定缓存大小。
int maxMemory = (int)(Runtime.getRuntime().totalMemory() / 1024);
int cacheSize = maxMemory / 8;
// #2:重写sizeOf方法计算每个缓存对象的内存占用。
LruCache<String, Bitmap> mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};

LruCache是一个泛型类可以容纳各种对象,因而它无法计算被储存对象的大小,所以我们需要重写它的 sizeOf 方法,手动进行计算。那LruCache是如何实现的呢,实际上它仅仅是对LinkedHashMap进行了封装并处理了线程安全问题。LinkedHashMap的构造函数中有一个布尔类型的参数, accessOrder ,当它为 true 时元素按访问顺序存储,为 false 时按插入顺序存储。当元素按访问顺序存储时在其尾部取出的元素也就是最近最少使用的元素,也就实现了LRU算法。LruCache只需要每次 put 函数被调用后计算当前总缓存的大小,当其超出门限值时移除位于LinkedHashMap尾部的元素即可。

2.2、DiskLruCache

DiskLruCache同LruCache一样都使用LinkedHashMap实现LRU算法,但DiskLruCache在实现和使用上更复杂一些,毕竟需要对文件进行管理。

获得DiskLruCache对象需要调用 DiskLruCache.open 函数:

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)

它接收4个参数,第一个是缓存区目录,第二个是客户端版本号,DiskLruCache认为当版本号发生变化时缓存是无效的,第三个参数代表每个键可以关联几个文件,最后一个参数指定的缓存区的大小。在创建对象时,DiskLruCache会根据缓冲区目录下名为“journal”的日志文件在LinkedHashMap中为缓存文件建立索引,所有对缓冲区的操作都会被记录在这个文件中。当缓冲区大小到达门限值后根据LRU算法对文件进行清理。

读取缓存时使用 DiskLruCache.get 函数:

public synchronized Snapshot get(String key) throws IOException

函数返回一个Snapshot对象,通过该对象我们可以获取到缓存文件的输入流,多个线程可以同时使用各自的SnapShot对象读取同一个Key对应的缓存。

操作缓存时使用 DiskLruCache.edit 函数:

public Editor edit(String key) throws IOException

创建或更改完毕后用 Editor.commit 函数提交或用 Editor.abort 函数取消。一个Key对应的缓存被操作时仍可以使用Snapshot对象读取其内容,因为Editor的所有操作都会先作用于临时文件。注意每个Key只能同时获取一个Editor对象,也就是说即使Editor没有做任何操作也要调用 Editor.abort 或  Editor.commit 函数,不然再次获取时函数返回 null 。

2.3、代码示例

public Bitmap loadBitmap(String url) {
// DiskLruCache要求键中不能含有特殊字符,所以
// 一般先做哈希处理。
String key = MD5(url);
Bitmap bitmap = loadBitmapFromMemCache(key);
if (bitmap != null) {
return bitmap;
}
try {
bitmap = loadBitmapFromDiskCache(key);
if (bitmap != null) {
return bitmap;
}
bitmap = loadBitmapFromHttp(url);
if (bitmap != null) {
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

在 loadBitmapFromHttp 函数中需要将图片资源放入DiskLruCache中,在 loadBitmapFromDiskCache 函数中将加载后的Bitmap对象放入LruCache中,如此便形成了一条缓存链。

Bitmap的加载与缓存的更多相关文章

  1. Android艺术——Bitmap高效加载和缓存代码分析(2)

    Bitmap的加载与缓存代码分析: 图片的压缩 比如有一张1024*768像素的图像要被载入内存,然而最终你要用到的图片大小其实只有128*96,那么我们会浪费很大一部分内存,这显然是没有必要的,下面 ...

  2. Android艺术——Bitmap高效加载和缓存(1)

    通过Bitmap我们可以设计一个ImageLoader,实现应该具有的功能是: 图片的同步加载:图片的异步加载:图片的压缩:内存缓存:磁盘缓存:网络获取: 1.加载 首先提到加载:BitmapFact ...

  3. [翻译]Bitmap的异步加载和缓存

    内容概述 [翻译]开发文档:android Bitmap的高效使用 本文内容来自开发文档"Traning > Displaying Bitmaps Efficiently", ...

  4. Android开发艺术探索》读书笔记 (12) 第12章 Bitmap的加载和Cache

    第12章 Bitmap的加载和Cache 12.1 Bitmap的高速加载 (1)Bitmap是如何加载的?BitmapFactory类提供了四类方法:decodeFile.decodeResourc ...

  5. Android新浪微博客户端(七)——ListView中的图片异步加载、缓存

    原文出自:方杰|http://fangjie.info/?p=193转载请注明出处 最终效果演示:http://fangjie.sinaapp.com/?page_id=54 该项目代码已经放到git ...

  6. 【代码笔记】iOS-实现网络图片的异步加载和缓存

    代码: - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. se ...

  7. BaseHttpListActivity,几行代码搞定Android Http列表请求、加载和缓存

    Android开发中,向服务器请求一个列表并显示是非常常见的需求,但实现起来比较麻烦,代码繁杂. 随着应用的更新迭代,这种需求越来越多,我渐渐发现了实现这种需求的代码的共同点. 于是我将Activit ...

  8. js的动态加载、缓存、更新以及复用(四)

    本来想一气呵成,把加载的过程都写了,但是卡着呢,所以只好在分成两份了. 1.页面里使用<script>来加载 boot.js . 2.然后在boot.js里面动态加载 bootLoad.j ...

  9. Android图片加载与缓存开源框架:Android Glide

    <Android图片加载与缓存开源框架:Android Glide> Android Glide是一个开源的图片加载和缓存处理的第三方框架.和Android的Picasso库类似,个人感觉 ...

随机推荐

  1. 内存映射mmap的几个api及其使用

    内存映射 mmap 内存映射mmap函数的作用是建立一段可以被两个或者多个程度读写的内存段,一个程序对他进行任何修改,对其它程序可见.同样,这个功能可以用在对文件的处理上,mmap函数创建一个指向一个 ...

  2. AHB/APB简介

    AHB AHB总线互联结构图 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大.数字IC从基于时序驱动的设计方法,发展到基于IP复用的设计方法,并在SOC设计中得到了广泛应用.在基于IP复用的 ...

  3. PHP解码unicode编码中文字符代码

    function replace_unicode_escape_sequence($match) { return mb_convert_encoding(pack('H*', $match[1]), ...

  4. JavaScript获取当前值

    JavaScript获取当前值 1.说明        获取select下拉框中的选中的值以及文本值 2.实现源码 <!DOCTYPE html PUBLIC "-//W3C//DTD ...

  5. es6学习笔记--let和const

    今天学习了es6中的let和const命令,借此整理一下笔记. let : let 和 var 的声明方式一样,但有 var 比不上的优点.下面用 var 和 let 的例子来加深对 let 的理解. ...

  6. Keras官方中文文档:keras后端Backend

    所属分类:Keras Keras后端 什么是"后端" Keras是一个模型级的库,提供了快速构建深度学习网络的模块.Keras并不处理如张量乘法.卷积等底层操作.这些操作依赖于某种 ...

  7. mybatis快速入门(六)

    前面mybatis的入门程序基本上都写完了,就看大家的灵活运用了,今天来吧前面的整合一下封装一个公共的BaseDao 只需要把前面的改造下然后创建一个BaseDao的接口,其它的继承BaseDao接口 ...

  8. Codeforces Round #467 (div.2)

    Codeforces Round #467 (div.2) 我才不会打这种比赛呢 (其实本来打算打的) 谁叫它推迟到了\(00:05\) 我爱睡觉 题解 A. Olympiad 翻译 给你若干人的成绩 ...

  9. 【BZOJ2337】Xor和路径(高斯消元)

    [BZOJ2337]Xor和路径(高斯消元) 题面 BZOJ 题解 我应该多学点套路: 对于xor之类的位运算,要想到每一位拆开算贡献 所以,对于每一位拆开来看 好了,既然是按位来算 我们就只需要计算 ...

  10. [HNOI2011]XOR和路径 && [HNOI2013]游走

    [HNOI2011]XOR和路径 题目大意 具体题目:戳我 题目: 给定一个n个点,m条边的有重边.有自环的无向图,其中每个边都有一个边权. 现在随机选择一条1到n的路径,路径权值为这条路径上所有边权 ...