加载流程:
if(内存命中){
从内存中读取
}else{
create AsyncTasks,task中的多个Runnable是通过堆栈先进后出的方式来调度,而非队列式的先进先出,目的是最先加载用户最近划到或打开的图片。
}
AsyncTask:
//do in background——该后台进程在用户scroll列表的时候会暂停,从而减小了列表划动时cpu的overhead,此方法也被ImageLoader和facebook的官方app所使用。
if(磁盘缓存命中){
从缓存中读取
}else{
从网络下载
成功后,存入磁盘缓存并且存入内存
}
内存图片管理
LruCache,容量为1/10运行时内存。以nexus 4为例,内存2G,jvm为示例app分配的可用内存为512MB,那么内存中的图片LruCache大小为51.2MB。
当达到容量后,不要直接recycle bitmap,而是将bitmap加到一个WeakReference的Map中;否则在小内存手机上,很可能会recycle掉一些还被引用着的bitmap。
缓存图片管理
ChocolateCache,策略类似于LruCache,但是对IO读写速度和命中率都做了优化(鸣谢 伯奎)
下载和解析图片的注意事项
黑图问题:
// 如果不根据这个固定的length来生成byte[],而是直接decode
// inputstream ,会造成黑图(图片出现黑色的矩形)
imgData = new byte[length];
byte[] temp = new byte[512];
int readLen = 0;
int destPos = 0;
while ((readLen = mInputStream.read(temp)) > 0) {
System.arraycopy(temp, 0, imgData, destPos, readLen);
destPos += readLen;
}
机型和网络兼容性问题:
三星note3在CDMA网络下得到的是GZIPInputStream(length为-1),其他机型和场景都是FixedLengthInputStream(length为正常大小),所以当length=-1时,采用最基本的InputStream->OutputStream->ByteArray的方式生成图像的字节数组
不同分辨率显示时的自动缩放问题(尤其是表情模块)
解析图片应采用BitmapFactory.decodeStream而不是BitmapFactory.decodeByteArray,因为后者在从source density到target density转换时,不会自动缩放。
例如,某250x250的图片,默认的density是320,但是LG的G2手机density为480,所以此图片应按照480/320=1.5的比例放大并显示到G2手机上。但是decodeByteArray之后得到的图片长宽仍然是250x250,但是如果使用decodeStream则会得到长宽均为250*1.5=375的图片,从而实现了最佳的显示效果。
根本原因详见源码:
public static Bitmap decodeStream (InputStream is, Rect outPadding, Options opts) {
... ...
if (opts == null || (opts. inScaled && opts. inBitmap == null)) {
float scale = 1.0f;
int targetDensity = 0;
if (opts != null) {
final int density = opts. inDensity;
targetDensity = opts. inTargetDensity;
if (density != 0 && targetDensity != 0) {
scale = targetDensity / ( float) density;//请注意这里有计算缩放比例,而decodeByteArray未进行此项操作
}
}
bm = nativeDecodeAsset(asset, outPadding, opts, true, scale);
if (bm != null && targetDensity != 0) bm.setDensity(targetDensity);
finish = false;
} else {
bm = nativeDecodeAsset(asset, outPadding, opts);
}
}
... ...
public static Bitmap decodeByteArray (byte [] data, int offset, int length, Options opts) {
if ((offset | length) < 0 || data. length < offset + length) {
throw new ArrayIndexOutOfBoundsException();
}
Bitmap bm = nativeDecodeByteArray(data, offset, length, opts);
if (bm == null && opts != null && opts. inBitmap != null) {
throw new IllegalArgumentException( "Problem decoding into existing bitmap");
}
return bm;
}
场景策略
网络图片按原尺寸处理
本地图片或拍照图片,因为经常是高清大图,所以需要根据具体情况设置采样率,减小decode bitmap和write to cache时的内存占用(鸣谢 风念)