首页 > 代码库 > Android客户端中Bitmap的下载过程和缓存机制

Android客户端中Bitmap的下载过程和缓存机制

加载流程:

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时的内存占用(鸣谢 风念)