如何高效使用和管理Bitmap

 我来答
huanglenzhi
推荐于2016-11-08 · 知道合伙人数码行家
huanglenzhi
知道合伙人数码行家
采纳数:117538 获赞数:517195
长期从事计算机组装,维护,网络组建及管理。对计算机硬件、操作系统安装、典型网络设备具有详细认知。

向TA提问 私信TA
展开全部

一、图片加载流程 

        首先,我们谈谈加载图片的流程,项目中的该模块处理流程如下: 

1.在UI主线程中,从内存缓存中获取图片,找到后返回。找不到进入下一步; 

2.在工作线程中,从磁盘缓存中获取图片,找到即返回并更新内存缓存。找不到进入下一步; 

3.在工作线程中,从网络中获取图片,找到即返回并同时更新内存缓存和磁盘缓存。找不到显示默认以提示。 

二、内存缓存类(PanoMemCache)


        这里使用Android提供的LruCache类,该类保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。当cache已满的时候加入新的item时,在队列尾部的item会被回收。


[java] view plain copy print ?

public class PanoMemoryCache {  

  

    // LinkedHashMap初始容量  

    private static final int INITIAL_CAPACITY = 16;  

    // LinkedHashMap加载因子  

    private static final int LOAD_FACTOR = 0.75f;  

    // LinkedHashMap排序模式  

    private static final boolean ACCESS_ORDER = true;  

  

    // 软引用缓存  

    private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;  

    // 硬引用缓存  

    private static LruCache<String, Bitmap> mLruCache;  

      

    public PanoMemoryCache() {  

    // 获取单个进程可用内存的最大值  

    // 方式一:使用ActivityManager服务(计量单位为M)  

        /*int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();*/  

    // 方式二:使用Runtime类(计量单位为Byte)  

        final int memClass = (int) Runtime.getRuntime().maxMemory();  

        // 设置为可用内存的1/4(按Byte计算)  

        final int cacheSize = memClass / 4;  

        mLruCache = new LruCache<String, Bitmap>(cacheSize) {  

            @Override  

            protected int sizeOf(String key, Bitmap value) {  

                if(value != null) {  

                    // 计算存储bitmap所占用的字节数  

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

                }  

            }  

        };  

          

    /* 

    * 第一个参数:初始容量(默认16) 

    * 第二个参数:加载因子(默认0.75) 

    * 第三个参数:排序模式(true:按访问次数排序;false:按插入顺序排序) 

    */  

        mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(INITIAL_CAPACITY, LOAD_FACTOR, ACCESS_ORDER) {  

            private static final long serialVersionUID = 7237325113220820312L;  

            @Override  

            protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {  

                if(size() > SOFT_CACHE_SIZE) {  

                    return true;  

                }  

                return false;  

            }  

        };  

    }  

      

    /** 

     * 从缓存中获取Bitmap 

     * @param url 

     * @return bitmap 

     */  

    public Bitmap getBitmapFromMem(String url) {  

        Bitmap bitmap = null;  

        // 先从硬引用缓存中获取  

        synchronized (mLruCache) {  

            bitmap = mLruCache.get(url);  

            if(bitmap != null) {  

                // 找到该Bitmap之后,将其移到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) {  

                    // 找到该Bitmap之后,将它移到硬引用缓存。并从软引用缓存中删除。  

                    mLruCache.put(url, bitmap);  

                    mSoftCache.remove(url);  

                    return bitmap;  

                } else {  

                    mSoftCache.remove(url);  

                }  

            }  

        }  

        return null;  

    }  

      

    /** 

     * 添加Bitmap到内存缓存 

     * @param url 

     * @param bitmap 

     */  

    public void addBitmapToCache(String url, Bitmap bitmap) {  

        if(bitmap != null) {  

            synchronized (mLruCache) {  

              mLruCache.put(url, bitmap);    

            }  

        }  

    }  

      

    /** 

     * 清理软引用缓存 

     */  

    public void clearCache() {  

        mSoftCache.clear();  

    mSoftCache = null;  

    }  

}  

        补充一点,由于4.0平台以后对SoftReference类引用的对象调整了回收策略,所以该类中的软引用缓存实际上没什么效果,可以去掉。2.3以前平台建议保留。 

三、磁盘缓存类(PanoDiskCache) 

 

五、使用decodeByteArray()还是decodeStream()? 

        讲到这里,有童鞋可能会问我为什么使用BitmapFactory.decodeByteArray(data, 0, data.length, opts)来创建Bitmap,而非使用BitmapFactory.decodeStream(is, null, opts)。你这样做不是要多写一个静态方法readInputStream()吗? 

        没错,decodeStream()确实是该使用情景下的首选方法,但是在有些情形下,它会导致图片资源不能即时获取,或者说图片被它偷偷地缓存起来,交 还给我们的时间有点长。但是延迟性是致命的,我们等不起。所以在这里选用decodeByteArray()获取,它直接从字节数组中获取,贴近于底层 IO、脱离平台限制、使用起来风险更小。 

六、引入缓存机制后获取图片的方法 

[java] view plain copy print ?

/** 

     * 加载Bitmap 

     * @param url 

     * @return  

     */  

    private Bitmap loadBitmap(String url) {  

        // 从内存缓存中获取,推荐在主UI线程中进行  

        Bitmap bitmap = memCache.getBitmapFromMem(url);  

        if(bitmap == null) {  

            // 从文件缓存中获取,推荐在工作线程中进行  

            bitmap = diskCache.getBitmapFromDisk(url);  

            if(bitmap == null) {  

                // 从网络上获取,不用推荐了吧,地球人都知道~_~  

                bitmap = PanoUtils.downloadBitmap(this, url);  

                if(bitmap != null) {  

                    diskCache.addBitmapToCache(bitmap, url);  

                    memCache.addBitmapToCache(url, bitmap);  

                }  

            } else {  

                memCache.addBitmapToCache(url, bitmap);  

            }  

        }  

        return bitmap;  

    }  

七、工作线程池化 

        有关多线程的切换问题以及在UI线程中执行loadBitmap()方法无效的问题,请参见另一篇博文: 使用严苛模式打破Android4.0以上平台应用中UI主线程的“独断专行”。 

有关工作线程的处理方式,这里推荐使用定制线程池的方式,核心代码如下: 

[java] view plain copy print ?

// 线程池初始容量  

private static final int POOL_SIZE = 4;  

private ExecutorService executorService;  

@Override  

public void onCreate(Bundle savedInstanceState) {  

    super.onCreate(savedInstanceState);  

  

    // 获取当前使用设备的CPU个数  

    int cpuNums = Runtime.getRuntime().availableProcessors();  

    // 预开启线程池数目  

    executorService = Executors.newFixedThreadPool(cpuNums * POOL_SIZE);  

  

    ...  

    executorService.submit(new Runnable() {  

        // 此处执行一些耗时工作,不要涉及UI工作。如果遇到,直接转交UI主线程  

        pano.setImage(loadBitmap(url));  

    });  

    ...  

  

}  

        我们知道,线程构造也是比较耗资源的。一定要对其进行有效的管理和维护。千万不要随意而行,一张图片的工作线程不搭理也许没什么,当使用场景变为 ListView和GridView时,线程池化工作就显得尤为重要了。Android不是提供了AsyncTask吗?为什么不用它?其实 AsyncTask底层也是靠线程池支持的,它默认分配的线程数是128,是远大于我们定制的executorService。

保融科技
2024-10-18 广告
司库管理是对企业集团以资金为主要元素的金融资源从筹集到内部配置,再到运营管理所进行的动态及全面管理,并采用信息技术手段实现,使资金管理高度契合集团发展战略。司库管理平台是在外部政策性指导以及企业集团内部自身发展诉求双重背景下产生的集团化资金... 点击进入详情页
本回答由保融科技提供
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式