`

use a recycled bitmap 的发现

 
阅读更多

 

    最近在做一个产品,里面有个用户指南的功能,该功能就是介绍怎么使用这个APP,然后是一个可以上下滚动的视图。其实就是一张图片。不过由于这张图片很大,所以用户退出这个界面的时候,必须回收资源。就是这个回收资源让我碰到了一问题。引发use a recycled bitmap的操作流程是这样子的,我进入Activity,然后得到图片并且显示出来, 退出时,在onDestroy()方法中recycle掉这个Bitmap对象。然后再次进入此界面,程序就爆了这个错误。

      先看关键代码:   在onCreate(Bundle)中

       

ImageView iv = (ImageView) findViewById(R.id.user_guide_iv);
Drawable draw = getResources().getDrawable(R.drawable.user_guide);
mUserGuideBmp = ((BitmapDrawable) draw).getBitmap();
iv.setImageBitmap(mUserGuideBmp);

 

            然后在 onDestroy()中回收资源
  

if (mUserGuideBmp!= null && !mUserGuideBmp.isRecycled()) {
	mUserGuideBmp.recycle();
	mUserGuideBmp= null;
}

 
    这些看起来都很正常,可是就是报错了。说我用了一个回收过的资源。难道我再次进入这个界面得到的还是我上次用的那个Bitmap的索引?如果是这样的,那么getResources().getDrawable()得到的也应该是我上次得到的Drawable了,因为我的Bitmap是该Drawable对象的成员变量。好吧,看getResources().getDrawable源码:

 

public Drawable getDrawable(int id) throws NotFoundException {
        synchronized (mTmpValue) {
            TypedValue value = mTmpValue;
            getValue(id, value, true);
            return loadDrawable(value, id);
        }
    }

 明显可以看出是是loadDrawable()方法得到Drawable对象,这个方法太多了,我们看下关键性的地方

 

 boolean isColorDrawable = false;
        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
            isColorDrawable = true;
        }
        final long key = isColorDrawable ? value.data :
                (((long) value.assetCookie) << 32) | value.data;

        Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);

        if (dr != null) {
            return dr;
        }

  可以看到有个getCachedDrawable(),看来第二次我们得到的Drawable对象是从缓存中得到的, 而缓存中的Drawable对象的成员变量Bitmap已经被我们给回收了,难怪会报错。知道原因了,就可以改了,我首先想到的清空下缓存,缓存没有了,Android应该会重新创建一个Drawable对象再加入缓存才对。由上面的方法可以看出缓存对象是mDrawableCache。那找下哪几个地方用到了mDrawableCache。不负众望,我在

public void updateConfiguration(Configuration config,DisplayMetrics metrics, CompatibilityInfo compat) 

方法中找到了

 clearDrawableCache(mDrawableCache, configChanges);

 这个方法明显是清空缓存的,从updateConfiguration的代码中可以看出,这个方法一定会被执行。那我们就在获取Drawable之前调用这个方法就可以了,试试

Configuration config = getResources().getConfiguration();
DisplayMetrics metrics = getResources().getDisplayMetrics();
getResources().updateConfiguration(config, metrics);

 其实我们并不是想要更改Configuration,所以传递给updateConfiguration方法的参数还是它原来的参数。

运行一下,报错,还是use a recycled bitmap的错误。晕,那好吧,看下clearDrawableCache方法

 private void clearDrawableCache(
            LongSparseArray<WeakReference<ConstantState>> cache,
            int configChanges) {
        int N = cache.size();
        if (DEBUG_CONFIG) {
            Log.d(TAG, "Cleaning up drawables config changes: 0x"
                    + Integer.toHexString(configChanges));
        }
        for (int i=0; i<N; i++) {
            WeakReference<Drawable.ConstantState> ref = cache.valueAt(i);
            if (ref != null) {
                Drawable.ConstantState cs = ref.get();
                if (cs != null) {
                    if (Configuration.needNewResources(
                            configChanges, cs.getChangingConfigurations())) {
                        if (DEBUG_CONFIG) {
                            Log.d(TAG, "FLUSHING #0x"
                                    + Long.toHexString(mDrawableCache.keyAt(i))
                                    + " / " + cs + " with changes: 0x"
                                    + Integer.toHexString(cs.getChangingConfigurations()));
                        }
                        cache.setValueAt(i, null);
                    } else if (DEBUG_CONFIG) {
                        Log.d(TAG, "(Keeping #0x"
                                + Long.toHexString(cache.keyAt(i))
                                + " / " + cs + " with changes: 0x"
                                + Integer.toHexString(cs.getChangingConfigurations())
                                + ")");
                    }
                }
            }
        }
    }

 

可以看到是下面这段代码才恩清空缓存

if (Configuration.needNewResources(
                            configChanges, cs.getChangingConfigurations())) {
                        if (DEBUG_CONFIG) {
                            Log.d(TAG, "FLUSHING #0x"
                                    + Long.toHexString(mDrawableCache.keyAt(i))
                                    + " / " + cs + " with changes: 0x"
                                    +           Integer.toHexString(cs.getChangingConfigurations()));
                        }
                        cache.setValueAt(i, null);
   } 

 

 那么

Configuration.needNewResources(configChanges,cs.getChangingConfigurations())

 方法就是判断你的配置是否改变了,可以发现配置没改变就不会清空缓存,看来android还是挺聪明的嘛。

可是我们不能为了清空缓存而去更改配置啊,那怎么办?

   突然发现我好傻,我得到Bitmap的方法换一种就可以了啊。我不要Drawable对象的成员变量保存的那个Bitmap就可以了啊。看下面获取Bitmap对象的方法:

Resources res = getResources();
Bitmap bmp = BitmapFactory.decodeResource(res, drawId);

 用这个方法获取的Bitmap对象跟上面的不同,它会每次都创建一个Bitmap对象,所以可以大胆的回收,程序通过,没问题。我可以深藏功与名啦

 

转载请注明出处:http://892848153.iteye.com/admin/blogs/2090216

 

 最后再记录一下关于图片的一些知识:

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,

因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。

因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,

decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,

无需再使用java层的createBitmap,从而节省了java层的空间。

根据Android版本的不同,bitmap data存放的位置是不同的,3.0以前是分配在native heap上,3.0以后是分配在VM heap上。要想把图片放在native heap里面只要使用BitmapFactory.decodeStream方法解析出Bitmap就可以了。

Bitmap类的构造方法都是私有的,所以开发者不能直接new出一个Bitmap对象,只能通过BitmapFactory类的各种静态方法来实例化一个Bitmap。仔细查看BitmapFactory的源代码可以看到,生成Bitmap对象最终都是通过JNI调用方式实现的。所以,加载Bitmap到内存里以后,是包含两部分内存区域的。简单的说,一部分是Java部分的,一部分是C部分的。这个Bitmap对象是由Java部分分配的,不用的时候系统就会自动回收了,但是那个对应的C可用的内存区域,虚拟机是不能直接回收的,这个只能调用底层的功能释放。所以需要调用recycle()方法来释放C部分的内存。从Bitmap类的源代码也可以看到,recycle()方法里也的确是调用了JNI方法了的。(这些都是网络上的帖子,待验证)

分享到:
评论

相关推荐

    android截屏功能demo

    4. **转换为Bitmap**:将帧缓冲数据转换为Android系统的Bitmap对象,这可以通过创建一个新的Bitmap并设置其像素数据来完成。 5. **保存截屏图片**:最后,可以将Bitmap对象保存为JPEG或PNG格式的图片文件到用户的...

    bitmap内存问题

    bitmap.isRecycled()) { bitmap.recycle(); System.out.println("=============recycle bitmap======="); } } cache.clear(); } ``` 上述代码展示了如何在需要的时候释放缓存中的`Bitmap`对象。 ### 4. 动态...

    Android图片缓存之Bitmap详解(一)

    - `isRecycled()` 可以检查Bitmap是否已经被回收,避免对回收后的Bitmap进行操作。 2. **尺寸信息**: - `getWidth()` 和 `getHeight()` 分别返回Bitmap的宽度和高度,这是计算内存消耗和进行图像操作的基础。 3...

    Recycled病毒专杀

    【标题】"Recycled病毒专杀"涉及到的是针对名为"Recycled"的计算机病毒的清除工具。这个病毒可能对用户的系统造成威胁,如数据丢失、系统性能下降甚至瘫痪。"Recycled"病毒通常会利用系统的漏洞进行传播,并且可能...

    USER_KEY_RECYCLED(解决方案).md

    USER_KEY_RECYCLED(解决方案).md

    Android Bitmap详解及Bitmap的内存优化

    - `isRecycled()`: 检查位图是否已被回收,如果返回true,则不应再使用。 - `getWidth()` 和 `getHeight()`: 获取位图的宽度和高度。 - `isMutable()`: 判断位图是否可修改,若返回true,可以使用`copyConfig`...

    Android实现将View保存成Bitmap的方法

    bitmap.isRecycled()) { bitmap.recycle(); bitmap = null; } view.setDrawingCacheEnabled(false); return bmp; } public static Bitmap duplicateBitmap(Bitmap bmpSrc){ // ... (见下文) } ``` 这种方法...

    UL 2809:2020 Environmental Claim Validation Procedure(ECVP) for Recycled Content - 最新完整英文版(21页).pdf

    UL 2809:2020 Environmental Claim Validation Procedure(ECVP) for Recycled Content UL 2809Environmental Claim Validation Procedure (ECVP) for Recycled Content是 Underwriters Laboratories Inc. (UL)发布...

    C# Design Patterns: A Tutorial

    C# Design Patterns: A Tutorial is a practical guide to writing C# programs using the most common patterns. This tutorial begins with clear and concise introductions to C#, object-oriented ...

    解析Android开发优化之:对Bitmap的内存优化详解

    bitmap.isRecycled()) { bitmap.recycle(); bitmap = null; } System.gc(); ``` 这段代码首先检查Bitmap是否已被回收,然后调用`recycle()`,接着将引用设为null,最后尝试触发垃圾回收。值得注意的是,`System....

    Recycled藏文件.rar_hidefile.h_回收站_隐藏

    然而,这种隐藏方法并不是绝对的安全措施,因为熟练的用户或者恶意软件仍有可能发现并访问这些隐藏文件。因此,如果需要更高的安全性,应该结合使用更复杂的加密和权限控制手段。 在处理这类隐藏文件时,用户需要...

    疯狂android资料:第七章图形与图像处理.doc

    值得注意的是,由于内存限制,频繁创建Bitmap可能导致内存溢出(OutOfMemoryError),因此,Android提供了isRecycled()和recycle()方法来检查和回收Bitmap,以优化内存管理。 Android的绘图系统基于Canvas和Paint类...

    android 分享微信小程序+压缩图片优化

    scaledBitmap.isRecycled()) { Bitmap compressedBitmap = Bitmap.createBitmap(scaledBitmap.getWidth(), scaledBitmap.getHeight(), Bitmap.Config.ARGB_8888); compressedBitmap.compress(Bitmap....

    search engine optimization for dummies

    Bulk up your site with content—original or recycled Use specialized search systems to attract valuable, highly targeted traffic Use link popularity to boost your position and your PageRank ...

    自定义dialog

    ... import android.app.Dialog; import android.content.Context;... .append("Attempting to draw with recycled bitmap. View ID = "); System.out.println("localStringBuilder=="+localStringBuilder); } } }

    yantubbs-ConcreteBone.rar_Recycled concrete_yantubbs_再生骨料_模拟混凝土_

    在标题"yantubbs-ConcreteBone.rar_Recycled concrete_yantubbs_再生骨料_模拟混凝土_"中,我们可以看到这是关于再生混凝土在结构分析中的应用,特别是通过MATLAB程序进行有限元模拟。 有限元方法(Finite Element ...

Global site tag (gtag.js) - Google Analytics