Как стать автором
Обновить

Комментарии 27

Хм, взяли мы запустили сервис, повернули экран — и все поломалося. Ресивер уже создался новый, а в сервисе лежит старый. Такие ситуации никак не хэндлятся у вас?
Вы правы, не хендлятся. В демо это осознано упрощено дабы показать как работает связка IntentService+ResultReceiver. Понятно, что в реальной жизни нужно обрабатывать перевороты экрана.
Тут как раз и начинается самое интересное. Чтобы его сохранять, надо пихать его в BundleonSaveInstanceState()). ResulReceiver у нас Parceable, так что проблем вроде как нет. Осатается только считать его из savedInstanceState в onCreate().

Вот только я столкнулся с таким непредвиденным поведением: после долгого нахождения приложения в бэкграунде (свернутом сосотянии), когда активити будет разрушена для освобождения памяти для других приложений, а потом когда вы снова развернете ее — то вызовется onCreate(), но в savedInstanceState будет лежать не ваш AppResultsReceiver, а почему-то простой ResultsReceiver. И при попытке закастить его к своему классу получите ClassCastException.

Почему так просходит — для меня загадка. Но именно из-за этого я отказался от ResultReceiver'ов, и стал использовать LocalBroadcastManager как замену. Надо сказать более удобно и менее геморно стало.
А разве IntentService не стопает себя после того, как отработает onHandleIntent()? Стопает. Почему бы тогда в вашей ситуации просто заново не делать startService() вместо попытки прикастить полученный инстанс из бандла?
Если я вызову startService() снова, то и onHandleIntent() у меня вызовется снова (после того как отработает предыдущий). Это что, тогда сколько раз я экран поверну, столько раз и и выполнится onHandleIntent()? Зачем мне такая логика?
Если ResultReceiver хранить в объекте Application, такую проблему можно обойти.
Как член класса? Не очень красивое решение.
Не напрямую, конечно.
Этот подход даже статью был оформлен не так давно.

habrahabr.ru/post/144275/
А если у Вас разные команды, поэтому resultreciver в разных activity будут по сути разные, сторить все в application явно не тру.
Забыли указать главное ограничение IntentService — полученные задачи выполняются один за одним, по очереди.
В основном IntentService подходит для обработки событий от других компонентов системы, например, ответов от AlarmManager, броадкастов (своих или системных), реакция на события от виджетов и тд.

Таким образом, для работы с сетью, IntentService уже не подходит, конечно же если мы говорим о выполнении нескольких запросов одновременно, а обычно это так.

Для работы с сетью все же придется использовать Service+Threads.
Таким образом, для работы с сетью, IntentService уже не подходит, конечно же если мы говорим о выполнении нескольких запросов одновременно, а обычно это так.

В случае, когда нужно организовать синхронное общение с сетью (например, общение с REST API) то IntentService подойдет нормально.
А вот если нужно делать параллельные асинхронные запросы — то да, согласен с вами
Синхронное общение с сетью на столько редко, что практически никогда. А даже если и когда, то в таком случае смысла в сервисе мало, хватит простого асинктаска.
ну вот бывают случаи, когда нужно засинкать локальную бд с веб сервисом, а потом оповестить об этом UI. и при это весь синк выполняется в 1-2 синхронных запроса. идеально, на мой взгляд, подходит IntentService
Ну то есть для работы с сетью все равно нужен будет еще один отдельный сервис?
ага, для работы с сетью, не имеющей отношения к основному синку данных, отдельный сервис/асинктаск/лоадер
Об этом собственно я и написал в самом первом комментарии, что IntentService ограничен и подходит для одиночных долго играющих запросов. Для чего то более динамичного, как например, работа с сетью, тот же rest api, уже не подходит.

Просто не плохо было бы об этом упомянуть в самой статье, плюсы и минусы, дабы не выглядело что IntentService — это универсальное решение.
в продолжение дискуссии
вот только наткнулся на класс MultiThreadedIntentService. Наследует обычный Service и делает из него IntentService, только уже с возможностью выполнения нескольких задач одновременно.
Очень хорошая статья, для понимания как правильно надо делать.

Но если не хочется самому все это делать, есть уже готовые библиотеки.
Например, github.com/octo-online/robospice, они как раз делают все через сервис, + поддержка кеширования и тд.
Можно даже поковырять их код для более лучшего понимания.

У них кстати есть интересная инфографика, показывающая возможные проблемы AsyncTask и Loader, в сравнении с подходом работы через сервис:
Показать картинку
image


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

Но опять же появляются новые проблемы:
1. все изменения связанные со сменой конфигурации надо обновлять самому, а это очень много работы.
2. возможность легко сделать утечку памяти в п1
3. если вы не завершите асинктаск ваша активити будет жить, а значит снова memory leak. Как такое может произойти — How to Leak a Context: Handlers & Inner Classes

Из плюсов сервиса: даже если пользователь не дождался завершения запроса, не страшно, его можно до выполнить, закешировать и показать моментально при следующем открытии.

Как альтеративный вариант можно использовать retain fragment без UI, и уже в нем создавать AsyncTask. Таким образом можно избавится хотя бы от проблемы с изменениями конфигурации при использовании AsyncTask.
1. Возможно, у кого-то этото впорос стоит остро, но у меня не было задачи менять структуру лейаута при повротах. Поэтому все корректно масштабировалось само. А где лейаут у меня строился в зависимости от размера экарна я и так его просчитывал в ручную.
2. Не верю
3. Зачем из мухи делать слона? Будет она жить ровно столько, сколько будет жить асинктаск. Если это сетевой запрос, то максимум отвалится по таймауту, а дальше сборщик все сожрет за милую душу.

А как потом обновлять UI из фрагмента?
Вообще-то поддерживать разные ориентации экранов в приложении является даже больше, чем правило хорошего тона.
Возможно, у кого-то этото впорос стоит остро, но у меня не было задачи менять структуру лейаута при повротах. Поэтому все корректно масштабировалось само. А где лейаут у меня строился в зависимости от размера экарна я и так его просчитывал в ручную.

Комрад имел ввиду не столько изменение UI, сколько обработку сохранения и восстановления текущего состояния активити.
2. Не верю

Верьте
Вообще-то поддерживать разные ориентации экранов в приложении является даже больше, чем правило хорошего тона.(хм почему то blockquote не пашет наверно из-за рейтинга)

Эм я не говорил про отключение смены ориентации девайса. А про отключение пересоздании активити при смене ориентации. Все вертится и ресайзится без проблем. Пробелмы повялются, если у вас под разные ориентации разные ресурсы юзаются, или разная структура лейаута, тогда их надо самим менять.
>Вообще-то поддерживать разные ориентации экранов в приложении является
>даже больше, чем правило хорошего тона.
Видимо 99% игроделов эти правила не читали.
> А как потом обновлять UI из фрагмента?

Просто слушаете оповещение и уже выполняете необходимые действия с UI.
Например как то так:
1. Делаем запрос на выполнение чего либо в фоне.
2. В самом же фрагменте в отдельном треде уже проверяем актуальность данных, и либо возвращаем уже существующие, либо делаем новую фоновую задачу и по завершению оповещаем слушателей.

Что будет во время поворота:
1. активити будет безболезненно пересоздана.
2. а вот все что во фрагменте было сохранится, по сути это тоже самое что вы делали игнорируя изменения для активити, только в этом случае это ограничивается приделами фрагмента.

Этот же способ можно использовать и для хранения временных данных при поворотах.
Например, у нас есть ответ от сервера, скажем какой то список, в базу мы пока не хотим кешировать и нам нужно что бы разметка при поворотах нормально подхватывалась в зависимости от конфигурации, и статику тоже не хотим использовать. Тогда делаем отдельный ретеин фрагмент и храним все в нем.
Может порекамендуете какую-нибть статью или туториал по данной тематике?
Конкретнее инетерсует, как правильно закодить UI, обновление модели в бекграунде, и обновления UI по модели
у асинк тасков проблема не только с поворотами. вам могут банально позвонить когда ваше приложение работает.
а еще пользователь может сам свернуть ваше приложение и уйти по своим делам, такое тоже часто случается.
тысячи случаев, на самом деле, андроид так непредсказуем
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории