Android:关于位图,内存使用和扩展的问题

时间:2011-06-01 17:48:11

标签: android memory-management bitmap

为了便于阅读,我发布了我的解决方案首先引用的代码示例,然后我在数字列表中列出了我的解决方案的解释。

我一直在努力解决这个问题。我做了很多阅读,在这里问了问题,并进行了实验;但还没有找到一个像样的解决方案。我需要从输入流中读取各种大小的图像,并以我的内存约束允许的高质量显示它们。以下是我考虑过的选项,这些选项对我来说都不是很好。任何帮助或输入将不胜感激。

 public class NativeTest extends Activity
 {
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
       super.onCreate(savedInstanceState);
       double nativeUsage = Debug.getNativeHeapAllocatedSize(); 
       Log.i("memory", nativeUsage+"");
    } 
 }


 double getAvailableMemory()
 {
    //current heap size 
     double heapSize =  Runtime.getRuntime().totalMemory();
    //amount available in heap 
    double heapRemaining = Runtime.getRuntime().freeMemory();   
    double nativeUsage  =  Debug.getNativeHeapAllocatedSize();
     double memoryAvailable = Runtime.getRuntime().maxMemory() - (heapSize - heapRemaining) - nativeUsage;
    return memoryAvailable;
 }

Bitmap createImageTrialAndError(InputStream stream)
{
    Bitmap image = null;
    int dowsample = 1;
    while(image == null)
    {
       try
       {    
        Options opts = new Options();
        opts.inSampleSize =  downsample;
        image = BitmapFactory.decodeStream(imgStream, null, opts);
       }
       catch (OutOfMemoryError ome)
       {
           downsample = downsample * 2;
           Log.i("out of mem", "try using: " + downsample); 
       }
    }

    return image;
}
  1. 理想的解决方案是Bitmap有Bitmap.drawBitmap(inputstream ...)方法。这将允许我从输入流中绘制而无需为Bitmap分配内存。唉,这不是一个选择。
  2. 根据可用内存缩放位图。这涉及检索位图的宽度和高度,计算位图所需的字节数width * height * 4,计算可用内存,然后BitmapFactory.Options.inSampleSize使得位图将使用比可用内存更少的内存。但是,此选项失败,因为我无法找到可靠的远程方法来计算可用内存。下面的getAvailableMemory()方法似乎应该可以工作:它将可用内存计算为最大内存 - 在java堆中使用的内存 - 本机堆中使用的内存。
    不幸的是,这个公式给出了非常不可靠的结果。主要是因为Debug.getNativeHeapAllocatedSize()似乎不是位图内存使用的准确表示。其不准确性的一个明显例子是下面的NativeTest活动。在我的三星Galaxy平板电脑上,输出的日志声明为:3759416.0。 3.75 mb的空活动原生分配,显然不是确定位图缩放的可靠方法。
  3. 以缩放因子1开始,并尝试初始化位图;如果由于内存导致初始化失败,则将缩放因子加倍并重试;并重复此过程直到成功。这由createBitmapTrialAndError()说明。这实际上是惊人的有效而且不是非常慢。但是,这是非常不受欢迎的,因为我在我的应用程序的其他地方使用SoftReferences,并且内存不足会强制收集这些SoftReferences,这会对性能产生重大影响。最初需要知道适当的缩放因子,这样可以避免不必要地收集这些SoftReferences。
  4. 我的最终解决方案似乎有点可疑。基本上它结合了2& 3,但不使用Debug.getNativeAllocatedSize()。相反,我跟踪自己的Bitmap内存分配,通过跟踪我分配位图和计算其内存使用量的任何位置,然后减去我回收的任何位图的内存使用量。我使用此值代替getAvailableMemory()中的nativeUsage,以便计算位图的正确缩放因子。如果在使用此方法时发生Out Of Memory异常,我使用解决方案3作为计算可接受比例的后备方式。显而易见的问题是试图跟踪我自己的本机内存使用量的大量粗略,但对我来说,它似乎是最好的解决方案。

3 个答案:

答案 0 :(得分:1)

正确的方法是在需要缩放时解码您需要显示的尺寸并在图块中对图像进行二次采样。

有一个库应该做到这一点,检查出来: https://github.com/davemorrissey/subsampling-scale-image-view

答案 1 :(得分:0)

android堆的问题是,你实际上并不知道你可以使用多少堆,因为如果你超越了内存限制,任何后台服务都可能在任何时候破坏你的一切。 / p>

为什么不将一个Bitmap保持为您一直在使用的画布的大小,以及一堆下采样的位图?然后,您可以将本机解决方案中的所有图像渲染到画布,并始终为发生的任何更改绘制缩减采样位图。一旦更改结束,或者清楚哪个图像最重要,请将原始分辨率的图像重绘为画布(通过再次访问磁盘)。

答案 2 :(得分:0)

一种简单直接的方法是使用Options的“inJustDecodeBounds”属性。对于您创建的选项对象,将此属性设置为true,然后继续解码流。

返回的位图将为null,这意味着没有为其分配内存,但您可以读取位图的尺寸,从而确定其大小并调整inSampleSize比率。

稍后将inJustDecodeBounds重置为false,现在您已知道缩小因子,因此现在可以生成所需大小的位图。

希望这会有所帮助。