0

我有一个烦人的问题:
我正在从服务器获取大量 GeoJSON 数据。即使有 16MB 堆,即使我多次启动和停止应用程序,这也有效。内存消耗保持不变并且永远不会超过。但是有一种情况是我超过了 16MB 堆。我想简短地描述一下:
该应用程序被主页按钮使用并“退出”,因此该应用程序驻留在后台并且尚未销毁。当应用程序恢复时,作为我应用程序一部分的“控制器”会检查新的 GeoJSON 数据。如果有 GeoJSON 数据更新,应用程序会下载并处理它,问题就从这里开始了。):

private synchronized String readUrlData(HttpURLConnection urlConnection) throws IOException {       
    Log.i(TAG, "Start reading data from URL");
    urlConnection.setReadTimeout(1000 * 45); //45 sec
    Reader reader = new InputStreamReader(urlConnection.getInputStream());

    StringBuilder sb = new StringBuilder(1024 * 16);
    char[] chars = new char[1024 * 16]; //16k
    int len;
    while((len = reader.read(chars)) >= 0) {
        sb.append(chars, 0, len);
    }

    reader.close();

    Log.i(TAG, "Finished reading data from URL");

    return sb.toString();
}

我在append()toString()中得到 OutOfMemory 。显然,当以前以某种方式使用该应用程序时,该应用程序为此占用的内存很少。我已经尝试为上面的代码找到一种对资源更友好的方式,但没有解决方案。同样,如果应用程序是从新启动的,则永远不会出现问题。而且我绝对确定我没有任何内存泄漏,因为

  1. 我用 MAT 检查了这部分和更多内容,占用的空间从未超过 1.6MB(这是 GeoJSON 数据)。
  2. 我以 24MB 的堆大小连续多次执行此用例。

如果发生内存泄漏,它会在第 3 次或第 4 次后以 24MB 堆崩溃,但它运行时没有问题。

我知道如何避免崩溃。我可以向用户显示一个 AlertDialog,告诉他有新的 GeoJSON 数据可用,他需要重新启动应用程序。但是有一个问题!如果应用程序被 Activity 的finish() “终止” ,应用程序仍然保留在内存中,所以当它重新启动时,崩溃再次发生,因为内存永远不会被释放(至少在大多数情况下我不能依赖它)。我已经想通了System.exit(0); 而不是完成会释放所有内存,因为它会杀死整个应用程序,因此在使用新的 GeoJSON 数据重新启动后不会发生崩溃。但我知道这不是一个好的解决方案。我已经尝试过System.gc()在重要部分,但这也不起作用。任何想法如何处理这个问题?可能我需要重新启动应用程序并释放所有已使用的内存。

另一种解决方案可能是重新设计上面的代码,但我认为不可能从中获得更多的 MB。

如果我没有找到合理的解决方案,我将在堆为 16MB 时使用System.exit(0)(我认为有办法检查)来重新启动应用程序。

4

2 回答 2

0

这里有一些想法:

  1. 一旦你知道有什么要读的,就把旧数据设置为空,这样它就会被 GC-ed 。

  2. 使用执行此任务的服务,它将在不同的进程上运行(因此至少有 16 mb 用于此任务)。一旦它处理了数据,以某种方式将其移动到您需要的任何组件,同时将之前的旧数据归零。

  3. 在获取数据时对其进行解码,而不是获取所有数据然后对其进行解码。

  4. 压缩数据,以便在解码时,它会在途中解压缩。

  5. 使用 json 的替代方案,例如 google 的协议缓冲区或您自己的自定义数据类型。

  6. 大多数设备的堆内存都超过 16 mb。您可以简单地将 min sdk 版本设置为 8 或 10,因为大多数内存比这更多的设备也有更高的 API。

于 2012-12-25T20:06:31.493 回答
0

感谢您的回答。最后,我决定让 System.exit(0) 在这种特殊情况下使用,因为数据几乎从未更新过,即使它恰好发生在有人让应用程序在后台运行时也一定是巧合。

于 2012-12-26T17:56:53.040 回答