8

我想它是被问过又问过的,但仍然有些事情我不太明白。

我尝试了两种不同的方法:

  1. 将所有图像保留在内存中,当开始超过某个限制时,开始删除它们
  2. 让 Android 用 SoftReferences 解决这个问题

在 2. 它只是有时我分配它们的第二个清理它们!而且我不会分配太多 - 30-40 图像 50x50 像素。

所以我坚持一个。问题是极限是多少?

  1. 我可以从设备中获得一些关于我还剩下多少位图内存的可靠信息吗?我做了一些研究,观察 DDMS 值,它只是占用越来越多的空间(如果我不清理的话),直到它爆炸。前一刻只剩200K,下一秒系统再提供2M……
  2. 我目前正在根据设备型号或屏幕尺寸使用一些启发式决策。我认为从长远来看这是死胡同。在某些手机上出现内存异常,在其他手机上完全免费。
  3. 是否有第三种解决方案,正确的?
4

5 回答 5

8

最重要的问题:你在recycle()画你的位图吗?这对于 Gingerbread 之前的应用程序非常重要(也可能是 Gingerbread 之后的应用程序)。

观看Google I/O 2011 上的 Android 应用程序内存管理会议帮助我更好地了解了为 Android 开发的特性。

该视频提到了一个名为MAT的工具- 一种内存分析器- 这对于确定内存中是否存在已泄漏的对象非常有用。您可能拥有超过您认为的 30-40 的数量。

对于查看和/或记录堆的当前大小等,我建议使用此答案中关于 Android Out of Memory exceptions的代码。

于 2011-08-10T21:31:28.127 回答
4

图像缓存是我构建并放入应用商店的应用程序的重要组成部分。该应用程序需要下载图像并将它们缓存在内存和 SDCard 上,以便它们的范围超出单次运行。

总体思路是将图像添加到缓存管理器中,a) 通过基于元数据的键将图像存储在关联容器 (HashMap) 中,以及 b) 将图像文件写入 SDCard。

在内存不足的情况下,我释放 HashMap。然而,仍然可以从 SD_Card 中检索图像并再次缓存到内存中。

我能够在不回收的情况下做到这一点,并且仍然没有看到内存问题。据我了解,回收不是必需的,但有助于获得用于“位图”的更早版本的内存,因为在 Gingerbread 之前的操作系统中,位图的分配使用本机内存。即不属于 Dalvik 堆的内存。所以垃圾收集器不会释放这个内存,而是由实现特定的策略释放的。

这是来自 Cache_Manager 类:

public static synchronized void addImage(Bitmap b, String urlString, boolean bSaveToFile, IMAGE_TYPES eIT, boolean bForce)
{
    String szKey = getKeyFromUrlString(urlString, eIT);

    if (false == m_hmCachedImages.containsKey(szKey) || bForce)
    {
        m_hmCachedImages.put(szKey, b);
        if (bSaveToFile)
        {
            boolean bIsNull = false;
            // Write a null object to disk to prevent future query for non-existent image.
            if (null == b)
            {
                try
                {
                    bIsNull = true;
                    b = getNullArt();
                }
                catch (NullPointerException e)
                {
                    e.printStackTrace();
                    throw e;
                }
            }

            // Don't force null art to disk
            if (false == File_Manager.imageExists(szKey) || (bForce && bIsNull == false))
                File_Manager.writeImage(b, szKey);
        }
    }
}

// 这里是 File_Manager 类中 writeImage() 的示例

public static void writeImage(Bitmap bmp, String szFileName)
{
    checkStorage();

    if (false == mExternalStorageWriteable)
    {
        Log.e("FileMan", "No Writable External Device Available");
        return;
    }

    try
    {
        // Create dirctory if doesn't exist
        String szFilePath = getFilesPath();
        boolean exists = (new File(szFilePath)).exists();
        if (!exists)
        {
            new File(szFilePath).mkdirs();
        }

        // Create file
        File file = new File(szFilePath, szFileName);

        // Write to file
        FileOutputStream os = new FileOutputStream(file);
        bmp.compress(Bitmap.CompressFormat.PNG, 90, os);
    } catch (IOException e)
    {
        // Unable to create file, likely because
        // external storage is
        // not currently mounted.
        Log.e("FileMan", "Error writing file", e);
    } catch (Exception e)
    {
        e.printStackTrace();
        throw e;
    }
}
于 2011-08-15T19:46:51.063 回答
2

该限制与运行它的设备上的 VM 堆大小有关,因为这与设备和操作系统不同,操作系统不同,它的范围可以从 16MB(应用程序的总数)到 256MB+(在平板电脑上)。

您将需要将其保持在较低端,为每个设备创建不同的构建,或者让应用程序在运行时检查余量并相应地加载您的图像。

有一些方法可以检查堆中的可用空间量及其大小:

此 API 参考将帮助您使用可用的方法:

http://developer.android.com/reference/android/app/ActivityManager.html#getMemoryClass

于 2011-07-29T16:04:43.823 回答
0

从高级的角度来看,图像加载和缓存是GreenDroid 框架的一部分。一探究竟。

于 2011-08-15T16:31:04.550 回答
-2

您可以使用WebView来显示图像,它具有内置缓存。另外,它支持2指缩放和滚动,无需任何特殊代码。

于 2011-08-15T23:16:05.730 回答