OutOfMemoryError: Failed to allocate when paging through responses

OutOfMemoryError: Failed to allocate when paging through responses

我收到以下 OutOfMemoryError 异常尝试翻阅来自 API

的响应

Caused by: java.lang.OutOfMemoryError: Failed to allocate a 37748744 byte allocation with 25165824 free bytes and 24MB until OOM, target footprint 268261936, growth limit 268435456 at java.util.Arrays.copyOf(Arrays.java:3257) at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:649) at java.lang.StringBuilder.append(StringBuilder.java:203) at com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.readAll(INatCall.java:105) at com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.restCalliNat(INatCall.java:56) at com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.restCalliNat(INatCall.java:75) at com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.restCalliNat(INatCall.java:75) at com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.restCalliNat(INatCall.java:75) at com.richmondgamesstudio.inattaxonomytreeviewer.utils.INatCall.doInBackground(INatCall.java:42)

有没有更有效的方法通过rest处理大量数据? 我的应用程序调用 API,returns 用户的 'entries'。 API 的最大页数是 200。大多数用户将有超过 200 个条目,这对大部分人来说都很好。我的应用程序可以分页并说明这一点。但是,有些用户将拥有 +2000 个条目,而我的应用程序 运行 内存不足,试图遍历这些较大的集合。 我的问题是:

递归休息调用

   private JSONArray restCall(URL url, JSONArray results) {
        Log.d(TAG, "restCalliNat: Start");
        InputStream is = null;
        int totalResults = 0;
        int perPage = 0;
        JSONArray newResults = new JSONArray();
        try {
            is = url.openStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
            String jsonText = readAll(rd); //<-- LINE 56
            is.close();
            rd.close();
            JSONObject json = new JSONObject(jsonText);
            newResults = json.getJSONArray("results");
            totalResults = (int) json.get("total_results");
            perPage = (int) json.get("per_page");

        } catch (IOException | JSONException e) {
            e.printStackTrace();
            return null;
        }

        if (results != null) {
            newResults = concatArray(results, newResults);
        }

        if (totalResults > page*perPage) {

            newResults = restCall(updatePageCount(url), newResults);
        }
        return newResults;
    }

在每个新页面上,新页面都会连接起来,直到我拥有所有条目。

private JSONArray concatArray(JSONArray arr1, JSONArray arr2) {
    JSONArray result = new JSONArray();
    try {
        for (int i = 0; i < arr1.length(); i++) {
            result.put(arr1.get(i));
        }
        for (int i = 0; i < arr2.length(); i++) {
            result.put(arr2.get(i));
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    return result;
}

将 API 响应转换为字符串

private static String readAll(Reader rd) throws IOException {
    StringBuilder sb = new StringBuilder();
    int cp;
    while ((cp = rd.read()) != -1) {
        sb.append((char) cp); //<--- LINE 105
    }
    return sb.toString();
}

and my app is running out of memory

这与错误所说的不完全相同:Caused by: java.lang.OutOfMemoryError: Failed to allocate a 37748744 byte allocation with 25165824 free bytes and 24MB until OOM。您有 24MB 的可用内存。但是,您正在尝试分配一个 36MB 的单个缓冲区,但这是行不通的。即使您有 124MB 或 524MB 的空闲内存,您也可能无法访问单个 36MB 的连续空闲内存块。

Converts API response to String

任一:

  • 一次从您的 REST Web 服务请求更少的项目(而不是 200,尝试 5);或
  • 停止尝试将整个 API 响应读入字符串,因为您的 API 响应非常大

您可能想尝试更现代的解决方案来访问 REST 样式的 Web 服务。例如,Retrofit 相当流行,不需要您将整个原始 JSON 读入单个字符串。即使您想坚持使用 openStream()-on-a-URL 方法,现代 JSON 解析器(Moshi、Gson,甚至 [=29 中的 JsonReader =] SDK) 是流式解析器,不需要您将整个原始 JSON 读入单个字符串。

原来我调用的权限对您可以请求的页面数量有限制。

per_Page 页面的计数限制为 200 个结果。但是 page_count 限制是页面 * per_Page > 1000。所以 per_page 计数是多少并不重要。如果页面 * per_Page > 1000 比它将取消调用。即使在溪流中间。

在清单中添加 android:hardwareAccelerated="false" , android:largeHeap="true" 它适用于某些情况,

<application
    android:allowBackup="true"
    android:hardwareAccelerated="false"
    android:largeHeap="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">