Comments 27
Хм, взяли мы запустили сервис, повернули экран — и все поломалося. Ресивер уже создался новый, а в сервисе лежит старый. Такие ситуации никак не хэндлятся у вас?
Вы правы, не хендлятся. В демо это осознано упрощено дабы показать как работает связка IntentService+ResultReceiver. Понятно, что в реальной жизни нужно обрабатывать перевороты экрана.
Тут как раз и начинается самое интересное. Чтобы его сохранять, надо пихать его в Bundle (в onSaveInstanceState()). ResulReceiver у нас Parceable, так что проблем вроде как нет. Осатается только считать его из savedInstanceState в onCreate().
Вот только я столкнулся с таким непредвиденным поведением: после долгого нахождения приложения в бэкграунде (свернутом сосотянии), когда активити будет разрушена для освобождения памяти для других приложений, а потом когда вы снова развернете ее — то вызовется onCreate(), но в savedInstanceState будет лежать не ваш AppResultsReceiver, а почему-то простой ResultsReceiver. И при попытке закастить его к своему классу получите ClassCastException.
Почему так просходит — для меня загадка. Но именно из-за этого я отказался от ResultReceiver'ов, и стал использовать LocalBroadcastManager как замену. Надо сказать более удобно и менее геморно стало.
Вот только я столкнулся с таким непредвиденным поведением: после долгого нахождения приложения в бэкграунде (свернутом сосотянии), когда активити будет разрушена для освобождения памяти для других приложений, а потом когда вы снова развернете ее — то вызовется onCreate(), но в savedInstanceState будет лежать не ваш AppResultsReceiver, а почему-то простой ResultsReceiver. И при попытке закастить его к своему классу получите ClassCastException.
Почему так просходит — для меня загадка. Но именно из-за этого я отказался от ResultReceiver'ов, и стал использовать LocalBroadcastManager как замену. Надо сказать более удобно и менее геморно стало.
А разве IntentService не стопает себя после того, как отработает onHandleIntent()? Стопает. Почему бы тогда в вашей ситуации просто заново не делать startService() вместо попытки прикастить полученный инстанс из бандла?
Если ResultReceiver хранить в объекте Application, такую проблему можно обойти.
Забыли указать главное ограничение IntentService — полученные задачи выполняются один за одним, по очереди.
В основном IntentService подходит для обработки событий от других компонентов системы, например, ответов от AlarmManager, броадкастов (своих или системных), реакция на события от виджетов и тд.
Таким образом, для работы с сетью, IntentService уже не подходит, конечно же если мы говорим о выполнении нескольких запросов одновременно, а обычно это так.
Для работы с сетью все же придется использовать Service+Threads.
В основном IntentService подходит для обработки событий от других компонентов системы, например, ответов от AlarmManager, броадкастов (своих или системных), реакция на события от виджетов и тд.
Таким образом, для работы с сетью, IntentService уже не подходит, конечно же если мы говорим о выполнении нескольких запросов одновременно, а обычно это так.
Для работы с сетью все же придется использовать Service+Threads.
Таким образом, для работы с сетью, IntentService уже не подходит, конечно же если мы говорим о выполнении нескольких запросов одновременно, а обычно это так.
В случае, когда нужно организовать синхронное общение с сетью (например, общение с REST API) то IntentService подойдет нормально.
А вот если нужно делать параллельные асинхронные запросы — то да, согласен с вами
Синхронное общение с сетью на столько редко, что практически никогда. А даже если и когда, то в таком случае смысла в сервисе мало, хватит простого асинктаска.
ну вот бывают случаи, когда нужно засинкать локальную бд с веб сервисом, а потом оповестить об этом UI. и при это весь синк выполняется в 1-2 синхронных запроса. идеально, на мой взгляд, подходит IntentService
Ну то есть для работы с сетью все равно нужен будет еще один отдельный сервис?
ага, для работы с сетью, не имеющей отношения к основному синку данных, отдельный сервис/асинктаск/лоадер
Об этом собственно я и написал в самом первом комментарии, что IntentService ограничен и подходит для одиночных долго играющих запросов. Для чего то более динамичного, как например, работа с сетью, тот же rest api, уже не подходит.
Просто не плохо было бы об этом упомянуть в самой статье, плюсы и минусы, дабы не выглядело что IntentService — это универсальное решение.
Просто не плохо было бы об этом упомянуть в самой статье, плюсы и минусы, дабы не выглядело что IntentService — это универсальное решение.
в продолжение дискуссии
вот только наткнулся на класс MultiThreadedIntentService. Наследует обычный Service и делает из него IntentService, только уже с возможностью выполнения нескольких задач одновременно.
вот только наткнулся на класс MultiThreadedIntentService. Наследует обычный Service и делает из него IntentService, только уже с возможностью выполнения нескольких задач одновременно.
Очень хорошая статья, для понимания как правильно надо делать.
Но если не хочется самому все это делать, есть уже готовые библиотеки.
Например, github.com/octo-online/robospice, они как раз делают все через сервис, + поддержка кеширования и тд.
Можно даже поковырять их код для более лучшего понимания.
У них кстати есть интересная инфографика, показывающая возможные проблемы AsyncTask и Loader, в сравнении с подходом работы через сервис:
Но если не хочется самому все это делать, есть уже готовые библиотеки.
Например, github.com/octo-online/robospice, они как раз делают все через сервис, + поддержка кеширования и тд.
Можно даже поковырять их код для более лучшего понимания.
У них кстати есть интересная инфографика, показывающая возможные проблемы AsyncTask и Loader, в сравнении с подходом работы через сервис:
Показать картинку
Хм сейчас гляну. В совем приложении использовал асинктаски, но я отключил пересоздание активити при повротах экрана и при необходимости, должен сказать оооооочень редкой, сам хендлил необходимые изменения, связанные с изменением лейаута.
Да, самый простой способ избавиться от проблемы при повороте — это отключить или игнорировать его.
Но опять же появляются новые проблемы:
1. все изменения связанные со сменой конфигурации надо обновлять самому, а это очень много работы.
2. возможность легко сделать утечку памяти в п1
3. если вы не завершите асинктаск ваша активити будет жить, а значит снова memory leak. Как такое может произойти — How to Leak a Context: Handlers & Inner Classes
Из плюсов сервиса: даже если пользователь не дождался завершения запроса, не страшно, его можно до выполнить, закешировать и показать моментально при следующем открытии.
Как альтеративный вариант можно использовать retain fragment без UI, и уже в нем создавать AsyncTask. Таким образом можно избавится хотя бы от проблемы с изменениями конфигурации при использовании AsyncTask.
Но опять же появляются новые проблемы:
1. все изменения связанные со сменой конфигурации надо обновлять самому, а это очень много работы.
2. возможность легко сделать утечку памяти в п1
3. если вы не завершите асинктаск ваша активити будет жить, а значит снова memory leak. Как такое может произойти — How to Leak a Context: Handlers & Inner Classes
Из плюсов сервиса: даже если пользователь не дождался завершения запроса, не страшно, его можно до выполнить, закешировать и показать моментально при следующем открытии.
Как альтеративный вариант можно использовать retain fragment без UI, и уже в нем создавать AsyncTask. Таким образом можно избавится хотя бы от проблемы с изменениями конфигурации при использовании AsyncTask.
1. Возможно, у кого-то этото впорос стоит остро, но у меня не было задачи менять структуру лейаута при повротах. Поэтому все корректно масштабировалось само. А где лейаут у меня строился в зависимости от размера экарна я и так его просчитывал в ручную.
2. Не верю
3. Зачем из мухи делать слона? Будет она жить ровно столько, сколько будет жить асинктаск. Если это сетевой запрос, то максимум отвалится по таймауту, а дальше сборщик все сожрет за милую душу.
А как потом обновлять UI из фрагмента?
2. Не верю
3. Зачем из мухи делать слона? Будет она жить ровно столько, сколько будет жить асинктаск. Если это сетевой запрос, то максимум отвалится по таймауту, а дальше сборщик все сожрет за милую душу.
А как потом обновлять UI из фрагмента?
Вообще-то поддерживать разные ориентации экранов в приложении является даже больше, чем правило хорошего тона.
Комрад имел ввиду не столько изменение UI, сколько обработку сохранения и восстановления текущего состояния активити.
Верьте
Возможно, у кого-то этото впорос стоит остро, но у меня не было задачи менять структуру лейаута при повротах. Поэтому все корректно масштабировалось само. А где лейаут у меня строился в зависимости от размера экарна я и так его просчитывал в ручную.
Комрад имел ввиду не столько изменение UI, сколько обработку сохранения и восстановления текущего состояния активити.
2. Не верю
Верьте
Вообще-то поддерживать разные ориентации экранов в приложении является даже больше, чем правило хорошего тона.(хм почему то blockquote не пашет наверно из-за рейтинга)
Эм я не говорил про отключение смены ориентации девайса. А про отключение пересоздании активити при смене ориентации. Все вертится и ресайзится без проблем. Пробелмы повялются, если у вас под разные ориентации разные ресурсы юзаются, или разная структура лейаута, тогда их надо самим менять.
Эм я не говорил про отключение смены ориентации девайса. А про отключение пересоздании активити при смене ориентации. Все вертится и ресайзится без проблем. Пробелмы повялются, если у вас под разные ориентации разные ресурсы юзаются, или разная структура лейаута, тогда их надо самим менять.
>Вообще-то поддерживать разные ориентации экранов в приложении является
>даже больше, чем правило хорошего тона.
Видимо 99% игроделов эти правила не читали.
>даже больше, чем правило хорошего тона.
Видимо 99% игроделов эти правила не читали.
> А как потом обновлять UI из фрагмента?
Просто слушаете оповещение и уже выполняете необходимые действия с UI.
Например как то так:
1. Делаем запрос на выполнение чего либо в фоне.
2. В самом же фрагменте в отдельном треде уже проверяем актуальность данных, и либо возвращаем уже существующие, либо делаем новую фоновую задачу и по завершению оповещаем слушателей.
Что будет во время поворота:
1. активити будет безболезненно пересоздана.
2. а вот все что во фрагменте было сохранится, по сути это тоже самое что вы делали игнорируя изменения для активити, только в этом случае это ограничивается приделами фрагмента.
Этот же способ можно использовать и для хранения временных данных при поворотах.
Например, у нас есть ответ от сервера, скажем какой то список, в базу мы пока не хотим кешировать и нам нужно что бы разметка при поворотах нормально подхватывалась в зависимости от конфигурации, и статику тоже не хотим использовать. Тогда делаем отдельный ретеин фрагмент и храним все в нем.
Просто слушаете оповещение и уже выполняете необходимые действия с UI.
Например как то так:
1. Делаем запрос на выполнение чего либо в фоне.
2. В самом же фрагменте в отдельном треде уже проверяем актуальность данных, и либо возвращаем уже существующие, либо делаем новую фоновую задачу и по завершению оповещаем слушателей.
Что будет во время поворота:
1. активити будет безболезненно пересоздана.
2. а вот все что во фрагменте было сохранится, по сути это тоже самое что вы делали игнорируя изменения для активити, только в этом случае это ограничивается приделами фрагмента.
Этот же способ можно использовать и для хранения временных данных при поворотах.
Например, у нас есть ответ от сервера, скажем какой то список, в базу мы пока не хотим кешировать и нам нужно что бы разметка при поворотах нормально подхватывалась в зависимости от конфигурации, и статику тоже не хотим использовать. Тогда делаем отдельный ретеин фрагмент и храним все в нем.
у асинк тасков проблема не только с поворотами. вам могут банально позвонить когда ваше приложение работает.
а еще пользователь может сам свернуть ваше приложение и уйти по своим делам, такое тоже часто случается.
тысячи случаев, на самом деле, андроид так непредсказуем
а еще пользователь может сам свернуть ваше приложение и уйти по своим делам, такое тоже часто случается.
тысячи случаев, на самом деле, андроид так непредсказуем
Sign up to leave a comment.
Общение между потоками через ResultReceiver