Pull to refresh
8
0
Максим Ефимов @MaximEfimov

User

Send message
Вообще, такого, конечно, не должно происходить. Спасибо, что подсветили проблему, будем разбираться.
Это проблема с любыми checked исключениями в реализациях любых интерфейсов – нельзя менять сигнатуру метода, но иначе выбросить такое исключение тоже нельзя. Опять же, поскольку checked – это не наследники какого-нибудь AbstractCheckedException, а лишь подмножество наследников Throwable, то мы даже не можем обобщить их и добавить в сигнатуру абстрактного метода. Можно, конечно, написать «throws Throwable» у базового метода, но это совсем уж ужасный дизайн.
Поэтому единственный нормальный способ выбросить checked исключение из переопределенного метода – завернуть его в любое unchecked исключение.
Да, задача повышения выживаемости процесса нами не ставилась, это немного другой аспект.
Было бы очень круто добавить функционал по управлению уровнем логирования отдельно для каждого тэга (или еще какой-то сущности). Обычно в очень большом проекте логов очень много, но часто хочется включить только какие-то определенные, например – посмотреть на одновременную работу уровня сети и уровня БД. Да, можно, конечно, фильтровать по набору тагов, но на мой взгляд в Logcat этот функционал реализован неудобно.
Возможно речь идет о некоторых ограничениях метода. Например, если вы положите в Intent список из миллиона строк, то вероятнее всего, принимающая сторона «не заведется». Если это будет Activity, то вы не увидите даже запуска метода onCreate(), как и хоть какого-нибудь сообщения в лог – такую ошибку становится непросто поймать.
Или, если вы напишете свой Parcelable, который будет не просто раскладываться в POJO, а лезть, например, в кеш, то десериализация такого объекта в неродном процессе также, скорее всего, ни к чему хорошему не приведет.
Я бы еще добавил, что нужно смотреть на правильную организацию потоков и передачу данных между ними. Для этого можно взять широкоизвестный Robospice, или недавно представленный нами Chronos. Очень часто ведь люди пишут все на AsyncTask'ах, когда 2015 год на дворе, а потом приложение неадекватно себя ведет, стоит только несколько раз повернуть экран.
Извлечь можно, но я не очень понимаю, как это поможет в данном случае. Нужно определить две точки, между которыми мы полагаем валидным оперирование компонентом UI, например – onResume() и onPause(). Для View выше предложили onAttachedToWindow() и onDetachedFromWindow(), возможно, это сработает.
И еще про isFinishing() – этот метод не позволяет отловить поворот экрана, так что остается потенциальная уязвимость краша при завершении операции во врем пересоздания UI.
Боюсь, что если прикручивать AsyncTask'и, то кода будет поболее. То, что я привел в примере достаточно один раз в базовом классе написать.

Завязываться на isFinishing() получится только у Acitivity, ближайший родственник этого метода во Fragment – isRemoving(), но они не идентичны. Получается, что экономим две строчки кода, а придется придумывать новый способ привязки, который не факт, что будет проще и короче.

Насчет тагов подумаем. Лично я очень не люблю instanceof и явные касты, поскольку это рушит ООП-дизайн. В качестве какого-то параметра можно всегда использовать любой объект, передаваемый в Operation при создании, и заключаемый в шаблонный Output при возвращении результата.
Если я вас правильно понял, то предлагается сделать изменяемое имя метода-обработчика. В принципе, такую опцию можно добавить, но я не уверен, что этот функционал будет часто востребован, поэтому не могу обещать, что сделаем это в ближайшее время. Впрочем вы сами можете дописать эту фичу, с точки зрения архитектуры это будет несложно.

Наследовать классы вовсе не обязательно, вот здесь пример, как «дружить» Chronos с обычной Activity: github.com/RedMadRobot/Chronos/wiki/How-to-use-Chronos#step-one--setting-up-gui. И у нас тоже в один и тот же метод-обработчик приходит как успешный результат, так и ошибка: github.com/RedMadRobot/Chronos/wiki/How-to-use-Chronos#step-four--handling-the-result.
Про освобождение памяти: GC соберет результаты операций, чтобы освободить память, и, когда Activity восстановится, они не будут доставлены. Повторно операция сама по себе не запустится, поскольку это, в общем случае, противоречит бизнес-логике – например, если это была операция включающая POST запрос на перевод денег в банковском приложении. Однако, вот здесь можете посмотреть, как написать код автоматически перезапускающихся операций: github.com/RedMadRobot/Chronos/wiki/Single-launch-mode, это очень просто.

Про сложное обновление данных: в этом случае вам лучше будет в одной операции агрегировать несколько. Таким образом получится, что до UI слоя дойдет только информация о полной загрузке, либо ошибке обновления данных. Также в этом случае логично описывать что делать при неполной загрузке (откат транзакции БД, инвалидация кеша, нотификация об ошибке и тому подобное) по ходу выполнения агрегированных операций. Мы используем такой подход в ряде приложений, которые должны работать в offline, и при этом синхронизироваться с сервером по возможности.
Если нужно самому привязываться через setRetainInstanceState(), то это поддержка жизненного цикла на уровне AsyncTask'ов, их так же можно «завернуть» во фрагмент. Кстати, именно так и работают Loader'ы, только у них свой менеджер, а не фрагментный, но суть там абсолютно та же.
Из готовых оберток над RxJava мне, к сожалению, не попадались достаточно качественные – в основном везде нужно или руками как-то сохранять подписки (особенно если их много), или при каждом запуске явно лезть в кеш и смотреть, пришли ли данные, или грузятся, или загрузка даже не запускалась.
RxJava, безусловно, очень крутая библиотека, но, к сожалению, в ней нет легкой привязки к жизненному циклу. Если верить самим авторам библиотеки, то привязку сделать, действительно, можно, но это требует написания своего способа хранить Subscription при пересоздании Activity: github.com/ReactiveX/RxJava/wiki/The-RxJava-Android-Module#fragment-and-activity-life-cycle. В Chronos фокус направлен именно на легкую и бесшовную интеграцию фоновых задач с жизненным циклом UI-классов.
Про View: в качестве «UI-клиента» можно использовать любой класс, в том числе и View. Однако, основная особенность библиотеки – привязка к жизненному циклу не сработает, поскольку у View нет как такового однозначного определения жизненного цикла. Вот здесь можете почитать рассуждения на этот счет: plus.google.com/u/0/+ArpitMathur/posts/cT1EuBbxEgN. Если придумаете, как правильно подключать View, то не стесняйтесь делать Pull Request. С другой стороны, выносить логику получения данных во View не рекомендуется, но это уже вопрос архитектуры.

Про смену ориентации и уход в бэкграунд: если не был убит сам процесс, то после пересоздания Activity, Chronos вернет ей все результаты операций, которые скопились за время, пока она была неактивна. Также есть очень простой способ подхватывать запущенные таски, чтобы, например, не начинать грузить данные два раза при повороте экрана: github.com/RedMadRobot/Chronos/wiki/Single-launch-mode.
Про контекст: в самих операциях ссылка на Context не содержится, ей владеет посредник – объект класса ChronosConnector, но он ее теряет после вызова onPause(). Если вам нужен контекст в операции, то можно брать, например, контекст приложения.

Про режим выполнения: под капотом на данный момент находится ExecutorService в лице CachedThreadPool, в будущем, возможно, вынесем реализацию в конфиг. Возможности задать приоритет нет, но это тоже в планах на расширение. IPC нет.

Information

Rating
Does not participate
Location
Россия
Registered
Activity