Tasks — обзор лучшего списка задач по версии Google Play
Александр Щербаков
Посмотрим на очередное приложение для ведения списков и планирования задач, которое Google выбрало лучшим в 2018 году.
Google подвела итоги 2018 года для своего магазина приложений и составила топ программ и игр. По списку уже прошелся Владимир Нимин — почитайте его материал на главной Mobile-Review. В этом топе я обратил внимание на список «Лучшие приложения на каждый день 2018», там появилось приложение Tasks или на русском «Задачи: Список задач» с очень высокой средней оценкой в 4,8 балла.
Я люблю подобные приложения, туда я записываю не только задачи по основной работе, но и все, что приходит в голову — идеи, списки покупок, бытовые дела, напоминания, фильмы, которые хочу посмотреть. Записывая все подряд я решаю сразу несколько проблем: не нужно держать все задачи в голове, все хранится в одном приложении и заодно избавляюсь от ощущения, что я что-то забыл. Для этого у меня стоит Microsoft To-Do — на сайте есть обзор этого приложения. У меня есть несколько претензий к нему, поэтому я периодически подыскиваю альтернативу.
Microsoft To-Do
Дизайн Tasks полностью выполнен по уже устаревшим принципам Material Design. После очень приятного интерфейса «Задач» от Google это выглядит странно.
На первый взгляд структура приложения кажется простой. Есть основной раздел со списками-категориями. Я его разделил на «день», «работа», «быт» «посмотреть» и «прочее». Каждой категории присваивается не только имя, но и цвет, в который окрашивается интерфейс. Внутри категории можно создавать вкладки, например, «быт» я разделил на две вкладки — «дела» и «покупки». В «дела» я добавляю все задачи, которые не относятся к работе — записи к врачу, напоминания и прочее, а во втором разделе обычный список покупок, чтобы не забыть ничего в магазине. Получается трехуровневая система — категории списков, вкладки и сами списки.
Внутри в каждой вкладки есть список — его можно пополнять, позиции менять местами, давать приоритет определенным пунктам, отмечать задачи выполненными. Есть несколько дополнительных функций — удалить все завершенные задачи, автоматически сортировать или создать копию списка.
Во всех вкладках в нижней части экрана есть поле для быстрого ввода задачи, его можно убрать в настройках. Для создания более сложных задач есть отдельная кнопка — там можно добавить название задачи, описание, срок выполнения, установить напоминание, выставить приоритет и отметить звездочкой.
В подобном приложении хотелось бы видеть вкладку типа «Мой день», в которую автоматически попадают задачи, запланированные на этот день. В таком случае можно открыть приложение утром и увидеть все задачи в одном списке, а в конце закрыть выполненное и перенести на завтра то, что не успел сделать. Для этого я создал отдельный список «день» и приходится вручную туда переносить задачи из других списков — это неудобно.
В Microsoft To-Do такой список есть, но задачи в него добавляются простым свайпом, что немного проще, но все еще неудобно. Еще есть встроенный ассистент, который сам советует добавить невыполненные в прошлом задачи.
Поломанные уведомления
На мой взгляд, напоминания в Tasks работают неправильно. Объясню на примере: я добавил в список запись к врачу на вторник в 8:45 и включил уведомление. Приложение напомнило мне о записи к врачу не заранее, а ровно в 8:45, то есть, когда я уже должен стоять у двери в больнице.
Проблема в том, что приложение не дает установить вручную время уведомления — оно строго привязано к сроку выполнения задачи. В том же Microsoft To-Do уведомление работает независимо от самой задачи. Я могу записать срок задачи на 8:45, а напоминание в 7:00 или вечером за день до приема.
Tasks
Microsoft To-Do
Есть вариант обойти это ограничение — в поле срока задачи ставится время для уведомления, а в описание можно записать нужное время. Еще можно создать отдельную задачу для напоминания с нужным временем уведомления. Оба варианта бредовые и никак не относятся к удобству пользователя. А ведь достаточно добавить возможность настраивать время уведомления вручную, это решит все проблемы.
Общее впечатление
Дизайн Tasks кажется спорным, но что можно сказать однозначно — работает приложение идеально и стабильно. Возможно, все открывается быстро, потому что в приложении кроме списков ничего и нет, но скорость меня порадовала. К тому же программа распространяется бесплатно и внутри вообще нет рекламы. Только в меню есть кнопка пожертвования, можно поблагодарить разработчика за 75 ₽.
Еще в приложении не хватает функции совместной работы, чтобы дать другому человеку доступ к списку. Есть кнопка «Поделиться», но она просто пересылает список текстом, другой человек не сможет пополнять или редактировать его. Также в Tasks нет нормальной синхронизации данных, можно сделать бэкап вручную и только потом вручную выгрузить в «облако». Но если с вашим телефоном что-то случится, а бэкап не делали давно, то все данные потеряются. Приложения Tasks на ПК тоже нет — большой минус.
У Tasks есть виджет, в котором можно смотреть какой-то конкретный список или включить отображение всех задач в порядке приоритета.
Заключение
Мне не понятно, почему у Tasks такой большой рейтинг. В приложении поломана система уведомлений, нельзя работать над списками с другими, нет синхронизации с облаком и нет версии для ПК. «Фишка» с присвоением цвета для каждой категории тоже кажется бесполезной, хватает навигации по заголовкам.
Хотя в целом приложение сделано толково — оно отлично подойдет для обычных списков покупок или желаемых фильмов, но с более сложными задачами не справится. К тому же есть вероятность потерять данные, если вовремя не сделать бэкап. Так что это точно не лучшее приложение для списков задач.
Источник
Tasks и Back Stack в Android
Итак. Каждое Android приложение, как минимум, состоит из фундаментальных объектов системы — Activity. Activity — это отдельный экран который имеет свою отдельную логику и UI. Количество Activity в приложении бывает разное, от одного до много. При переходах между различными Activity пользователь всегда может вернуться на предыдущую, закрытую Activity при нажатии кнопки back на устройстве. Подобная логика реализована с помощью стека (Activity Stack). Его организация «last in, first out» — т.е. последний вошел, первый вышел. При открытии новой Activity она становится вершиной, а предыдущая уходит в режим stop. Стек не может перемешиваться, он имеет возможность добавления на вершину новой Activity и удаление верхней текущей. Одна и та же Activity может находиться в стеке, сколько угодно раз.
Task — это набор Activity. Каждый таск содержит свой стек. В стандартной ситуации, каждое приложение имеет свой таск и свой стек. При сворачивании приложения, таск уходит в background, но не умирает. Он хранит весь свой стек и при очередном открытии приложения через менеджер или через launcher, существующий таск восстановится и продолжит свою работу.
Ниже покажу картинку, как работает стек.
Если продолжать нажимать кнопку back, то стек будет удалять Activity до того, пока не останется главная корневая. Если же на ней пользователь нажмет back, приложение закроется и таск умрет. Кстати, я говорил о том, что когда мы сворачиваем наше приложение и запускам например новое, то наш таск просто уходит в background и будет ждать момента, пока мы его вызовем. На самом деле есть одно «но». Если мы будем иметь много задач в background или же просто сильно нагружать свое устройство, не мала вероятность того, что таск умрет из за нехватки системных ресурсов. Это не война конечно, но то что мы потеряем все наши текущие данные и наш стек очистится — это точно. Кстати для избежания потери данных в таком случаи, вам стоит почитать про SavingActivityState.
Маленький итог
Управление тасками
Существует два пути для изменения стандартной организации тасков. Мы можем устанавливать специальные атрибуты в манифесте для каждой Activity. Также мы можем устанавливать специальные флаги для Intent, который запускает новую Activity с помощью startActivity(). Заметьте, что иногда атрибуты в манифесте и флаги в Intent могут противоречить друг другу. В этом случаи флаги Intent будут более приоритетны.
Атрибут launchMode
Для каждой Activity в манифесте можно указать атрибут launchMode. Он имеет несколько значений:
- standard — (по умолчанию) при запуске Activity создается новый экземпляр в стеке. Activity может размещаться в стеке несколько раз
- singleTop — Activity может распологаться в стеке несколько раз. Новая запись в стеке создается только в том случаи, если данная Activity не расположена в вершине стека. Если она на данный момент является вершиной, то у нее сработает onNewIntent() метод, но она не будет пересоздана
- singleTask — создает новый таск и устанавливает Activity корнeвой для него, но только в случаи, если экземпляра данной Activity нет ни в одном другом таске. Если Activity уже расположена в каком либо таске, то откроется именно тот экземпляр и вызовется метод onNewIntent(). Она в свое время становится главной, и все верхние экземпляры удаляются, если они есть. Только один экземпляр такой Activity может существовать
- singleInstance — тоже что и singleTask, но для данной Activity всегда будет создаваться отдельный таск и она будет в ней корневой. Данный флаг указывает, что Activity будет одним и единственным членом своего таска
На самом деле не важно, в каком таске открыта новая Activity. При нажатии кнопки back мы все равно вернемся на предыдущий таск и предыдущие Activity. Единственный момент, который нужно учитывать — это параметр singleTask. Если при открытии такой Activity мы достанем ее из другого background таска, то мы полностью переключаемся на него и на его стек. на картинке ниже это продемонстрировано.
Флаги
Как и говорил, мы можем устанавливать специальный флаги для Intent, который запускает новую Activity. Флаги более приоритетны, чем launchMode. Существует несколько флагов:
- FLAG_ACTIVITY_NEW_TASK — запускает Activity в новом таске. Если уже существует таск с экземпляром данной Activity, то этот таск становится активным, и срабатываем метод onNewIntent().
Флаг аналогичен параметру singleTop описанному выше - FLAG_ACTIVITY_SINGLE_TOP — если Activity запускает сама себя, т.е. она находится в вершине стека, то вместо создания нового экземпляра в стеке вызывается метод onNewIntent().
Флаг аналогичен параметру singleTop описанному выше - FLAG_ACTIVITY_CLEAR_TOP — если экземпляр данной Activity уже существует в стеке данного таска, то все Activity, находящиеся поверх нее разрушаются и этот экземпляр становится вершиной стека. Также вызовется onNewIntent()
Affinity
Стандартно все Activity нашего приложения работают в одном таске. По желанию мы можем изменять такое поведение и указывать, чтобы в одном приложении Activity работали в разных тасках, или Activity разных приложений работали в одном. Для этого мы можем в манифесте для каждой Activity указывать название таска параметром taskAffinity. Это строковое значение, которое не должно совпадать с названием package, т.к. стандартный таск приложения называется именно как наш пакет. В общем случаи данный параметр указывает, что Activity будет гарантированно открываться в своём отдельном таске. Данный параметр актуален, если мы указываем флаг FLAG_ACTIVITY_NEW_TASK или устанавливаем для Activity атрибут allowTaskReparenting=«true». Этот атрибут указывает, что Activity может перемещаться между тасками, который её запустил и таском, который указан в taskAffinity, если один из них становится активным.
Чистка стека
Если таск долгое время находится в background, то система сама чистит его стек, оставляя только корневую Activity. Данное поведение обусловлено тем, что пользователь может забыть, что он делал в приложении до этого и скорее всего зашел в него снова уже с другой целью. Данная логика также может быть изменена с помощью нескольких атрибутов в манифесте.
- alwaysRetainTaskState — если флаг установлен в true для корневой Activity, то стек не будет чиститься и полностью восстановится даже после длительного времени
- clearTaskOnLaunch — если установить флаг в true для корневой Activity, то стек будет чиститься моментально, как только пользователь покинет таск. Полная противоположность alwaysRetainTaskState
- finishOnTaskLaunch — работает аналогично clearTaskOnLaunch, но может устанавливаться на любую Activity и удалять из стека именно её
Это всё для данного топика. Статья не импровизированная, а по сути является вольным переводом официальной документации. Рекомендую собрать легкий пример и поэксперементировать с флагами и атрибутами. Некоторые моменты, лично для меня были, неожиданно интересными. любые ошибки и недоработки учту в лс. Спасибо.
Источник
Полный список
— немного теории по Task
— фиксируем Activity в состоянии Paused
В этом уроке узнаем, куда помещается Activity, пока его не видно. И откуда оно достается при нажатии кнопки назад. В хелпе об этом написано достаточно понятно. Я сделаю краткий перевод основной части этого хелпа и использую их схемы.
Мы уже знаем, что приложение может содержать несколько Activity. И что Activity умеет вызывать Activity из других приложений с помощью Intent и Intent Filter. Если вы хотите отправить письмо из вашего приложения, вы вызываете Activity почтовой программы и передаете ей данные. Письмо уходит и вы возвращаетесь в ваше приложение. Создается ощущение, что все это происходило в рамках одного приложения. Такая «бесшовность» достигается за счет того, что оба Activity (ваше и почтовое) были в одном Task.
Прежде, чем продолжу объяснять, хочу сразу привести аналогию, чтобы тему легче было понять. В скобках я буду давать понятия-аналоги из Android.
Механизм организации Activity в Android очень схож по реализации с навигацией в браузере. Вы находитесь в одной вкладке(Task) и открываете страницы (Activity) переходя по ссылкам (Intent). В любой момент можете вернуться на предыдущую страницу, нажав кнопку Назад. Но кнопка Вперед отсутствует, т.к. страница, на которой была нажата кнопка Назад, стирается из памяти. И надо снова нажимать ссылку, если хотим попасть на нее. Если вам надо открыть что-то новое, вы создаете новую вкладку и теперь уже в ней открываете страницы, переходите по ссылкам, возвращаетесь назад. В итоге у вас есть несколько вкладок. Большинство из них на заднем фоне, а одна (активная, с которой сейчас работаете) – на переднем.
В итоге список аналогий браузера и Android таков:
Теперь вам будет более понятен текст про Task.
Task – группа из нескольких Activity, с помощью которых пользователь выполняет определенную операцию. Обычно стартовая позиция для создания Task – это экран Домой (Home).
Находясь в Home вы вызываете какое-либо приложение из списка приложений или через ярлык. Создается Task. И Activity приложения (которое отмечено как MAIN в манифест-файле) помещается в этот Task как корневое. Task выходит на передний фон. Если же при вызове приложения, система обнаружила, что в фоне уже существует Task, соответствующий этому приложению, то она выведет его на передний план и создавать ничего не будет.
Когда Activity_A вызывает Activity_B, то Activity_B помещается на верх (в топ) Task и получает фокус. Activity_A остается в Task, но находится в состоянии Stopped (его не видно и оно не в фокусе). Далее, если пользователь жмет Back находясь в Activity_B, то Activity_B удаляется из Task и уничтожается. А Activity_A оказывается теперь на верху Task и получает фокус.
В каком порядке открывались (добавлялись в Task) Activity, в таком порядке они и содержатся в Task. Они никак специально не сортируются и не упорядочиваются внутри. Набор Activity в Task еще называют back stack. Я буду называть его просто — стэк.
Схема (с офиц.сайта) демонстрирует пример:
В верхней части то, что видит пользователь. В нижней – содержимое Task. Видно, как при вызове новых Activity они добавляются в верх стэка. А если нажата кнопка Назад, то верхнее Activity из стэка удаляется и отображается предыдущее Activity.
Допустим у нас есть Task с несколькими Activity. Он на переднем фоне, мы с ним работаем сейчас.
— если мы нажмем кнопку Home, то ничего не будет удалено, все Activity сохранятся в этом Task-е, а сам Task просто уйдет на задний фон и его всегда можно будет вызвать оттуда, снова вызвав приложение, Activity которого является корневым для Task-а. Либо можно удерживать кнопку Home и мы увидим как раз список Task-ов, которые расположены на заднем фоне.
— если же в активном Task-е несколько раз нажимать кнопку Назад, то в итоге в стэке не останется Activity, пустой Task будет удален и пользователь увидит экран Home.
Там еще как всегда куча нюансов и сложностей, но мы пока остановимся на этом и в дебри не полезем. Этих знаний вполне хватит, чтобы ответить на вопросы предыдущего урока: почему на шаге 2 MainActivity исчезло с экрана, но осталось висеть в памяти и не было уничтожено? Ведь на шаге 3 было уничтожено ActivityTwo после того, как оно пропало с экрана. А на шаге 4 было в итоге уничтожено и MainActivity. Почему шаг 2 стал исключением?
Теперь вы знаете, почему. Потому, что на шаге 2 MainActivity осталось в стэке, а ActivityTwo вставилось на верх стэка и получило фокус. Ну а на шаге 3 и 4 были удалены Activity из верха стэка, в Task не осталось Activity, и мы увидели экран Home.
Если бы мы на шаге 3 нажали не Back, а Home, то Task с обоими Activity ушел бы задний фон и ничего не было бы уничтожено.
Paused
Теперь давайте откроем проект с прошлого урока P0241_TwoActivityState. Мы хотели поймать состояние Paused для Activity. Это состояние означает, что Activity не в фокусе, но оно видно, пусть и частично. Мы можем этого добиться, если присвоим диалоговый стиль для ActivityTwo. Оно отобразится как всплывающее окно и под ним будет частично видно MainActivity – оно и будет в статусе Paused. Давайте реализуем.
Для этого открываем AndroidManifest.xml, вкладка Application, находим там ActivityTwo и справа в поле Theme пишем такой текст: @android:style/Theme.Dialog
Все сохраняем и запускаем приложение.
MainActivity: onCreate()
MainActivity: onStart()
MainActivity: onResume()
MainActivity: onPause()
ActivityTwo: onCreate()
ActivityTwo: onStart()
ActivityTwo: onResume()
Видим, что не был вызван метод onStop для MainActivity, а значит приложение не было переведено в состояние Stopped и находится в режиме Paused.
ActivityTwo: onPause()
MainActivity: onResume()
ActivityTwo: onStop()
ActivityTwo: onDestroy()
MainActivity восстановилось одним лишь вызовом onResume, а onStart не понадобился, т.к. оно было в состоянии Paused, а не Stopped.
Мы четко увидели разницу между этим примером и им же на прошлом уроке. И MainActivity у нас был в состоянии Paused.
Далее можно нажать Back, а можно Home — вы уже знаете, что произойдет в обоих случаях. По логам можно убедиться в этом.
Чтобы вернуть ActivityTwo нормальный режим отображения, зайдите снова в манифест и удалите строку из поля Theme.
Кстати, у вас уже вполне достаточно знаний, чтобы создать приложение с кучей Activity, прописать вызовы и поиграться, посмотреть логи. Тем самым закрепите темы LifeCycle и Task.
На следующем уроке:
— вызываем Activity используя неявный вызов и Intent Filter
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник