- The Android Lifecycle cheat sheet — part I: Single Activities
- Part I: Activities
- Single Activity — Scenario 1: App is finished and restarted
- Single Activity — Scenario 2: User navigates away
- Single Activity — Scenario 3: Configuration changes
- Single Activity — Scenario 4: App is paused by the system
- Android navigation component. Простые вещи, которые приходится делать самому
- Переключение между пунктами меню
- Открытие новой Activity
- Передача параметров в startDestination
- A Single-Activity Android Application. Why not?!
- Obvious Things about Activity
- What you’re signing up for or taking control of it
- Will I get those precious 60 fps?
The Android Lifecycle cheat sheet — part I: Single Activities
Android is designed to empower users and let them use apps in a intuitive way. For example, users of an app might rotate the screen, respond to a notification, or switch to another task, and they should be able to continue using the app seamlessly after such an event.
To provide this user experience, you should know how to manage component lifecycles. A component can be an Activity, a Fragment, a Service, the Application itself and even the underlying process. The component has a lifecycle, during which it transitions through states. Whenever a transition happens, the system notifies you via a lifecycle callback method.
To help us explain how lifecycles work, we’ve defined a series of scenarios which are grouped according to the components that are present:
Part I: Activities — single activity lifecycle (this post)
The diagrams are also available as a cheat sheet in PDF format for quick reference.
Note: these diagrams apply to Android P / Jetpack 1.0 behavior.
The following scenarios showcase the default behavior of the components, unless otherwise noted.
If you find errors or you think something important is missing, report it in the comments.
Part I: Activities
Single Activity — Scenario 1: App is finished and restarted
- The user presses the Back button, or
- The Activity.finish() method is called
The simplest scenario shows what happens when a single-activity application is started, finished and restarted by the user:
- onSaveInstanceState is not called (since the activity is finished, you don’t need to save state)
- onCreate doesn’t have a Bundle when the app is reopened, because the activity was finished and the state doesn’t need to be restored.
Single Activity — Scenario 2: User navigates away
- The user presses the Home button
- The user switches to another app (via Overview menu, from a notification, accepting a call, etc.)
In this scenario the system will stop the activity, but won’t immediately finish it.
When your activity enters the Stopped state, the system uses onSaveInstanceState to save the app state in case the system kills the app’s process later on (see below) .
Assuming the process isn’t killed, the activity instance is kept resident in memory, retaining all state. When the activity comes back to the foreground, the activity recalls this information. You don’t need to re-initialize components that were created earlier.
Single Activity — Scenario 3: Configuration changes
- Configuration changes, like a rotation
- User resizes the window in multi-window mode
Configuration changes like rotation or a window resize should let users continue exactly where they left off.
- The activity is completely destroyed, but the state is saved and restored for the new instance.
- The Bundle in onCreate and onRestoreInstanceState is the same.
Single Activity — Scenario 4: App is paused by the system
- Enabling Multi-window mode (API 24+) and losing the focus
- Another app partially covers the running app (a purchase dialog, a runtime permission dialog, a third-party login dialog…)
- An intent chooser appears, such as a share dialog
This scenario doesn’t apply to:
- Dialogs in the same app. Showing an AlertDialog or a DialogFragment won’t pause the underlying activity.
- Notifications. User receiving a new notification or pulling down the notification bar won’t pause the underlying activity.
Источник
Android navigation component. Простые вещи, которые приходится делать самому
Всем привет! Хочу рассказать об особенностях в работе Navigation Architecture Component, из-за которых у меня сложилось неоднозначное впечатление о библиотеке.
Эта статья не пошаговая инструкция, в ней опущены детали реализации, чтобы сосредоточить внимание на ключевых моментах. В интернете есть немало одинаковых примеров использования (есть и переводы) — они помогут познакомиться с библиотекой. Так же перед чтением предлагаю изучить документацию.
Сразу скажу, библиотеку безусловно считаю полезной и не исключаю возможности неверного использования, но, пожалуй, я перепробовал всё прежде чем писать эту статью.
Итак, вот сценарии, при реализации которых ожидания по функционалу не совпали с реальностью в реализации:
- переключение между пунктами меню в navigation drawer
- открытие новой Activity со своим графом навигации
- передача параметров в startDestination
Переключение между пунктами меню
Это одна из тех функций, которые повлияли на решение использовать Navigation Component.
Нужно всего лишь сделать одинаковыми id пунктов меню
и id экранов (destination в графе навигации)
затем нужно связать меню с контроллером навигации:
Навигация в меню заработала — ну разве не чудо?!
Обратите внимание на «гамбургер» (иконка меню), при переключении между пунктами меню он меняет своё состояние на кнопку «назад». Такое поведение показалось непривычным (привычное — как в приложении play market) и, какое-то время, я пытался разобраться, что же сделал не так?
Всё так! Перечитав документацию по принципам навигации (а именно: пункты два и три), понял, что «гамбургер» показывается только для startDestination, вернее так: кнопка «назад» показывается для всех, кроме startDestination. Ситуацию можно поменять применив различные уловки в подписке (addOnNavigatedListener()) на изменение destination, но их даже описывать не стоит. Работает так, нужно смириться.
Открытие новой Activity
Activity может выступать в качестве navigation host и, в то же время, в графе навигации может выступать в роли одного из destination. Открытие Activity без вложенного графа навигации работает как ожидается, то есть вызов:
осуществит переход (как в случае с фрагментами) и откроет запрошенную Activity.
Гораздо интереснее рассмотреть случай, когда целевая Activity сама выступает в роли navigation host, то есть вариант 2 из документации:
В качестве примера давайте рассмотрим Activity для добавления заметки. В ней будет основной фрагмент с полями ввода EditFragment, он в графе навигации будет startDestination. Давайте положим, что при редактировании нам нужно прикрепить фото, для этого будем переходить к PhotoFragment для получения снимка с камеры. Граф навигации будет выглядеть так:
EditActivity мало отличается от MainActivity. Основное отличие в том, что на EditActivity нет меню:
Activity открывается, навигация внутри неё работает:
Опять обратим внимание на кнопку навигации в toolbar — на стартовом EditFragment нет кнопки «Назад к parent Activity» (а хотелось бы). С точки зрения документации, тут всё законно: новый граф навигации, новое значение startDestination, на startDestination не показывается кнопка «Назад», конец.
Для тех, кому хочется вернуть привычное поведение c parent activity, сохранив при этом функционал переключения между фрагментами, могу предложить такой костыль подход:
Подписка нужна для того, чтобы для NavigationUI.ActionBarOnNavigatedListener все destination не являлись startDestination. Таким образом NavigationUI.ActionBarOnNavigatedListener не будет скрывать кнопку навигации (за деталями стоит обратиться к исходникам). Добавим к этому обработку onSupportNavigateUp() штатным образом на startDestination и получим то, что хотелось.
Стоит сказать, что решение это далеко от идеала хотя бы потому, что это неочевидное вмешательство в поведение библиотеки. Полагаю, могут возникнуть проблемы в случае использования deep links (ещё не проверял).
Передача параметров в startDestination
В Navigation Component есть механизм передачи параметров от одного destination другому. Есть даже инструмент для обеспечения безопасности типов за счёт кодогенерации (неплохо).
Сейчас мы разберём случай, из-за которого я не смог поставить твёрдую пятёрку этому функционалу.
Вернёмся к EditActivity, достаточно привычный сценарий, когда одна Activity используется для создания и редактирования объектов. При открытии объекта для редактирования в Activity нужно передать, например, id объекта — давайте сделаем это штатным образом:
Я добавил параметр непосредственно в корневой элемент графа (navigation), но можно добавить в целевой фрагмент. От этого изменится только способ получения параметра.
Я добавил add и edit action’s в одни из фрагментов, так они будут доступны только из него.
В этом примере ImportFragmentDirections — автоматически сгенерированый safe-args класс.
Вы, наверняка, обратили внимание на особенности получения параметров в EditFragment. Так работает, потому что edit action (из пункта 1) передаёт аргументы в EditActivity, а она, в свою очередь, почему-то жадничает не передаёт её в граф (например, вызовом navController.graph.setDefaultArguments()). Эту особенность можно обойти, если вручную подготовить navigation controller. Один из способов описан на StackOwerflow.
Пожалуй, наибольшая сложность возникнет при одновременном использовании в качестве startDestination и обычного destination. То есть, при переходе и передаче параметров в startDestination из любого другого destination этого графа, фрагменту придётся самостоятельно определять, откуда извлекать параметры: из arguments или из intent.extras. Это нужно иметь ввиду при проектировании переходов с передачей параметров.
Резюмируя, хочу отметить, что сам не перестал использовать библиотеку и, несмотря на перечисленные недостатки особенности, считаю её достаточно полезной, чтобы рекомендовать к использованию. Очень надеюсь, что в следующих релизах изменится ситуация по крайней мере с передачей параметров в startDestination.
Источник
A Single-Activity Android Application. Why not?!
The reason behind writing this article was one of the I/O 2016 sessions where a speaker told about Google’s plans to neutralize the UX differences between Native Android App and Web. In its turn, availability of stable working libraries and my solid development experience finally served as a call to action. Though in all fairness, experience is not always the best motivator for making changes to the development process which has been ‘debugged to shine’.
Obvious Things about Activity
A basic Android application project can consist of one or more Activities. Activity may or may not contain Fragments. Fragment can include some UI that can be reused in an unspecified Activity or Fragment. It seems to be all logical and taken from the Bible. The question is: When should I create a new Activity? Let’s see what options we might have:
- An independent screen such as SettingsActivity which exists as one-off instance in the application.
- A screen that I can create as multiple instances, such as ProfileActivity.
- A screen that can be accessed using a Deep Link.
- A screen with unique settings of Status Bar, Navigation Bar and other window parameters.
Seems to be all, doesn’t it?
Based on our experience and the options above we can point out the following benefits (will reduce the development time and the number of bugs):
- Can set style for out-of-the-box screens using xml of Status Bar and Navigation Bar to input parameters.
- Built-in transition between screens.
- Can reuse or replace a previously launched Activity.
If there are advantages, then there should be some disadvantages as well. Let’s find out what they are.
- One of the main drawbacks is asynchronous launch. No matter how ‘heavy’ your Activity is, Android will decide itself on what the delay should be before the launch.
- The Shared Element Transition does not work between Fragment and Activity. That’s a bit of a headache when building complex UI/UX.
- If screen layout is changed, the system re-creates screens for all previous Activities along with all the fragments relating to the backward navigation. By the way the same is true when restoring an app after it’s killed by the system.
Summarizing the above, the disadvantages can be brought down to a common denominator — the loss of control over the application by developer.
What you’re signing up for or taking control of it
Fragments do solve these problems, but also bring all the advantages that we’ve used to live with to nought! One has to pay in full for a complete control over the application whereas the chances of making a mistake are doubled. In addition to all the advantages of a Multiple-Activity which turn into disadvantages when you use a Single-Activity, you will have to solve the following tasks:
- Writing or selecting a library to navigate between screens.
- Breaking down the application into DI (Dependency Injection) modules and creating relations between them both for data transfer and semantically.
- Controlling UI of the main Activity depending on the state which the application is in.
If you have enough skills and proficiency, the estimate for developing an app will be one and half times higher in terms of the number of screens and their relationships between each other. That is, if you have 2 screens in the application and it takes 8 hours to implement them (layout, animations, saving state), then in a Single-Activity App the solution will require 12 hours as such.
Will I get those precious 60 fps?
Animation smoothness between the screens will depend only on a smart arrangement of the views, their number as well as the data binding (text, pictures, audio, video). Messing up is only possible in the background. For example, one of the apps or services would take hold of the CPU or an I/O channel.
Activity or Fragment launching is practically the same under ideal circumstances. But you may ask why? Activity is actually “heavy” and has many properties and components. That’s all fine, but let’s get down the facts.
There are 3 measurable values that are absolutely important for us as they reflect the resource ‘efficiency’ of using an Activity or a Fragment.
To get the above values, let’s use the Android Studio 3.2.1 Profiler and Battery Historian.
Analyze a screen with a typical layout that consists of: TextView, ImageView, Button, ProgressBar, and RecyclerView — and a default fadeIn/Out animation to render the transition between screens. It is important that it is drawn within ≤16 ms to maintain the experimental integrity.
Let’s analyze not only how an Activity or a Fragment becomes visible, but also their closing to simulate the user’s actions. Therefore, we will go through the following scenario.
Running the scenarios with a Single and Multiple Activity several times we’ve obtained the following results from Battery Historian.
Источник