Java / Android: java.lang.OutOfMemoryError при создании объекта JSON - PullRequest
8 голосов
/ 21 марта 2012

Я импортирую данные 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"). Можно ли в любом случае обрабатывать большие наборы данных без проблем с выделением памяти?

Ответы [ 4 ]

5 голосов
/ 21 марта 2012

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

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

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

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

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

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

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

1 голос
/ 21 марта 2012

Потоковый парсер pull является способом.Я рекомендую GSON, так как у него небольшая память (просто разбор около 16 Кб, Джексон намного больше)

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

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

, и это медленно и вызывает переполнение памяти.

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

https://github.com/ko5tik/jsonserializer

1 голос
/ 21 марта 2012

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

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

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

0 голосов
/ 06 августа 2014

Я сделал это немного по-другому, мой код 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;
            }           
        } 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...