Android 位图解码流OutOfMemory异常

Android 位图解码流OutOfMemory异常,android,bitmap,out-of-memory,Android,Bitmap,Out Of Memory,我正在我的应用程序中使用自己的ViewFlowAndroid示例实现。我正在从web服务下载加密图像,然后将它们保存在SD卡上。我正在使用viewflow动态解密图像并显示它们。但问题是,当用户开始太快地更改图像时,它会向我抛出一个OutOfMemoryException,而我发现/测试的所有信息都不适用于我的情况。以下是我正在使用的: @Override public View getView(int position, View convertView, ViewGroup parent)

我正在我的应用程序中使用自己的
ViewFlow
Android示例实现。我正在从web服务下载加密图像,然后将它们保存在SD卡上。我正在使用viewflow动态解密图像并显示它们。但问题是,当用户开始太快地更改图像时,它会向我抛出一个
OutOfMemoryException
,而我发现/测试的所有信息都不适用于我的情况。以下是我正在使用的:

 @Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.image_item, null);
    }

    try {
        File bufferFile = new File(ids.get(position));
        FileInputStream fis   = new FileInputStream(bufferFile);

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
        IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        CipherInputStream cis = new CipherInputStream(fis, cipher);

        BitmapFactory.Options o = new BitmapFactory.Options();
        final int REQUIRED_SIZE=300*1024;

        //Find the correct scale value. It should be the power of 2.
        int width_tmp= o.outWidth, height_tmp= o.outHeight;
        int scale=1;
        while(true){
            if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                break;
            width_tmp/=2;
            height_tmp/=2;
            scale*=2;
        }

        //Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize=scale;

        Bitmap ops = BitmapFactory.decodeStream(cis,null,o2);
        ((ImageView) convertView.findViewById(R.id.imgView)).setImageBitmap(ops);
        cis.close();
        fis.close();

        System.gc();

    } catch (Exception e) {
        e.printStackTrace();
        ((ImageView) convertView.findViewById(R.id.imgView)).setImageResource(R.drawable.image_unavailablee);
    }

    return convertView;
}
有什么办法解决这个问题吗?

摘自此处:

试试这个:

BitmapFactory.Options o = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];

Bitmap bitmapImage = BitmapFactory.decodeFile(path, options);
而不是:

BitmapFactory.Options o = new BitmapFactory.Options();
    final int REQUIRED_SIZE=300*1024;
所以,在使用BitmapFactory.decodeFile()之前,创建一个16kb的字节数组,并在解码过程中将其传递给临时存储器

希望有帮助! 引用:

使用回收()。它将释放与此位图关联的本机对象,并清除对像素数据的引用

 Bitmap ops = BitmapFactory.decodeStream(cis,null,o2);
        ((ImageView) convertView.findViewById(R.id.imgView)).setImageBitmap(ops);
        cis.close();
        fis.close();
        ops.recycle();
        System.gc();

REQUIRED_SIZE
应包含最大尺寸(宽度、高度,以像素为单位),如

在计算比例因子之前,您还错过了几行将图像边界获取到
BitmapFactory.Options o

    //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(cis, null, o);

    //The new size we want to scale to
    final int REQUIRED_SIZE = 1024;
然后使用
o.outWidth
o.outHeight
计算比例因子。您可能需要再次获取
cis
,以便对流进行实际解码

更新:

此外,您还可以将以下变量作为适配器的成员,并在构造函数中进行初始化

SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());

这应该没有问题。

我一次又一次地遇到同样的问题

这是我的代码,可能有点过火,但我不得不解释这一点,因为不同的相机尺寸,分辨率等,但你必须调整它,以满足你的需要

            BitmapFactory.Options imageOptions = new BitmapFactory.Options();
        imageOptions.inJustDecodeBounds = true;
        ByteArrayInputStream imageByteArrayInputStream = new ByteArrayInputStream(data);
        BitmapFactory.decodeStream(imageByteArrayInputStream, null, imageOptions);
        System.gc();

        // Decode frame size
        BitmapFactory.Options frameOptions = new BitmapFactory.Options();
        frameOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), selectedFrameResourceID, frameOptions);
        System.gc();

        // Scale factor for pre scaling
        int preScaleFactor = 1;
        if (imageOptions.outWidth > frameOptions.outWidth || imageOptions.outHeight > frameOptions.outHeight) {
            preScaleFactor = Math.max(imageOptions.outWidth / frameOptions.outWidth, imageOptions.outHeight / frameOptions.outHeight);
        }

        // Decode with inSampleSize
        BitmapFactory.Options scaleOptions = new BitmapFactory.Options();
        scaleOptions.inSampleSize = preScaleFactor;

        imageByteArrayInputStream = new ByteArrayInputStream(data);
        Bitmap preScaledBitmap = BitmapFactory.decodeStream(imageByteArrayInputStream, null, scaleOptions);
        System.gc();

        Bitmap finalBitmap;

        // Scale factor for precise scaling
        // If the scaled image is not exactly the same size as the frame than resize it precisely
        if (preScaledBitmap.getWidth() != frameOptions.outWidth || preScaledBitmap.getHeight() != frameOptions.outHeight) {
            float scaleFactor = Math.max((float)((float)preScaledBitmap.getWidth() / (float)frameOptions.outWidth), (float)((float)preScaledBitmap.getHeight() / (float)frameOptions.outHeight));
            float scalePercentage = Math.min((float)((float)frameOptions.outWidth / (float)preScaledBitmap.getWidth()), (float)((float)frameOptions.outHeight / (float)preScaledBitmap.getHeight()));

            Matrix matrix = new Matrix();
            matrix.preScale(scalePercentage, scalePercentage);

            // If the capture width for the source is bigger than the actual width of the source, then set is to the max of the actual source width
            int sourceCaptureWidth = (int)(frameOptions.outWidth * scaleFactor);
            if (sourceCaptureWidth > preScaledBitmap.getWidth()) {
                sourceCaptureWidth = preScaledBitmap.getWidth();
            }

            // Same as above but than for the height
            int sourceCaptureHeight = (int)(frameOptions.outHeight * scaleFactor);
            if (sourceCaptureHeight > preScaledBitmap.getHeight()) {
                sourceCaptureHeight = preScaledBitmap.getHeight();
            }

            finalBitmap = Bitmap.createBitmap(preScaledBitmap, 0, 0, sourceCaptureWidth, sourceCaptureHeight, matrix, true);

            preScaledBitmap.recycle();
            preScaledBitmap = null;

希望这有帮助

你在哪个版本的Android上运行这个?如果您在Honeycom或更高版本上运行它,您应该能够使用Eclipse内存分析器查看内存的使用位置

也就是说,一旦不再需要或显示位图,就需要对其调用recycle()(这是Sujit答案的问题)。换句话说,如果位图从屏幕上消失,最好将其循环使用(),然后在返回视图时重新加载。否则,该位图就是usi

为此,在ImageView上调用getDrawable(),在ImageView上调用setImageDrawable(null),然后将drawable强制转换为BitmapDrawable,并在其中循环使用位图


有关Android 3.0之前位图内存如何工作的更多信息,您可以看到我在这个问题上的一篇帖子:

仅供处理大型位图的人员参考,有一篇文章介绍了如何处理此类问题以避免内存不足的最佳方法


希望有帮助

当我尝试使用recycle时,它抛出了一个异常:java.lang.RuntimeException:Canvas:尝试使用一个回收的位图android.graphics。Bitmap@40627090,640x960您可以在finally块中使用recycle()。尝试这样做。事实上,我在getView函数中实现了延迟加载,目前它运行正常,没有任何错误。我想整个问题在于我在getView函数中解码流,它分配了太多内存。无论如何,谢谢你的回答!如果它仍然给我一个错误,我会尝试你的解决方案。没问题,我很高兴你能解决它。我想向你致敬,先生。几年前,我发明了自己的例行程序来做同样的事情,但我更喜欢你的。非常令人印象深刻。虽然我最终偷了机器人提供的“提示”,从莱昂纳多·阿兰戈·巴埃纳的答案,这几乎像是作弊!这是我见过的最好、最干净、最重要、最快的方法。非常感谢您的分享。有时,最好的信息是安卓小组创建的或写的博客,但这是我最后一次看到的地方。
SecretKeySpec keySpec = new SecretKeySpec("01234567890abcde".getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec("fedcba9876543210".getBytes());
            BitmapFactory.Options imageOptions = new BitmapFactory.Options();
        imageOptions.inJustDecodeBounds = true;
        ByteArrayInputStream imageByteArrayInputStream = new ByteArrayInputStream(data);
        BitmapFactory.decodeStream(imageByteArrayInputStream, null, imageOptions);
        System.gc();

        // Decode frame size
        BitmapFactory.Options frameOptions = new BitmapFactory.Options();
        frameOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), selectedFrameResourceID, frameOptions);
        System.gc();

        // Scale factor for pre scaling
        int preScaleFactor = 1;
        if (imageOptions.outWidth > frameOptions.outWidth || imageOptions.outHeight > frameOptions.outHeight) {
            preScaleFactor = Math.max(imageOptions.outWidth / frameOptions.outWidth, imageOptions.outHeight / frameOptions.outHeight);
        }

        // Decode with inSampleSize
        BitmapFactory.Options scaleOptions = new BitmapFactory.Options();
        scaleOptions.inSampleSize = preScaleFactor;

        imageByteArrayInputStream = new ByteArrayInputStream(data);
        Bitmap preScaledBitmap = BitmapFactory.decodeStream(imageByteArrayInputStream, null, scaleOptions);
        System.gc();

        Bitmap finalBitmap;

        // Scale factor for precise scaling
        // If the scaled image is not exactly the same size as the frame than resize it precisely
        if (preScaledBitmap.getWidth() != frameOptions.outWidth || preScaledBitmap.getHeight() != frameOptions.outHeight) {
            float scaleFactor = Math.max((float)((float)preScaledBitmap.getWidth() / (float)frameOptions.outWidth), (float)((float)preScaledBitmap.getHeight() / (float)frameOptions.outHeight));
            float scalePercentage = Math.min((float)((float)frameOptions.outWidth / (float)preScaledBitmap.getWidth()), (float)((float)frameOptions.outHeight / (float)preScaledBitmap.getHeight()));

            Matrix matrix = new Matrix();
            matrix.preScale(scalePercentage, scalePercentage);

            // If the capture width for the source is bigger than the actual width of the source, then set is to the max of the actual source width
            int sourceCaptureWidth = (int)(frameOptions.outWidth * scaleFactor);
            if (sourceCaptureWidth > preScaledBitmap.getWidth()) {
                sourceCaptureWidth = preScaledBitmap.getWidth();
            }

            // Same as above but than for the height
            int sourceCaptureHeight = (int)(frameOptions.outHeight * scaleFactor);
            if (sourceCaptureHeight > preScaledBitmap.getHeight()) {
                sourceCaptureHeight = preScaledBitmap.getHeight();
            }

            finalBitmap = Bitmap.createBitmap(preScaledBitmap, 0, 0, sourceCaptureWidth, sourceCaptureHeight, matrix, true);

            preScaledBitmap.recycle();
            preScaledBitmap = null;