Comments 8
Спасибо за статью! Интересно читать опыт других людей.
Мы для работы с json используем AndroidAnnotations Rest API (для получения с сервиса json) + Jackson (парсинг). Тоже работает отлично, падений по памяти замечено не было. Ну это, так сказать, может кому-то пригодится наш опыт :)
Мы для работы с json используем AndroidAnnotations Rest API (для получения с сервиса json) + Jackson (парсинг). Тоже работает отлично, падений по памяти замечено не было. Ну это, так сказать, может кому-то пригодится наш опыт :)
+2
Захотелось придраться :)
Во-первых, грузить столько данных за раз, тем более на мобильное устройство, ну это у вас наверное странное странное апи. Как правило, доступ к большому количеству объектов делается со смещением от последнего загруженого (или аналогично), ну ок.
Во-вторых, раз уж вы грузите такие объемы, пихать их все распаршенными в память в любом случае неправильно, т.к. даже если вы сначала запишите весь ответ от сервера в файл или распарсите его целиком налету, то вы же все равно можете выпасть с OutOfMemoryError после его парсинга, решение с файлом и потоковым парсингом всех записей просто сократило вам потребление памяти ~ в 2 раза. Так что, смею заявить, что все предложенные способы упадут с OutOfMemoryError на еще более больших объемах данных.
Как бы сделал я (надеюсь не придется с таким апи работать), я бы последовательно парсил элементы прямо из InputStream от сервера и писал их в БД, зачем? Чтобы минимизировать расходы по памяти, т.к. InputStream освобождается при чтении, а все элементы в памяти мы не храним, пишем все в БД. Соответственно, потом надо будет просто извлекать их уже из БД при отображении в списке.
Естественно, писать в БД можно не прямо последовательно, а накапливая небольшой буфер в памяти. Так же и с чтением из БД при отображении в списке, можно подгружать следующие паки данных из БД пока пользователь не докрутил список до них, можно держать небольшой кеш данных в SoftHashMap чтобы не дергать БД постоянно. Вот как то так :)
Во-первых, грузить столько данных за раз, тем более на мобильное устройство, ну это у вас наверное странное странное апи. Как правило, доступ к большому количеству объектов делается со смещением от последнего загруженого (или аналогично), ну ок.
Во-вторых, раз уж вы грузите такие объемы, пихать их все распаршенными в память в любом случае неправильно, т.к. даже если вы сначала запишите весь ответ от сервера в файл или распарсите его целиком налету, то вы же все равно можете выпасть с OutOfMemoryError после его парсинга, решение с файлом и потоковым парсингом всех записей просто сократило вам потребление памяти ~ в 2 раза. Так что, смею заявить, что все предложенные способы упадут с OutOfMemoryError на еще более больших объемах данных.
Как бы сделал я (надеюсь не придется с таким апи работать), я бы последовательно парсил элементы прямо из InputStream от сервера и писал их в БД, зачем? Чтобы минимизировать расходы по памяти, т.к. InputStream освобождается при чтении, а все элементы в памяти мы не храним, пишем все в БД. Соответственно, потом надо будет просто извлекать их уже из БД при отображении в списке.
Естественно, писать в БД можно не прямо последовательно, а накапливая небольшой буфер в памяти. Так же и с чтением из БД при отображении в списке, можно подгружать следующие паки данных из БД пока пользователь не докрутил список до них, можно держать небольшой кеш данных в SoftHashMap чтобы не дергать БД постоянно. Вот как то так :)
+5
В целом согласен (правда я замерял, разница не в 2 раза, а даже больше, и тем не менее).
Но, если мне нужно скачать за один раз не пачку однотипных объектов, а 5-6 разных типов? Сделать 5 отдельных запросов, а далее парсить каждый… я пробовал, получается медленнее и дольше.
Это первая загрузка данных, последующие тянут только диффы. Так что не всё так страшно. Устанавливая 2Гис тоже первый раз скачиваешь карту, но работа в оффлайне того стоит. ;-)
Но, если мне нужно скачать за один раз не пачку однотипных объектов, а 5-6 разных типов? Сделать 5 отдельных запросов, а далее парсить каждый… я пробовал, получается медленнее и дольше.
Это первая загрузка данных, последующие тянут только диффы. Так что не всё так страшно. Устанавливая 2Гис тоже первый раз скачиваешь карту, но работа в оффлайне того стоит. ;-)
+1
В свое время решил данную проблему с помощью
это аналог SAX парсера для XML, только в отношении Json.
Определяешь какие секции тебе нужны и итеративно их пробегаешь и парсишь.
Метод геморойный, так как это почти ручной парсинг, но самый лучший по использованию памяти,
так как сам определяешь что оставить, а что взять в обработку.
Совместно с описанным выше импортом в базу, самое оптимальное решение.
JsonReader reader = new JsonReader(new InputStreamReader(is, "UTF-8"));
это аналог SAX парсера для XML, только в отношении Json.
Определяешь какие секции тебе нужны и итеративно их пробегаешь и парсишь.
Метод геморойный, так как это почти ручной парсинг, но самый лучший по использованию памяти,
так как сам определяешь что оставить, а что взять в обработку.
Совместно с описанным выше импортом в базу, самое оптимальное решение.
0
Минус – не удастся контролировать процесс скачивания (прервать его адекватным способом), а так же – неизвестно, сколько уже скачано данных. Красивый прогресс-бар не нарисуешь.
Ошиаетесь. Вполне себе стандартный трюк — написать врапер стрима который бы подсчитывал сколько байт через него прошло. Писал и использовал на практике. Начните копать с FilterInputStream, но можно и без него, главное понимание стримов вообще. И прервать его можно вполне адекватно если знать матчасть.
+1
Sign up to leave a comment.
Быстрая десериализация действительно больших JSON-ответов