Java / Android: java.lang.OutOfMemoryError при создании объекта JSON

Я импортирую данные JSON из URI открытой базы данных http://data.seattle.gov/api/views/3k2p-39jp/rows.json, а строки доходят до 445454. Используя следующий код, я создаю объект JSON От всех данных.

HttpGet get = new HttpGet(uri); HttpClient client = new DefaultHttpClient(); HttpResponse response = client.execute(get); BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8")); StringBuilder builder=new StringBuilder(); for(String line=null;(line = reader.readLine()) != null;){ builder.append(line).append("\n"); } JSONTokener jsonTokener=new JSONTokener(builder.toString()); JSONObject finalJson=new JSONObject(jsonTokener); JSONArray data=finalJson.getJSONArray("data"); 

Поскольку данные слишком велики, я получаю 03-21 03:41:49.714: E/AndroidRuntime(666): Caused by: java.lang.OutOfMemoryError указывает на источник ошибки в buildr.append(line).append("\n") . В любом случае я могу обрабатывать большие массивы данных без проблем с распределением памяти?

Solutions Collecting From Web of "Java / Android: java.lang.OutOfMemoryError при создании объекта JSON"

Этот JSON огромен!

Вам определенно нужно использовать потоковый JSON-парсер. Для Android есть два: GSON и Jackson.

Передача GSON Streaming объясняется по адресу: https://sites.google.com/site/gson/streaming

Мне нравится, как GSON объясняет возникшую проблему:

Большинство приложений должны использовать только API объектной модели. Потоковая передача JSON полезна в нескольких ситуациях:

Когда невозможно или нежелательно загружать всю объектную модель в память. Это наиболее актуально для мобильных платформ, где память ограничена.

Поток Джексона документируется по адресу: http://wiki.fasterxml.com/JacksonInFiveMinutes#Streaming_API_Example

Если возможно, запрашивайте только части данных – это также сокращает время для сети io и, таким образом, экономит аккумулятор.

В противном случае вы можете попытаться не хранить входящие данные в памяти, а «перевести» их на SD-карту. Когда он хранится там, вы можете перебрать его. Скорее всего, это будет означать использование вашего собственного токенизатора JSON, который не создает полное дерево, но который может (например, SAX-парсер) смотреть только на часть дерева объектов за раз.

Вы можете взглянуть на Джексона , у которого есть потоковый режим, который может быть применим.

Поток потокового парсера – это путь. Я рекомендую GSON, так как у этого есть небольшая память (просто вытащить парсинг примерно на 16K, Джексон намного больше)

Ваш код проблематичен, потому что вы выделяете:

  • Буфер для хранения всех строковых данных, поступающих из службы
  • Все объекты JSON DOM

И это происходит медленно, и это приводит к спаду памяти.

Если вам нужны java-объекты из ваших данных JSON, вы можете попробовать создать мою небольшую библиотеку привязки данных на GSON (отключить самостоятельную рекламу шамелей):

https://github.com/ko5tik/jsonserializer

Я сделал это по-другому, мой код JSON ожидал статуса, который подходит к концу. Поэтому я изменил код, чтобы вернуться раньше.

 // try to get formattedAddress without reading the entire JSON String formattedAddress; while ((read = in.read(buff)) != -1) { jsonResults.append(buff, 0, read); formattedAddress = ((String) ((JSONObject) new JSONObject( jsonResults.toString()).getJSONArray("results").get(0)) .get("formatted_address")); if (formattedAddress != null) { Log.i("Taxeeta", "Saved memory, returned early from json") ; return formattedAddress; } } JSONObject statusObj = new JSONObject(jsonResults.toString()); String status = (String) (statusObj.optString("status")); if (status.toLowerCase().equals("ok")) { formattedAddress = ((String) ((JSONObject) new JSONObject( jsonResults.toString()).getJSONArray("results").get(0)) .get("formatted_address")); if (formattedAddress != null) { Log.w("Taxeeta", "Did not saved memory, returned late from json") ; return formattedAddress; } }