Что такое контекст и как его использовать?
Context – это базовый класс, который реализуется самой системой Android. Он предоставляет доступ к базовым функциям приложения. Такие основные компоненты как Activity, Application и другие наследуются от класса Context.
Используя контекст, можно:
- получить текущее состояние (state) приложения,
- получить доступ к информации, хранящейся в Activity или Application,
- получить доступ к ресурсам, базам данных или настройкам приложения.
Для получения доступа к контексту используются методы:
Для доступа к различным ресурсам и объектам сам контекст использует методы:
Можно выделить два основных типа контекста:
Context – это базовый класс, который реализуется самой системой Android. Он предоставляет доступ к базовым функциям приложения. Такие основные компоненты как Activity, Application и другие наследуются от класса Context.
Используя контекст, можно:
- получить текущее состояние (state) приложения,
- получить доступ к информации, хранящейся в Activity или Application,
- получить доступ к ресурсам, базам данных или настройкам приложения.
Для получения доступа к контексту используются методы:
getApplicationContext()
, getContext()
, getBaseContext()
или this
. Для доступа к различным ресурсам и объектам сам контекст использует методы:
getAssets()
, getResources()
, getPackageManager()
, getString()
, getSharedPrefsFile()
. Можно выделить два основных типа контекста:
ApplicationContext
и ActivityContext
. ApplicationContext
– экземпляр, реализующий паттерн singleton. Контекст, привязанный к жизненному циклу приложения. Следует использовать там, где нужен контекст, жизненный цикл которого отделен от текущего. ActivityContext
– контекст, который доступен в активити и привязан к его жизненному циклу. Используется при передаче контекста в рамках Activity или когда нужен контекст, жизненный цикл которого привязан к текущему контексту.Что содержит файл AndroidManifest.xml?
AndroidManifest содержит ключевую информацию о приложении - название, версию, иконки, какие разрешения приложение использует, регистрирует все используемые классы activity, сервисы и т.д.
Ключевые теги в порядке вложенности:
Например, intent-filter может содержать вложенные элементы
В теге
– обозначает, что данная активити будет являться точкой входа в приложение. А тег
– указывает на то, что данная активити будет отображаться первой при старте приложения.
Кроме атрибутов по умолчанию (при создании проекта) в манифесте также может указываться: версия приложения, версия SDK, разрешения на доступ к различным ресурсам, поддержка разрешений экрана, настройки ориентации и т.д.
AndroidManifest содержит ключевую информацию о приложении - название, версию, иконки, какие разрешения приложение использует, регистрирует все используемые классы activity, сервисы и т.д.
Ключевые теги в порядке вложенности:
<manifest>
– в корневом теге по умолчанию определяется пакет приложения. <application>
– тег определяет основные настройки приложения. Есть ряд обязательных атрибутов, к которым относятся android:icon, android:label, android:theme и другие. <activity>
– теги отображают все используемые в приложении активити. <intent-filter>
– расположен внутри тегов активити. Указывает как компонент будет использоваться. Описывает тип интентов (намерений), на которые Activity, Service, или Broadcast receiver могут реагировать. Например, intent-filter может содержать вложенные элементы
<action>
и <category>
. В теге
<action>
атрибут “android:name="android.intent.action.MAIN”
– обозначает, что данная активити будет являться точкой входа в приложение. А тег
<category>
с атрибутом “android:name="android.intent.category.LAUNCHER”
– указывает на то, что данная активити будет отображаться первой при старте приложения.
Кроме атрибутов по умолчанию (при создании проекта) в манифесте также может указываться: версия приложения, версия SDK, разрешения на доступ к различным ресурсам, поддержка разрешений экрана, настройки ориентации и т.д.
Что такое View?
View – это класс элемента графического интерфейса. По своей сути View – это общее название для всех элементов графического интерфейса.
View это суперкласс всех привычных нам элементов UI Android, таких как TextView, EditText, Button. Но не только эти классы наследуются от View. Все, что может быть отрисовано на экране Android-устройства должно наследоваться от View. По такой же логике можно создать своего наследника класса View и использовать в качестве элемента графического интерфейса.
Если представить все различные компоненты UI Android в качестве иерархии классов, то класс View будет корнем этой иерархии, от которого будут исходить “ветви”.
View – это класс элемента графического интерфейса. По своей сути View – это общее название для всех элементов графического интерфейса.
View это суперкласс всех привычных нам элементов UI Android, таких как TextView, EditText, Button. Но не только эти классы наследуются от View. Все, что может быть отрисовано на экране Android-устройства должно наследоваться от View. По такой же логике можно создать своего наследника класса View и использовать в качестве элемента графического интерфейса.
Если представить все различные компоненты UI Android в качестве иерархии классов, то класс View будет корнем этой иерархии, от которого будут исходить “ветви”.
Расскажите про Fragment
Фрагменты можно представить как легковесную активити. Они представляют собой часть UI, которую можно переиспользовать много раз. Как и у активити, у фрагмента есть свой lifecycle, layout и он может обрабатывать действия пользователя на своем layout. С помощью фрагментов можно внести модульность в свое приложение, используя различные сочетания фрагментов в различных активити.
Стоит учесть, что стандартный класс Fragment из android.app сейчас считается deprecated. Необходимо использовать класс Fragment из support-библиотеки, то есть androidx.fragment.app.
Чтобы создать свой фрагмент, необходимо сделать то же самое что и с Activity, то есть создать наследника класса Fragment. При этом необходимо, чтобы у данного наследника был пустой конструктор, так как именно такой вызывает система при пересоздании фрагментов (при configuration changes, например).
Фрагменты можно представить как легковесную активити. Они представляют собой часть UI, которую можно переиспользовать много раз. Как и у активити, у фрагмента есть свой lifecycle, layout и он может обрабатывать действия пользователя на своем layout. С помощью фрагментов можно внести модульность в свое приложение, используя различные сочетания фрагментов в различных активити.
Стоит учесть, что стандартный класс Fragment из android.app сейчас считается deprecated. Необходимо использовать класс Fragment из support-библиотеки, то есть androidx.fragment.app.
Чтобы создать свой фрагмент, необходимо сделать то же самое что и с Activity, то есть создать наследника класса Fragment. При этом необходимо, чтобы у данного наследника был пустой конструктор, так как именно такой вызывает система при пересоздании фрагментов (при configuration changes, например).
В чем разница между методами onCreate() и onStart()?
Оба метода являются частью жизненного цикла Activity.
Оба метода являются частью жизненного цикла Activity.
onCreate()
– вызывается, когда активити создается в первый раз или происходит изменение конфигурации. Активити еще не видна пользователю. В методе задаются первоначальные настройки, создаются объекты визуального интерфейса. Этот метод получает объект Bundle, который содержит прежнее состояние activity, если оно было сохранено. За ним всегда следует вызов метода onStart()
. onStart()
– вызывается, когда Activity видна пользователю (но он все еще не может с ней взаимодействовать). Метод может вызываться несколько раз, при определенных случаях, например, когда приложение находилось в фоне и возвращается на передний план через методы onStop()
и onRestart()
. Следующим методом за ним всегда вызывается onResume()
и активити переходит в состояние Resumed.Что следует делать при повороте экрана?
При смене ориентации приложения Activity уничтожается и создается заново. Удаляются, соответственно, и все объекты. Вызываются коллбэки в составе жизненного цикла
Чтобы сохранить созданные в активити объекты и данные, необходимо реализовать пару методов: первый позволяет сохранить данные –
Для сохранения данных переопределяется
При реинициализации активити, Bundle с сохраненным состоянием передается в
Система вызывает
Вызов метода
При смене ориентации приложения Activity уничтожается и создается заново. Удаляются, соответственно, и все объекты. Вызываются коллбэки в составе жизненного цикла
onPause()
, onStop()
, onSaveInstanceState()
, onDestroy()
– onCreate()
, onStart()
, onRestoreInstanceState()
, onResume()
. Чтобы сохранить созданные в активити объекты и данные, необходимо реализовать пару методов: первый позволяет сохранить данные –
onSaveInstanceState()
, а второй восстановить – onRestoreInstanceState()
. Для сохранения данных переопределяется
onSaveInstanceState()
и данные упаковываются в объект типа Bundle. При реинициализации активити, Bundle с сохраненным состоянием передается в
onCreate()
и в onRestoreInstanceState()
. Система вызывает
onSaveInstanceState()
и onRestoreInstanceState()
только в том случае, когда необходимо сохранить состояние, например при повороте экрана или в случае уничтожении Activity при нехватке памяти. Данные коллбэки не вызываются, если пользователь выходит из активити нажав Back или если активити убивается вызовом finish()
. Вызов метода
onSaveInstanceState()
зависит от версии API. До API 28 метод вызывается до onStop()
, а начиная API 28 после onStop()
. onRestoreInstanceState()
вызывается после onStart()
.Сохранение состояния при портретной ориентации в Android
Следует сохранять состояние, так как Configuration Change может произойти не только при изменении ориентации экрана.
Изменение ориентации экрана это только частный случай изменения конфигурации. Изменение языка системы например тоже считается изменением конфигурации. Да и сохранять состояние может быть нужно не только при Configuration Change.
Например пользователь может отвлечься на пришедшее сообщение, перейти в другое приложение чтобы ответить, а затем вернуться обратно, при этом система уже может убить процесс нашего приложения, но пользователь ожидает что все сохранилось. Для этого важно использовать средства сохранения состояния активити/фрагмента.
Следует сохранять состояние, так как Configuration Change может произойти не только при изменении ориентации экрана.
Изменение ориентации экрана это только частный случай изменения конфигурации. Изменение языка системы например тоже считается изменением конфигурации. Да и сохранять состояние может быть нужно не только при Configuration Change.
Например пользователь может отвлечься на пришедшее сообщение, перейти в другое приложение чтобы ответить, а затем вернуться обратно, при этом система уже может убить процесс нашего приложения, но пользователь ожидает что все сохранилось. Для этого важно использовать средства сохранения состояния активити/фрагмента.
Что вы знаете про BroadcastReceiver?
Android приложения могут получать различные сообщения от самой системы, а также от других приложений и от других компонентов данного приложения. Эти сообщения называются broadcast (широковещательные сообщения). Например, система рассылает данные сообщения когда запускается или когда устройство подключается к зарядке.
Для получения и обработки данных сообщений используются BroadcastReceiver. Броадкасты оборачиваются в объект Intent, в котором указывается тип данного броадкаста. У ресиверов же есть IntentFilter, в котором они указывают, какой тип броадкастов они хотят получать.
Когда броадкаст был отправлен, система сама рассылает его всем ресиверам, которые были подписаны на данный тип броадкастов. Не стоит злоупотреблять возможностью получать различные сообщения, указывайте только тот тип броадкастов, который необходим, иначе это может сказаться на производительности.
Если ресивер получает броадкаст, а приложение, в котором он объявлен не запущено, то система запускает данное приложение и вызывает метод
Android приложения могут получать различные сообщения от самой системы, а также от других приложений и от других компонентов данного приложения. Эти сообщения называются broadcast (широковещательные сообщения). Например, система рассылает данные сообщения когда запускается или когда устройство подключается к зарядке.
Для получения и обработки данных сообщений используются BroadcastReceiver. Броадкасты оборачиваются в объект Intent, в котором указывается тип данного броадкаста. У ресиверов же есть IntentFilter, в котором они указывают, какой тип броадкастов они хотят получать.
Когда броадкаст был отправлен, система сама рассылает его всем ресиверам, которые были подписаны на данный тип броадкастов. Не стоит злоупотреблять возможностью получать различные сообщения, указывайте только тот тип броадкастов, который необходим, иначе это может сказаться на производительности.
Если ресивер получает броадкаст, а приложение, в котором он объявлен не запущено, то система запускает данное приложение и вызывает метод
onReceive()
ресивера. Именно в этом методе и необходимо обработать данный броадкаст.Основные методы жизненного цикла сервиса
Самыми важными методами жизненного цикла сервиса являются:
1.
Данный метод вызывается после того, как другой компонент (например активити) вызвал метод
2.
Вызывается после того, как другой компонент вызвал метод
3.
Вызывается один раз при создании сервиса для его настройки. Он вызывается до того как буду вызваны
4.
Вызывается, когда сервис уничтожается. В данном методе необходимо избавиться от всего ненужного использованного в сервисе, как например потоки, которые в нем были запущены или же всяческие listener’ы и observer’ы
Самыми важными методами жизненного цикла сервиса являются:
1.
onStartCommand()
Данный метод вызывается после того, как другой компонент (например активити) вызвал метод
startService()
. Когда данный метод был вызван, сервис запускается и живет до тех пор, пока в нем не будет вызван метод stopSelf()
, либо же в другом компоненте не будет вызван метод stopService()
. Если же сервис будет bound, то данный метод можно не переопределять 2.
onBind()
Вызывается после того, как другой компонент вызвал метод
bindService()
. Если данный метод был вызван, значит данный сервис является bound. Этот метод должен возвращать интерфейс для взаимодействия с данным сервисом (так как bound сервисы должны предоставлять клиент-серверный интерфейс для взаимодействия с ними). Данный интерфейс должен представлять из себя объект класса-наследника IBinder. Этот метод должен быть реализован в любом случае, если же сервис не предполагает его использование через bindService()
, то необходимо просто вернуть null 3.
onCreate()
Вызывается один раз при создании сервиса для его настройки. Он вызывается до того как буду вызваны
onStartCommand()
и onBind()
. Если сервис уже запущен, то этот метод вызван не будет 4.
onDestroy()
Вызывается, когда сервис уничтожается. В данном методе необходимо избавиться от всего ненужного использованного в сервисе, как например потоки, которые в нем были запущены или же всяческие listener’ы и observer’ы
Создание кастомной View в Android
Чтобы создать кастомную View, необходимо проделать следующие шаги
1. Создать класс, который будет наследовать View или же наследника View.
2. Переопределить конструкторы
Всего есть 4 конструктора, каждый из них используется для своих целей. Чтобы начать пользоваться кастомной View, необходимо переопределить только один, но в таком случае вы ограничиваете вариант создания View только до одного. Вот конструкторы и их использование:
- constructor(
Используется, чтобы создать View через Котлин.
- constructor(
Используется, чтобы создать View через XML
- constructor(
Используется, чтобы создать View через XML c дефолтным атрибутом style
- constructor(
Используется, чтобы создать View через XML c дефолтным атрибутом style и/или дефолтным ресурсом style
3. Переопределить on… методы.
Тут уже зависит от того, насколько новую View вы хотите создать. Возможно, хватит только переопределения
Это только базовый набор, если необходимо создать полностью новую View возможно придется создать свои собственные event listener’ы, средства доступа к свойствам, модификаторы и так далее.
Чтобы создать кастомную View, необходимо проделать следующие шаги
1. Создать класс, который будет наследовать View или же наследника View.
2. Переопределить конструкторы
Всего есть 4 конструктора, каждый из них используется для своих целей. Чтобы начать пользоваться кастомной View, необходимо переопределить только один, но в таком случае вы ограничиваете вариант создания View только до одного. Вот конструкторы и их использование:
- constructor(
context: Context
)Используется, чтобы создать View через Котлин.
- constructor(
context: Context, attrs: AttributeSet
)Используется, чтобы создать View через XML
- constructor(
context: Context, attrs: AttributeSet, defStyleAttr: Int
)Используется, чтобы создать View через XML c дефолтным атрибутом style
- constructor(
context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int
)Используется, чтобы создать View через XML c дефолтным атрибутом style и/или дефолтным ресурсом style
3. Переопределить on… методы.
Тут уже зависит от того, насколько новую View вы хотите создать. Возможно, хватит только переопределения
onDraw()
, а возможно придется переопределять и остальные методы тоже. Например, если необходимо создать квадратный LinearLayout, хватит только переопределения onMeasure()
.Это только базовый набор, если необходимо создать полностью новую View возможно придется создать свои собственные event listener’ы, средства доступа к свойствам, модификаторы и так далее.
Способы выполнения кода в UI потоке. Можно ли обновлять View не из UI потока?
Обращаться к каким-нибудь элементам View не из UI потока нельзя, поэтому UI поток так и назван, что только он может обновлять вьюхи. Если попытаться обновить вьюхи не из UI потока, то вылетит
Есть несколько способов исполнить кусок кода в UI потоке.
1. Использовать метод activity.runOnUiThread(Runnable). Этот метод выполняет кусок кода, который записан в метод
2. Использовать метод view.post(Runnable), view.postDelayed(Runnable, Long). View.post(Runnable) работает точно так же как и runOnUiThread(Runnable), а вот view.postDelayed(Runnable, Long) позволяет отложить исполнение кода на заданное количество миллисекунд
3. Использовать handler. У класса Handler есть несколько методов, которые добавлять сообщения в очередь сообщений потока. Так что если создать handler для UI потока, используя либо
Обращаться к каким-нибудь элементам View не из UI потока нельзя, поэтому UI поток так и назван, что только он может обновлять вьюхи. Если попытаться обновить вьюхи не из UI потока, то вылетит
CalledFromWrongThreadException
.Есть несколько способов исполнить кусок кода в UI потоке.
1. Использовать метод activity.runOnUiThread(Runnable). Этот метод выполняет кусок кода, который записан в метод
run()
объекта Runnable. Если этот метод был вызван из UI потока, то UI поток просто сразу исполнит данный код, если же из какого-либо другого потока, то данный кусок кода будет добавлен в очередь.2. Использовать метод view.post(Runnable), view.postDelayed(Runnable, Long). View.post(Runnable) работает точно так же как и runOnUiThread(Runnable), а вот view.postDelayed(Runnable, Long) позволяет отложить исполнение кода на заданное количество миллисекунд
3. Использовать handler. У класса Handler есть несколько методов, которые добавлять сообщения в очередь сообщений потока. Так что если создать handler для UI потока, используя либо
context.getMainLooper()
или Looper.getMainLooper()
и затем добавлять сообщения в очередь, можно будет запускать код на главном потоке. У класса Handler для этого есть следующие методы:handler.post()
handler.postDelayed()
handler.postAtTime()
handler.postAtFrontOfQueue()
– но этот метод может привести к неожиданным последствиям, так что использовать его необходимо только в особенных случаях на свой страх и риск.Расскажите про реализацию метода View.onMeasure()
Сама же реализация выглядит примерно так:
1. На вход
В каждом из них закодировано два значения, сам размер и режим. Режим закодирован с помощью сдвига на 30 битов. Всего есть три режима:
MeasureSpec.UNSPECIFIED = 0 - родитель не наложил никаких ограничений по размеру на View
MeasureSpec.EXACTLY = 1 - родитель задал точные размеры View
MeasureSpec.AT_MOST = 2 - родитель задал максимальный размер View
2. Вычисление самих размеров
Учитывая поступившие аргументы и ограничения от родителя необходимо расчитать размеры View
3. Вызвать метод
onMeasure()
- метод, необходимый, чтобы определить размеры View и его “детей”. Данный метод должен быть переопределен таким образом, чтобы быстро и точно возвращать размеры View и его “детей”. Так же есть установки на View от его родителей, который накладывают дополнительные ограничения.Сама же реализация выглядит примерно так:
1. На вход
onMeasure()
поступает два аргумента: widthMesureSpec
и heightMesureSpec
.В каждом из них закодировано два значения, сам размер и режим. Режим закодирован с помощью сдвига на 30 битов. Всего есть три режима:
MeasureSpec.UNSPECIFIED = 0 - родитель не наложил никаких ограничений по размеру на View
MeasureSpec.EXACTLY = 1 - родитель задал точные размеры View
MeasureSpec.AT_MOST = 2 - родитель задал максимальный размер View
2. Вычисление самих размеров
Учитывая поступившие аргументы и ограничения от родителя необходимо расчитать размеры View
3. Вызвать метод
setMeasuredDimension()
/вызвать super.onMeasure()
, чтобы передать вычисленные значения дальше. Если этого не сделать то вылетит исключение IllegalStateException
Запустится ли приложение в нескольких процессах?
По умолчанию одно приложения запускается в одном процессе, но можно его запустить и в разных.
Пока запущено андроид устройство, пользователь может покинуть приложение (например, чтобы ответить на входящий звонок), на какое-то время и тогда система может убить процесс, в котором запущено приложение. Поэтому может быть необходимо запускать различные компоненты приложения в различных процессах.
В классе манифеста указываются различные эти самые компоненты приложения, такие как <activity>, <service>, <receiver> и <provider>. У каждого из этих компонент можно указать процесс, в котором они будут запущены с помощью атрибута android:process. Таким образом можно запускать приложение в нескольких процессах. Таким же образом можно и запускать несколько приложений в одном процессе, но в таком случае будет необходимо, чтобы у данных приложений был одинаковый Linux ID и они должны быть подписаны одинаковым сертификатом.
По умолчанию одно приложения запускается в одном процессе, но можно его запустить и в разных.
Пока запущено андроид устройство, пользователь может покинуть приложение (например, чтобы ответить на входящий звонок), на какое-то время и тогда система может убить процесс, в котором запущено приложение. Поэтому может быть необходимо запускать различные компоненты приложения в различных процессах.
В классе манифеста указываются различные эти самые компоненты приложения, такие как <activity>, <service>, <receiver> и <provider>. У каждого из этих компонент можно указать процесс, в котором они будут запущены с помощью атрибута android:process. Таким образом можно запускать приложение в нескольких процессах. Таким же образом можно и запускать несколько приложений в одном процессе, но в таком случае будет необходимо, чтобы у данных приложений был одинаковый Linux ID и они должны быть подписаны одинаковым сертификатом.
Как можно добавлять и переключать фрагменты? Как работать с бэкстэком?
Если не говорить про NavigationComponent из Jetpack, то для добавления, переключения и удаления фрагментов используются два класса, FragmentManager и FragmentTransaction, но на самом деле почти все происходит в FragmentTransaction, менеджер нужен только чтобы получить транзакцию.
Получить транзакцию можно методом менеджера
Метод
Метод
Метод
После вызова всех методов необходимо вызвать метод
Если не говорить про NavigationComponent из Jetpack, то для добавления, переключения и удаления фрагментов используются два класса, FragmentManager и FragmentTransaction, но на самом деле почти все происходит в FragmentTransaction, менеджер нужен только чтобы получить транзакцию.
Получить транзакцию можно методом менеджера
beginTransaction()
. Чтобы добавить фрагмент используется метод add()
, чтобы заменить replace()
, а чтобы удалить remove()
. Транзакция может принимать несколько методов.Метод
add()
принимает в качестве аргументов id View, в который будет помещен фрагмент (сейчас рекомендуется в качестве контейнеров для фрагментов использовать FragmentContainerView), а также сам фрагмент (либо класс фрагмента). Так же опционально можно передать args и tagМетод
replace()
принимает все то же самое что и add в качестве аргументов, за исключением класса. Заменять фрагменты можно только объектами.Метод
remove()
принимает только объект класса Fragment.После вызова всех методов необходимо вызвать метод
commit()
транзакции, таким образом все изменения вступят в силу. Так же можно вместо использования beginTransaction()
и commit()
использовать метод fragmentManager.commit { }
, который автоматически вызовет beginTransaction()
и commit()
, но для этого необходимо подключить библиотеку fragment-ktxВ чем разница между build type, flavor и build variant?
Build Type - тип сборки. Он используется чтобы задать настройки сборки. По умолчанию Android Studio создает 2 типа сборки, debug и release, но в файле build.gradle прописан только один. Тип сборки debug включает в себя полезные инструменты для отладки, а также он подписан своим ключом. Тип сборки release же включает в себя оптимизацию кода через ProGuard. Но вы можете задать свои настройки для каждого из типов сборки.
Product Flavor же немного другая вещь. Он описывает версии приложения с разной функциональностью. Так например можно задать разные applicationId для платной и бесплатной версий приложения, для того чтобы они могли одновременно существовать и Google Play или быть одновременно установлены на одном устройстве. Точно так же можно задать им разные имена. В пример можно привести Age of History и Age of History Lite - две версии одной и той же игры, одна платная, а другая бесплатная.
Build Variant - вариант сборки, совмещает эти две вещи. То есть если есть 2 build type: debug и release, и 2 product flavor: normal и lite, то будет 4 варианта сборки:
1. normalDebug
2. normalRelease
3. liteDebug
4. liteRelease
Build Type - тип сборки. Он используется чтобы задать настройки сборки. По умолчанию Android Studio создает 2 типа сборки, debug и release, но в файле build.gradle прописан только один. Тип сборки debug включает в себя полезные инструменты для отладки, а также он подписан своим ключом. Тип сборки release же включает в себя оптимизацию кода через ProGuard. Но вы можете задать свои настройки для каждого из типов сборки.
Product Flavor же немного другая вещь. Он описывает версии приложения с разной функциональностью. Так например можно задать разные applicationId для платной и бесплатной версий приложения, для того чтобы они могли одновременно существовать и Google Play или быть одновременно установлены на одном устройстве. Точно так же можно задать им разные имена. В пример можно привести Age of History и Age of History Lite - две версии одной и той же игры, одна платная, а другая бесплатная.
Build Variant - вариант сборки, совмещает эти две вещи. То есть если есть 2 build type: debug и release, и 2 product flavor: normal и lite, то будет 4 варианта сборки:
1. normalDebug
2. normalRelease
3. liteDebug
4. liteRelease
Методы получения ссылки на Fragment из Activity
Есть два стандартных метода, которые вы можете вызвать на активити, чтобы найти в ней фрагмент. Это
Первый метод находит фрагмент по тегу, который был использован при добавлении фрагмента методами
Метод же
Есть два стандартных метода, которые вы можете вызвать на активити, чтобы найти в ней фрагмент. Это
findFragmentByTag(String)
и findFragmentById(Int)
. Данные методы позволяют найти фрагмент из активити.Первый метод находит фрагмент по тегу, который был использован при добавлении фрагмента методами
add()
или replace()
. Если данный метод не находит фрагмента, который был добавлен с таким тегом, то он возвращает null.Метод же
findFragmentById()
принимает в качестве аргумента id. Id можно задать контейнером фрагмента (аргумент containerViewId при вызове add()
или replace()
), либо же в XML файле как атрибут тега <fragment>
Что такое жизненный цикл фрагмента?
Фрагменты реализуют интерфейс LifecycleOwner и, соответственно, у них можно получить объект Lifecycle. У Lifecycle есть всего 5 состояний: INITIALIZED, CREATED, STARTED, RESUMED, DESTROYED. То есть сам жизненный цикл фрагмента по сути точно такой же как и у активити, так как объект Lifecycle может иметь точно такие же состояния. Но в отличии от активити у фрагмента больше колбэк-методов. Если рассматривать жизненный цикл фрагмента вместе с колбэками, то получится как то так
- CREATED (вызов
- STARTED (вызов
- RESUME (вызов
- STARTED (вызов
- CREATED (вызов
- DESTROYED
Фрагменты реализуют интерфейс LifecycleOwner и, соответственно, у них можно получить объект Lifecycle. У Lifecycle есть всего 5 состояний: INITIALIZED, CREATED, STARTED, RESUMED, DESTROYED. То есть сам жизненный цикл фрагмента по сути точно такой же как и у активити, так как объект Lifecycle может иметь точно такие же состояния. Но в отличии от активити у фрагмента больше колбэк-методов. Если рассматривать жизненный цикл фрагмента вместе с колбэками, то получится как то так
- CREATED (вызов
onCreate()
)onCreate()
- фрагмент созданonCreateView()
- View создаетсяonViewCreated()
- View была созданаonViewStateRestored()
- состояние View было восстановлено- STARTED (вызов
onStart()
) - фрагмент видим, но с ним нельзя взаимодействовать- RESUME (вызов
onResume()
) - все анимации и тд отработали и с фрагментом можно взаимодействовать- STARTED (вызов
onPause()
) - фрагмент видим, но с ним нельзя взаимодействовать- CREATED (вызов
onStop()
)onStop()
- фрагмент больше не видимonSaveInstanceState()
- используется для сохранения состояния фрагмента (например при configuration change)onDestroyView()
- View уничтожено- DESTROYED
onDestroy()
- фрагмент уничтоженОсобенности работы с данными через ContentResolver
ContentResolver имеет методы, одноименные методам ContentProvider, и выполняющие CRUD функции (create, read, update, delete).
Вот список данных методов:
1.
2.
3.
4.
ContentResolver имеет методы, одноименные методам ContentProvider, и выполняющие CRUD функции (create, read, update, delete).
Вот список данных методов:
1.
insert()
- соответствует функции create. Создает новую запись в провайдере и возвращает URI данной записи. Данные для новой записи помещаются в объект ContentValues как пары столбец - значение.2.
query()
- соответствует функции read. С английского переводится как запрос и именно так и происходит работа с данным методов, так как работа с данным методом аналогична SELECT в SQL3.
update()
- соответствует функции update. Обновляет записи в провайдере. Данные, так и в методе insert()
, помещаются в объект ContentValues.4.
delete()
- соответствует функции delete. Работа с данным методом очень похожа на работу с методом query()
. Разница лишь в том, что вместо того чтобы вернуть данные, которые подходят под запрос, данные метод удаляет ихГде используется Target Fragment?
TargetFragment используется примерно для того же что и
Чтобы использовать его для того, чтобы получить результат выполнения какого-либо фрагмента необходимо проделать выполнить следующие шаги
1. В вызванном фрагменте указать target фрагмент методом
Данный метод принимает в себя сам фрагмент (инстанс) и
1. В вызывающем фрагменте необходимо переопределить метод
2. Вызвать в конце выполения вызванного фрагмента
Стоит понимать что данным методом можно не просто передавать данные из одного фрагмента в другой. Это довольно гибкий способ.
Target Fragment объявлен deprecated начиная с API Level 28, так что сейчас лучше его не использовать. В качестве альтернатив можно выделить связку библиотек Navigation Component (официальная библиотека от Google) и Navigation Result (создана энтузиастом).
TargetFragment используется примерно для того же что и
startActivityForResult()
, но между фрагментами. Таргет фрагмент это фрагмент который вызывает другой фрагмент чтобы получить из него какой-либо результат ()
Чтобы использовать его для того, чтобы получить результат выполнения какого-либо фрагмента необходимо проделать выполнить следующие шаги
1. В вызванном фрагменте указать target фрагмент методом
setTargetFragment()
Данный метод принимает в себя сам фрагмент (инстанс) и
requestCode
. Чтобы получить инстанст родительского фрагмента можно использовать findFragmentByTag()
1. В вызывающем фрагменте необходимо переопределить метод
onActivityResult()
2. Вызвать в конце выполения вызванного фрагмента
targetFragment.onActivityResult()
, чтобы передать данные из дочернего фрагмента в родительский.Стоит понимать что данным методом можно не просто передавать данные из одного фрагмента в другой. Это довольно гибкий способ.
Target Fragment объявлен deprecated начиная с API Level 28, так что сейчас лучше его не использовать. В качестве альтернатив можно выделить связку библиотек Navigation Component (официальная библиотека от Google) и Navigation Result (создана энтузиастом).
Особенности различия Bundle от Intent
Bundle с английского означает пакет/сверток. Этот класс используется для передачи данных между базовыми компонентами, например между активити или между фрагментами. Так же он используется для сохранения состояния активити при изменениях конфигурации. В этом классе реализованы методы
Intent же означает намерение. Этот класс используется для описания действий, которые необходимо выполнить. Например стартануть активити или сервис. При этом при старте активити или сервиса можно в intent положить те же самые примитивы, строки и Parcelable с Serializable. Но дело в том, что объект Intent в себе содержит объект Bundle, и все эти элементы интент закинет в бандл который у него есть.
Bundle с английского означает пакет/сверток. Этот класс используется для передачи данных между базовыми компонентами, например между активити или между фрагментами. Так же он используется для сохранения состояния активити при изменениях конфигурации. В этом классе реализованы методы
get()
и put()
для всех примитивов, строк, а также для Parcelable и Serializable.Intent же означает намерение. Этот класс используется для описания действий, которые необходимо выполнить. Например стартануть активити или сервис. При этом при старте активити или сервиса можно в intent положить те же самые примитивы, строки и Parcelable с Serializable. Но дело в том, что объект Intent в себе содержит объект Bundle, и все эти элементы интент закинет в бандл который у него есть.