加载流程:

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读写速度和命中率都做了优化(鸣谢 伯奎)
 
下载和解析图片的注意事项
下载过程优化:
之前采用ImageLoader时,发现它先将图片下载到文件中,然后再从文件中decode图片并显示,增加了一步IO操作,所以现在直接从网络字节流decode图片并返回。
 
黑图问题:
直接从网络的InputStream解析Bitmap时,会出现黑图(图片中有横向的黑色矩形),所以不直接decode从网络得到的InputStream,而是把stream转换为一个固定长度的byte array,再进行解析,黑图问题解决。
                            // 如果不根据这个固定的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,无法采用以上方法生成byte array),其他机型和场景都是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的图片,从而实现了最佳的显示效果。根本原因可以从BitmapFactory.java的源码中得到解释(第一个方法带有缩放功能,第二个方法则没有):
 
    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;
    }
 
 
场景策略
网络获取的图片都是服务端处理过的图片,按原尺寸处理没有任何问题,但是本地图片或拍照图片,经常是高清大图,极易Out of memory,所以需要根据具体情况设置采样率,减小decode bitmap和write to cache时的内存占用(鸣谢 风念)
 
待优化点
关于BitmapFactory.Options.inBitmap的trade-off:
google推荐使用BitmapFactory.Options.inBitmap属性,将新的图片decode并保存到旧图片的内存(reuse the memory of old bitmaps),从而“removing both memory allocation and de-allocation”,减少GC操作,提升性能。这样做也有一个缺点,如此一来需要把从LruCache中evict出的图片放入软引用(方便用来reuse内存),而非很快就会被回收掉的弱引用,结果会造成不再使用的旧图片大量留驻内存,直到内存紧张时才被回收,虽然会提升一定性能,但是应用会“看上去”比较耗内存。
 

Android客户端中Bitmap的下载过程和缓存机制的更多相关文章

  1. wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)

    wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之L ...

  2. 在android studio中导入github下载的工程

    1.从Github中下载工程压缩包,并将其解压到本地 2.修改文件 假设,解压后的文件目录如下: (1)修改配置文件  xx\build.gradle // Top-level build file ...

  3. Android开发中的输入合法性检验

    Why ? 合法性检查对于程序的健壮性具有重要作用.在Android开发中,良好的合法性检查设计机制可以使程序更加清晰,产生bug更少,交互更加友好. What ? 合法性检查的目的在于确定边界.对于 ...

  4. WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制

    原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...

  5. Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信

    Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...

  6. Android开发中使用七牛云存储进行图片上传下载

    Android开发中的图片存储本来就是比较耗时耗地的事情,而使用第三方的七牛云,便可以很好的解决这些后顾之忧,最近我也是在学习七牛的SDK,将使用过程在这记录下来,方便以后使用. 先说一下七牛云的存储 ...

  7. ONVIF客户端中预置位设置代码实现过程

    simpleOnvif的功能:提供支持Windows.Linux.arm.Android.iOS等各种平台的SDK库,方便集成,二次开发 之前跟大家分享了我们安徽思蔷信息科技的simpleOnvif的 ...

  8. 图片以BLOB存储在后台数据库中,Android客户端要进行读取显示

    解决方法: 1:在后台以InputStream的方式将图片从数据库中读出: public static InputStream getPicInputStream(){ String id = &qu ...

  9. 【转】Android源码下载过程的一些注意事项

    原文网址:http://www.360doc.com/content/14/0113/11/11948835_344809459.shtml 其它一些事项说明: 1.在源代码下载过程中,我们在源代码下 ...

随机推荐

  1. Android中的常见时区

    方法: private void printTimeZone(){ String[] ids= TimeZone.getAvailableIDs(); for (int i = 0; i < i ...

  2. Unreachable catch block for IOException. This exception is never thrown from the try statement body

    Unreachable catch block for IOException. This exception is never thrown from the try statement body ...

  3. bzoj2242

    快速幂 扩展欧几里得 baby-step-giant-step 可以自行baidu 程序附部分注释 ; type link=^node;      node=record        re,wh:l ...

  4. SharePoint 2010中使用SPListItemCollectionPosition更快的结果

    转:http://www.16kan.com/article/detail/318657.html Introduction介绍 In this article we will explore the ...

  5. [C# 网络编程系列]专题三:自定义Web服务器

    转自:http://www.cnblogs.com/zhili/archive/2012/08/23/2652460.html 前言: 经过前面的专题中对网络层协议和HTTP协议的简单介绍相信大家对网 ...

  6. I Hate It HDOJ---1754

    I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  7. 遍历form表单

    //表单 var form = new Ext.form.FormPanel({ //创建表单面板 labelAlign: 'center', //水平对齐方式 layout: 'form', //布 ...

  8. Git报错:insufficient permission for adding an object to repository database .git/objects

    在本地搭建Git服务器后,在开发机上push新代码,发现Git提示: insufficient permission for adding an object to repository databa ...

  9. JavaScript高级程序设计36.pdf

    TreeWalker TreeWalker是NodeIterator的更高级的版本,除了包括nextNode()和previousNode()在内的相同功能外,这个类型还提供了用于不同方向上遍历DOM ...

  10. Intellij 中的git操作 转!

    http://blog.csdn.net/lovesummerforever/article/details/50032937 Git原理以后会分章节介绍,本次主要说一下intellij怎样操作git ...