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

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

А просто обрабатывать onConfigurationChanged() не вариант?

Ну и еще обычно делают так.


В конструкторе AsynkTask:


this.activityRef = new WeakReference<SomeActivity>(activity);

В начале метода onPostExecute:


SomeActivity activity = activityRef.get();
if (activity==null) return;
Все очень плохо.
Ну вынесите вы долгую операцию в сервис

зачем городить такое?
    //waiting for our form
                while (!(ctx.getCurrentActivity() instanceof AsmykPleaseWaitActivity)) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        return;
                    }
                }

Спасибо за критику, данная конструкция добавлена для того, что бы не получилось так, что фоновая задача выполнилась быстрее чем запустилась Activity, например если произошел какой либо непредвиденный сбой в начале операции.
Опишите, пожалуйста, ваше предложение подробнее

Строго говоря, если разработчику нужны длительные операции в активити, фрагменте или вью — он нарушает SRP. Всё это нужно вынести в сервис, а в отображении только проверять состояние, нужна эта операция, работает или уже завершена.

Ок, давайте на практическом примере.
Моя задача, реализация процесса регистрации:
1) Получаю от пользователя первую порцию данных
2) Выполняю шаг регистрации 1. Последовательно отправляется N различных запросов. Тут я хочу показывать пользователю, что именно сейчас программа делает
3) Получаю от пользователя вторую порцию данных
4) Выполняю шаг регистрации 2. Последовательно отправляется M различных запросов. Тут я хочу показывать пользователю, что именно сейчас программа делает

Как я должен это сделать при помощи сервисов?

Запросто.
1) Создаёте активность, которая проверяет состояние модели и отображает фрагмент соответствующий этапу регистрации.
2) Фрагмент спрашивает сервис работает ли его задача, и если работает отображает соответствующее уведомление, если нет — ожидает действий пользователя.
Всё. Состояния легко добавляются и однозначны. Кроме того, возможны любые перерывы в работе с приложением.
Да, я знаю что многие любят хранить состояние отображения в самом отображении. Это создаёт много проблем при поддержке.


В целом, создание ниток внутри пределов объекта и принадлежащих только создавшему объекту — жизнеспособная идея… в рамках агентного подхода. Но отображение на роль агента подходит плохо.


Кроме того, существуют лоадеры. Они продолжают работать пока фрагмент не был убит.

А почему именно в onResume()? Этот метод вызывается довольно часто. Почему не регистрируете в onStart()?
Для того, что бы фреймворк знал, какая именно Activity сейчас работает.
У фреймворка есть возможность запускать задачи UI при выполнении предусловий см https://mkabramyan.github.io/Asmyk/net/mabramyan/asmyk/core/AsmykUITask.html
Например вы можете выполнить какое либо UI задание из фоновой задачи/сервиса под конкретную Activity или при каких либо специфичных условиях (одно ограничение, проверка должна быть относительно быстрой).
Предусловия описываются в методе isApplicatble()
Запустить задачу вы можете вызвав метод runUITask экземпляра класса AsmykApplicationContext.
Если вы укажете параметр postpone=true, тогда задача будет выполнена если isApplicatble вернет true.
Если isApplicable вернет false задача будет отложена. Как только сменится текущая Activity проверка isApplicatble будет вызвана снова и так до тех пор пока isApplicatble не вернет true. Это позволяет однозначно выполнить специфичную UI задачу из фоновой операции.
Спасибо за вопрос, надеюсь у меня получилось описать понятно

После того как узнал о Moxy (https://github.com/Arello-Mobile/Moxy) вообще перестал беспокоиться о повороте экрана. В ней презентер спокойно переживает смену конфигурации и плюсом после пересоздания view применяет к нему все вызванные до поворота методы, что позволяет почти не париться о сохранении состояния.

А просто лоадер взять не? Примеры лоадеров в документации конечно страшные, но это просто обычный POJO с жизненным циклом activityrecord. Ну или фрагмента.
Денис Неклюдов говорил, что у лоадеров есть баг, когда он не завершится никогда.
Основные лоадеры ничего не делают сами по себе, это просто объекты которые хранятся с временем жизни активитирекорда, что напрограммируете, то и получится. Я так понимаю, речь про asynctaskloader, можно тогда ссылочку на баг?
public class AsmykApplicationContext extends Application {
    
    ...
    private Activity currentActivity;
    
    ...
    /**
     * Internal method
     *
     * @param currentActivity
     */
    protected synchronized void setCurrentActivity(AsmykCompatActivity currentActivity) {
        this.currentActivity = currentActivity;
        executeUITasks();
    }

    /**
     * Get current activity
     *
     * @return
     */
    public synchronized Activity getCurrentActivity() {
        return currentActivity;
    }
}


Не делайте так
Почему?
Это одна из самых крутых утечек памяти из возможных на андроиде.
+ «current Activity» вообще-то может быть больше одной штуки. + instantrun наверное на этом сильно поперхнется и не обновит ссылку.
+ «current Activity» вообще-то может быть больше одной штуки

Я вызываю setCurrentActivity в событии onResume. Каким образом их будет несколько?

Это одна из самых крутых утечек памяти из возможных на андроиде.

Я вызываю setCurrentActivity(null) на каждом onPause
Каким образом утечет память? См https://developer.android.com/guide/components/activities/activity-lifecycle.html

instantrun наверное на этом сильно поперхнется и не обновит ссылку

Почему?

Я вызываю setCurrentActivity в событии onResume. Каким образом их будет несколько?

Навскидку — multiwindow.

Я вызываю setCurrentActivity(null) на каждом onPause

Ну, к примеру, закрывается ActivityA и открывается ActivityB. Есть вероятность, что ActivityA.onPause() вызовется после ActivityB.onResume(). Тут получится что currentActivity null. Да, утечки памяти тут не будет, но все же нехорошо хранить ссылки на активити в контексте процесса.

Почему?

Ну тут наверное андроид студия показывает предупреждение
«Do not place Android context classes in static fields; this is a memory leak and also breaks Instant Run.» не так ли?
1) При компилляции проекта, студия не ругается.
2) Где вы нашли статическое поле?
Навскидку — multiwindow.

См жизненный цикл multiwindow https://developer.android.com/guide/topics/ui/multi-window.html?hl=ru
Фреймворк не будет лагать в таком режиме. Просто UI задачи предназанченные для специфичной Activity будут выполняться только тогда, когда Activity будет реально актинвной. ИМХО случай довольно редкий, к тому же не вызовет ошибок. Помечу, эту особенность в Javadoc-е

но все же нехорошо хранить ссылки на активити в контексте процесса.

Почему?

Хорошо, мне ответить нечего больше :-) Основная моя идея что для контроля жизненного цикла активити и фрагментов лучше использовать предназначенные для этого стандартные компоненты, такие как лоадеры или setretaininstance когда надо. Если процесс не принадлежит одной активити то только тогда следует использовать что-то внешнее, например сервисы. Использовать максимально возможный scope — application/process можно, но только если понимаешь что делаешь. Из возможных проблем — wakelocks, doze mode, или неправильное понимание жизненного цикла фрагментов.

Проще всего при показе ProgressDialog (не важно как реализуется) закреплять текущее положение активити, чтобы юзер не мог его изменить. После закрытия ProgressDialog открепляем положение и все довольны.
<activity
            android:configChanges="keyboardHidden|orientation|screenSize"
            ...
/>

так не пробовал никто?
Поворот экрана — это не единственная возможная смена конфигурации.
А заголовок как звучит? =)
Async My Kit
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории