`
yhz61010
  • 浏览: 564414 次
  • 来自: -
博客专栏
63c13ecc-ef01-31cf-984e-de461c7dfde8
libgdx 游戏开发
浏览量:12295
社区版块
存档分类
最新评论

[原创] 连载 4 - 深入讨论 Android 关于高效显示图片的问题 - 如何管理位图内存

阅读更多
  更加详细的说明,可以参阅如下官网地址:http://developer.android.com/training/building-graphics.html

  快速导航
  1. 如何高效的加载大位图。(如何解码大位图,避免超过每个应用允许使用的最大内存)http://yhz61010.iteye.com/blog/1848337
  2. 如何在非 UI 线程处理位图。(如何使用 AsyncTask 在后台线程处理位图及处理并发问题)http://yhz61010.iteye.com/blog/1848811
  3. 如何对位图进行缓存。(如何通过创建内存缓存和磁盘缓存来流畅的显示多张位图)http://yhz61010.iteye.com/blog/1849645
  4. 如何管理位图内存。(如何针对不同的 Android 版本管理位图内存)http://yhz61010.iteye.com/blog/1850232
  5. 如何在 UI 中显示位图。(如何通过 ViewPager 和 GridView 显示多张图片)http://yhz61010.iteye.com/blog/1852927

  如何管理位图内存?
  在http://yhz61010.iteye.com/blog/1849645一文中,我们已经讨论了如何对位图进行缓存处理。但还有一些具体的事情需要做,比如说如何更好的利用垃圾回收器和重用位图等。对于不同的 Android 版本,我们通常有不同的策略。

  在我们开始今天的学习之前,先来了解下 Android 是如何管理位图内存的:
  ・在 Android 2.2 (API level及之前版本,当进行垃圾回收时,会停止你的应用程序线程,这样就会产生一个延迟,从而会影响性能。在 Android 2.3 中添加了并发的垃圾回收器,这就意味着若位图没有被任何对象引用的话,它所占用的内存就会很快被回收。
  ・在 Android 2.3.3 (API level 10) 及之前版本,返回的位图数据是保存在程序内存中的,和位图本身所占的内存是区分开的,位图本身是保存在 Dalvik 堆中的。返回的位图数据虽然保存在内存中,但是所占用的内存并不会按照可预知的方式将其释放掉,从而导致的潜在问题就是应用程序很容易就超过内存限制并导致程序崩溃。从 Android 3.0 (API Level 11) 开始,返回的位图数据及位图本身都被保存在 Dalvik 堆中。

  下面我们就开始学习如何针对不同的 Android 版本进行位图内存优化。

  Android 2.3.3 及早先版本的内存管理
  在 Android 2.3.3 (API level 10)及早先版本中,建议使用 recycle() 进行内存管理。如果在你的程序中显示大量的位图数据,你很可能就会遇到 OutOfMemoryError 错误。recycle() 方法允许应用程序尽可能的回收内存。

  注意:只有当你确信位图不再被使用时,才能调用 recycle() 方法。若你已经调用了 recycle() 方法,但是之后你却再次访问了那些位图,那么你会看到如下错误提示:"Canvas: trying to use a recycled bitmap"。

  如下的代码片断为你演示了如何调用 recycle()。代码中使用了引用计数(保存在 mDisplayRefCount 和 mCacheRefCount 中)用来跟踪位图是正在被显示还是被保存在内存中。当满足如下条件时,位图将会被回收:
  ・引用计数变量 mDisplayRefCount 和 mCacheRefCount 的值都为 0。
  ・bitmap 对象不为 null,并且它还没被回收。
private int mCacheRefCount = 0;
private int mDisplayRefCount = 0;
...
// Notify the drawable that the displayed state has changed.
// Keep a count to determine when the drawable is no longer displayed.
public void setIsDisplayed(boolean isDisplayed) {
    synchronized (this) {
        if (isDisplayed) {
            mDisplayRefCount++;
            mHasBeenDisplayed = true;
        } else {
            mDisplayRefCount--;
        }
    }
    // Check to see if recycle() can be called.
    checkState();
}

// Notify the drawable that the cache state has changed.
// Keep a count to determine when the drawable is no longer being cached.
public void setIsCached(boolean isCached) {
    synchronized (this) {
        if (isCached) {
            mCacheRefCount++;
        } else {
            mCacheRefCount--;
        }
    }
    // Check to see if recycle() can be called.
    checkState();
}

private synchronized void checkState() {
    // If the drawable cache and display ref counts = 0, and this drawable
    // has been displayed, then recycle.
    if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
            && hasValidBitmap()) {
        getBitmap().recycle();
    }
}

private synchronized boolean hasValidBitmap() {
    Bitmap bitmap = getBitmap();
    return bitmap != null && !bitmap.isRecycled();
}

  Android 3.0 及更高版本的内存管理
  Android 3.0 (API Level 11) 引进了 BitmapFactory.Options.inBitmap 字段,如果设置了该属性,那么当使用了带有该 Options 参数的 decode 方法在加载内容时,decode 方法会尝试重用一个已经存在的位图。这就意味着位图内存已经被重用了,从而性能得到了改善,并且移除了内存的分配和解除分配。下面是一些使用 inBitmap 的注意事项:
  ・重用的位图大小必须和源位图大小相同(这样才能保证它们占用相同的内存),并且位图的格式应该是 JPEG 或 PNG(无论是作为资源形式还是数据流形式)。
  ・若设置了重用的位图的 Bitmap.Config 配置,则需要重写 inPreferredConfig 方法。
  ・你应该总是使用位图的解码方法,因为我们不能认为重用的位图是可用的(例如,若位图大小不匹配,就无法保证位图可重用)。

  保存位图便于以后使用
  如下的代码片断为你演示了位图是如何被保存便于以后使用的。当程序运行在 Android 3.0 或更高版本中时,位图将会从 LruCache 中被移除,指向该位图的 soft reference 将被存放在一个 HashSet 中,以便于之后在 inBitmap 中使用:
HashSet<SoftReference<Bitmap>> mReusableBitmaps;
private LruCache<String, BitmapDrawable> mMemoryCache;

// If you're running on Honeycomb or newer, create
// a HashSet of references to reusable bitmaps.
if (Utils.hasHoneycomb()) {
    mReusableBitmaps = new HashSet<SoftReference<Bitmap>>();
}

mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {

    // Notify the removed entry that is no longer being cached.
    @Override
    protected void entryRemoved(boolean evicted, String key,
            BitmapDrawable oldValue, BitmapDrawable newValue) {
        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
            // The removed entry is a recycling drawable, so notify it
            // that it has been removed from the memory cache.
            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
        } else {
            // The removed entry is a standard BitmapDrawable.
            if (Utils.hasHoneycomb()) {
                // We're running on Honeycomb or later, so add the bitmap
                // to a SoftReference set for possible use with inBitmap later.
                mReusableBitmaps.add
                        (new SoftReference<Bitmap>(oldValue.getBitmap()));
            }
        }
    }
....
}

  使用一个已经存在的位图
  我们可以在程序中使用解码方法来判断是否有可重用的位图。例如:
public static Bitmap decodeSampledBitmapFromFile(String filename,
        int reqWidth, int reqHeight, ImageCache cache) {

    final BitmapFactory.Options options = new BitmapFactory.Options();
    ...
    BitmapFactory.decodeFile(filename, options);
    ...

    // If we're running on Honeycomb or newer, try to use inBitmap.
    if (Utils.hasHoneycomb()) {
        addInBitmapOptions(options, cache);
    }
    ...
    return BitmapFactory.decodeFile(filename, options);
}

  上述代码中的 addInBitmapOptions() 方法实现如下。该方法会查找一个已经存在的位图,将并该位图设置给 inBitmap 属性。注意,只有当找到了一个匹配的位图时(我们永远不能假定这种匹配一定能被找到),该方法才会为 inBitmap 赋值。
private static void addInBitmapOptions(BitmapFactory.Options options,
        ImageCache cache) {
    // inBitmap only works with mutable bitmaps, so force the decoder to
    // return mutable bitmaps.
    options.inMutable = true;

    if (cache != null) {
        // Try to find a bitmap to use for inBitmap.
        Bitmap inBitmap = cache.getBitmapFromReusableSet(options);

        if (inBitmap != null) {
            // If a suitable bitmap has been found, set it as the value of
            // inBitmap.
            options.inBitmap = inBitmap;
        }
    }
}

// This method iterates through the reusable bitmaps, looking for one 
// to use for inBitmap:
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
        Bitmap bitmap = null;

    if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
        final Iterator<SoftReference<Bitmap>> iterator
                = mReusableBitmaps.iterator();
        Bitmap item;

        while (iterator.hasNext()) {
            item = iterator.next().get();

            if (null != item && item.isMutable()) {
                // Check to see it the item can be used for inBitmap.
                if (canUseForInBitmap(item, options)) {
                    bitmap = item;

                    // Remove from reusable set so it can't be used again.
                    iterator.remove();
                    break;
                }
            } else {
                // Remove from the set if the reference has been cleared.
                iterator.remove();
            }
        }
    }
    return bitmap;
}

  最后,为了能给 inBitmap 赋值,我们还需要使用如下方法来判断是否有一个可选的位图满足指定的位图大小:
private static boolean canUseForInBitmap(
        Bitmap candidate, BitmapFactory.Options targetOptions) {
    int width = targetOptions.outWidth / targetOptions.inSampleSize;
    int height = targetOptions.outHeight / targetOptions.inSampleSize;

    // Returns true if "candidate" can be used for inBitmap re-use with
    // "targetOptions".
    return candidate.getWidth() == width && candidate.getHeight() == height;
}
2
1
分享到:
评论

相关推荐

    android 位图转单色位图

    在Android开发中,有时我们需要将彩色的位图(Bitmap)转换为单色位图,以实现特定的效果,比如创建简单的二值化图像、节省内存或提高处理速度。本篇文章将详细探讨如何在Android中进行这种转换,从32位深图和24位深...

    Android 大位图压缩方法二

    综上所述,"Android 大位图压缩方法二"可能涵盖了多种位图压缩和管理策略,包括合理解码、内存缓存、异步加载、选择合适的位图格式以及使用高效的压缩算法等。这些技术都是为了在保证图片质量的同时,有效地降低内存...

    android中的位图操作demo

    综上所述,“android中的位图操作demo”主要展示了如何在Android平台上高效、灵活地处理位图,包括加载、绘制、创建倒影等操作,并强调了内存管理和性能优化的重要性。开发者可以通过这个示例学习到Android图形处理...

    android-image-filter-ndk,使用android ndk在c中处理位图的android示例项目.zip

    2. 位图内存管理:由于Bitmap消耗大量内存,需要合理使用`BitmapFactory.Options`进行解码,避免内存溢出。 三、C语言处理位图 1. JNI接口:Java Native Interface(JNI)是Java平台的标准组成部分,用于在Java...

    安卓android上pdf转图片

    处理PDF文件可能会消耗大量内存,因此在实际应用中要注意内存管理。可以考虑分批处理页面,使用流式加载,或者使用异步任务来避免UI冻结。 7. **安全与版权**: 转换过程中要尊重PDF文件的版权。如果PDF包含敏感...

    Android从网络加载图片并显示在ImageView控件上

    4. 解码和缩放图片,避免内存问题。 5. 将Bitmap设置到ImageView显示。 6. 可选地,使用图片加载库如Glide或Picasso优化整个流程。 通过以上步骤,我们可以实现高效、流畅的图片加载效果,提升用户在Android应用中...

    10-android ImageView 图片视图

    - **内存管理**:避免一次性加载大量图片导致内存溢出,合理使用`BitmapFactory.Options`进行解码,如`inSampleSize`控制图片尺寸。 - **资源尺寸适配**:根据不同的屏幕密度提供不同分辨率的图片资源,遵循MDPI、...

    android显示sdcard上的图片

    在Android平台上,显示SD卡上的图片是一项常见的任务,尤其对于...通过熟练掌握这些知识点,开发者可以构建出高效且用户友好的图片显示功能。在实际开发中,还需要根据具体需求和设备特性进行优化,以实现最佳效果。

    Android图片裁剪----移动、缩放图片进行裁剪

    在Android开发中,图片裁剪是一项常见的功能,广泛应用于各种应用程序,如社交应用中的头像设置、拍照应用的编辑功能等。本知识点将详细介绍如何在Android中实现图片的移动和缩放裁剪。 首先,我们需要了解Android...

    新版Android开发教程.rar

    ----------------------------------- Android 编程基础 1 封面----------------------------------- Android 编程基础 2 开放手机联盟 --Open --Open --Open --Open Handset Handset Handset Handset Alliance ...

    fmx-android-imagelist获取图片

    接下来,我们要讨论如何在Android应用程序中从ImageList中获取并显示图片。通常,这涉及到以下几个步骤: 1. **添加图片到ImageList**:在运行时,可以使用`AddImage`方法动态加载图像。例如,如果有一个`ImageList...

    VC 图片控件显示多幅位图

    在VC++编程环境中,图片控件(CStatic)通常是用于展示静态图像的,但通过一些扩展,我们可以让它显示多幅位图,实现动态效果。这个主题涵盖了如何在对话框的图片控件上加载、切换和操作位图。下面将详细讨论实现这...

    Android防止内存溢出浅析.zip

    在Android应用开发中,内存溢出(Out Of Memory,简称OOM)是一个常见的问题,它会导致应用崩溃,严重影响用户体验。理解并防止Android应用中的内存溢出是优化应用性能的关键环节。以下是对Android防止内存溢出的...

    4-11-2-1(动态位图).7z

    【标题】"4-11-2-1(动态位图).7z" 提供的信息表明,这是一个关于安卓程序的压缩文件,其中包含了与动态位图相关的知识点。动态位图是指那些可以随时间变化或者根据用户交互而改变的图像,常见于移动应用,特别是游戏...

    深入理解Linux内存管理

    文章首先介绍了内核开发的基础知识,包括代码管理和物理内存描述,随后深入讨论了页表管理、进程地址空间管理等关键主题,并最终涉及到高级话题如非连续内存分配和Slab分配器。 #### 二、代码管理 代码管理章节...

    电信设备-一种基于Android内核的动态生成位图图像的信息显示方法.zip

    总结来说,基于Android内核的动态生成位图图像的信息显示方法是一种高效且灵活的解决方案,它允许电信设备根据实际需要动态创建和更新图像,同时优化系统资源的使用。通过深入理解Android内核和位图操作,开发者可以...

    android-gif-drawable 支持gif显示的view源码.zip

    总的来说,`android-gif-drawable`库通过高效的GIF解析、内存管理和性能优化,为Android开发者提供了一种高效、灵活的GIF显示解决方案。通过深入研究其源码,我们可以学习到如何处理二进制数据、如何实现动画播放...

    sketch-smooth-corner-android,一个android项目解释了如何在android画布上绘制草图平滑角.zip

    "sketch-smooth-corner-android"项目正是为了解决这个问题。它提供了一种方法,使得在Android应用中,我们可以绘制出具有平滑过渡的草图角,而不是传统锐利的90度角。这种平滑处理对于提升用户界面的视觉吸引力非常...

    笔记分享---Android 高清加载巨图

    本笔记将深入探讨如何高效地加载和管理高清大图,避免内存溢出(Out Of Memory,OOM)问题。 一、Bitmap对象与内存管理 1. Bitmap对象在内存中的存储:Android将Bitmap数据存储为一个像素数组,占用连续的内存空间...

    Android-Android自定义控件之局部图片放大镜--BiggerView

    在Android中,`BiggerView`通常由两部分组成:一个主显示区域(通常是ImageView)用于显示整个图片,以及一个浮动的放大区域(通常是一个小的透明视图),当用户在主显示区域滑动时,这个放大区域会显示用户手指下的...

Global site tag (gtag.js) - Google Analytics