Источники: Android Interview Questions, Mobile System Design Interview, GPT4, Собеседования на YouTube, мои походы на собеседования
Этот репозиторий написан в первую очередь автором для самообразования и подготовку к собеседованиям. Если Вы найдёте какой-то недочёт в ответах, то можете сообщить об этом.
- Kotlin
- Kotlin Coroutines
- Kotlin Flow API
- Android
- Jetpack Compose
- Android Библиотеки
- Android Архитектура
- Android System Design
- Android Unit Тестирование
- Android Инструменты и Технологии
- Java
- Прочие темы
- Структуры данных и алгоритмы
-
- Языковые особенности: Kotlin предоставляет более современный синтаксис, поддерживает функциональное программирование, null-безопасность, extension functions, и множество других возможностей, которых нет в Java.
- Совместимость: Kotlin полностью совместим с Java, позволяя использовать Java-библиотеки и фреймворки. В то же время, Kotlin предлагает более краткий и выразительный синтаксис.
- Компиляция: Kotlin компилируется в байт-код JVM, как и Java, но также может компилироваться в JavaScript или в исполняемый код для платформы Android и iOS через Kotlin/Native.
-
const
используется для определения компиляционных констант. Значения, объявленные как const, встраиваются непосредственно в код в места их использования, что улучшает производительность и уменьшает количество объектов во время выполнения. -
lateinit
используется для отложенной инициализации переменных, тип которых не может быть null. Это позволяет объявить переменную без начальной инициализации и инициализировать ее позже. -
Inline
функции встраивают код функции в место ее вызова, что уменьшает накладные расходы на вызов функций, особенно полезно для функций с лямбда-параметрами. -
Companion object
позволяет объявить члены класса, доступные без создания экземпляра этого класса, аналогично статическим членам в Java. -
Для удаления дубликатов можно использовать distinct() или преобразовать массив в Set.
-
@JvmStatic
используется в companion object для указания, что аннотированный член должен быть сгенерирован как статический метод в Java. -
@JvmField
предотвращает генерацию геттеров и сеттеров для переменной, делая ее публичным полем в Java. -
@JvmOverloads
генерирует перегруженные версии функций для Java, предоставляя значения по умолчанию для параметров. -
noinline используется для указания, что лямбда-параметр не должен инлайниться, например, если он передается в другую функцию как аргумент или сохраняется в переменной.
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { }
-
crossinline
гарантирует, что лямбда-выражение не будет содержать нелокальных возвратов, то есть возвратов из внешней функции.inline fun withoutCrossinline(block: () -> Unit) { println("Before block") block() // Этот блок может содержать нелокальный return println("After block") // Этот код может быть пропущен, если в block() есть нелокальный return } inline fun withCrossinline(crossinline block: () -> Unit) { println("Before block") block() // Этот блок не может содержать нелокальный return из-за crossinline println("After block") // Этот код будет выполнен после block() независимо от содержимого block } fun main() { withoutCrossinline { println("Inside block") return // Нелокальный возврат: выходит из main(), не достигая "After block" } withCrossinline { println("Inside block") // return // Ошибка: нелокальные возвраты запрещены в лямбде с crossinline } }
-
Функции области видимости (
let
,run
,with
,apply
,also
) позволяют упростить работу с объектами, временно изменяя их контекст или обеспечивая дополнительную область видимости для выполнения кода. -
reified
используется сinline
функциями для сохранения типов во время выполнения, позволяя работать с ними как с обычными классами, не теряя информацию о типе.inline fun <T> myGenericFunction(clazz: Class<T>, item: Any) { if (item is T) { // Ошибка: Cannot check for instance of erased type: T println("Item is of type T") } } inline fun <reified T> myGenericFunction(item: Any) { if (item is T) { // Теперь это работает, так как T является reified типом println("Item is of type T") } }
-
lateinit используется для отложенной инициализации мутабельных переменных, а lazy - для иммутабельных переменных, инициализируемых при первом обращении.
-
Инициализационный блок init используется для выполнения кода в момент создания экземпляра класса.
-
==
проверяет равенство значений, в то время как===
проверяет идентичность объектов (ссылаются ли переменные на один и тот же объект в памяти). -
Функции высшего порядка - это функции, которые принимают функции в качестве параметров или возвращают их. Это позволяет создавать выразительный и гибкий код.
-
Лямбда-выражения или лямбды - это короткие блоки кода, которые могут быть переданы в функции высшего порядка.
-
associateBy
- это функция коллекций, которая создаетMap
изList
, используя предоставленную функцию для определения ключей. -
В Kotlin все классы по умолчанию закрыты (
final
) для наследования. Чтобы класс мог быть унаследован, он должен быть помечен какopen
. -
internal
делает член класса видимым только внутри модуля, в котором он объявлен. -
partition
разделяет коллекцию на пару списков по условию: один для элементов, соответствующих условию, и один для остальных.val (positives, negatives) = list.partition { it > 0 }
-
infix
позволяет вызывать функции с одним параметром без скобок и точки.infix fun Int.add(other: Int): Int = this + other val sum = 1 add 2
-
Kotlin Multiplatform - это фреймворк, позволяющий разрабатывать кросс-платформенные приложения, используя Kotlin. Код, не зависящий от платформы, пишется один раз и может быть использован на разных платформах.
-
- Приостанавливающие функции (
suspend
) позволяют асинхронно выполнять длительные операции без блокирования потока. - Блокирующие операции заставляют поток ожидать завершения операции.
- Приостанавливающие функции (
-
Kotlin предлагает безопасность по отношению к null, сокращенный и более читаемый синтаксис по сравнению с Java, поддержку функционального программирования, совместимость с Java, поддержку корутин для асинхронного программирования.
-
val
объявляет иммутабельную переменную (константу), значение которой не может быть изменено после инициализации.var
объявляет мутабельную переменную, значение которой может быть изменено. -
if (::name.isInitialized) { println(name) }
-
lazy
предоставляет механизм ленивой инициализации дляval
, когда значение инициализируется только при первом обращении к переменной.val lazyValue: String by lazy { println("computed!") "Hello" }
-
Kotlin предоставляет четыре модификатора видимости:
private
,protected
,internal
, иpublic
(по умолчанию).private
ограничивает видимость областью объявления,protected
также какprivate
, но видимо в подклассах,internal
видимо в пределах одного модуля,public
видимо везде. -
В Kotlin нет прямого эквивалента статических методов Java, но можно использовать
companion object
или объектные объявления (object) для создания функций, доступных без экземпляра класса. -
Data классы в Kotlin автоматически генерируют методы
equals()
,hashCode()
,toString()
, а такжеcomponentN()
для каждого свойства иcopy()
. Они идеально подходят для классов, которые служат для хранения данных. -
Для создания синглтона в Kotlin используется
object
. -
public
- это модификатор доступа, который делает член класса доступным из любого места.open
указывает, что класс или член класса может быть переопределен в подклассе. -
let
используется для выполнения блока кода с объектом и возвращает результат блока.run
комбинируетlet
иwith
, выполняя блок кода с объектом и возвращая результат.with
принимает объект и блок кода, выполняет блок с объектом как контекстом и возвращает результат.apply
иalso
возвращают объект после выполнения блока кода, что удобно для инициализации.
-
Array
- это изменяемая коллекция фиксированного размера, в то время какList
может быть неизменяемой (listOf
) или изменяемой (mutableListOf
), и размер ее может меняться. -
Метки позволяют управлять потоком выполнения, особенно во вложенных циклах или при использовании лямбд.
loop@ for (i in 1..100) { for (j in 1..100) { if (i + j > 100) break@loop } }
-
Корутины - это легковесные потоки, позволяющие асинхронно выполнять длительные операции, не блокируя основной поток.
-
Coroutine Scope
определяет область видимости корутины, управляя ее жизненным циклом. -
Coroutine Context
содержит настройки корутины, такие как диспетчер, который определяет, в каком потоке будет выполняться корутина. -
launch
запускает корутину без возвращения результата и возвращаетJob
, в то время какasync
запускает корутину, которая возвращает результат в видеDeferred<T>
. -
Sealed class
- это специальный класс, который ограничивает иерархию наследования. Все подклассыsealed class
должны быть объявлены в том же файле, что и сам sealed class. Это позволяет использоватьsealed classes
в качестве выразительного средства для представления ограниченного набора типов и обеспечивает большую безопасность при использовании вwhen
выражениях, так как компилятор может проверить, обработаны ли все случаи.// В файле A.kt sealed class MySealedClass // В файле B.kt, попытка наследования приведет к ошибке class MySubclass : MySealedClass() // Это вызовет ошибку компиляции, так как MySubclass находится в другом файле
-
- Большая гибкость: Sealed классы позволяют определять различные свойства и методы для каждого подкласса, в то время как enum константы ограничены теми свойствами и методами, которые определены в самом enum классе.
- Иерархия типов: Sealed классы позволяют создавать сложные иерархии типов с различным поведением, что невозможно с enum классами.
- Поддержка состояний с данными: Подклассы sealed класса могут иметь свои собственные конструкторы с параметрами, что позволяет хранить данные в каждом состоянии. В enum классах данные могут быть только фиксированными и общими для всех констант.
-
Kotlin предоставляет множество стандартных коллекций, таких как списки (
List
), множества (Set
), карты (Map
), как изменяемые, так и неизменяемые версии. -
Расширяющие функции позволяют добавлять новые функции к существующим классам без их модификации.
fun String.addExclamation(): String = "$this!"
-
Elvis оператор используется для предоставления альтернативного значения в случае, если выражение слева от него равно
null
.val name = getName() ?: "Unknown"
-
- Делегирование свойств: Kotlin позволяет делегировать реализацию операций чтения и записи свойства другому объекту. Это делается с помощью ключевого слова by.
- Делегирование реализации интерфейса: Если класс должен реализовать интерфейс, Kotlin позволяет делегировать реализацию всех методов этого интерфейса другому объекту.
import kotlin.reflect.KProperty class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, спасибо за делегирование '${property.name}' мне!" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$value было присвоено значению '${property.name}' в $thisRef.") } } class Example { var p: String by Delegate() } fun main() { val e = Example() println(e.p) // Чтение из свойства p e.p = "Новое значение" // Запись в свойство p }
interface SoundBehavior {
fun makeSound()
}
class ScreamBehavior(val n: Int) : SoundBehavior {
override fun makeSound() {
repeat(n) {
println("Aaa!")
}
}
}
class SingBehavior(val song: String) : SoundBehavior {
override fun makeSound() {
println("Singing $song")
}
}
class Animal(soundBehavior: SoundBehavior) : SoundBehavior by soundBehavior
fun main() {
val screamer = Animal(ScreamBehavior(3))
screamer.makeSound() // Выводит "Aaa!" три раза
val singer = Animal(SingBehavior("Happy Birthday"))
singer.makeSound() // Выводит "Singing Happy Birthday"
}
-
Nothing
— специальный тип в Kotlin, который не имеет значений. Он используется для обозначения операций, которые никогда не завершаются (например, бесконечный цикл или исключение). Функция, возвращающая Nothing, указывает, что она никогда не вернет управление (то есть всегда прервет выполнение, выбросив исключение)Unit
в Kotlin аналогичен void в Java. Он используется, когда функция не возвращает значимого результата. Функции, возвращающие Unit, могут возвращать результат явно или не возвращать его вовсе, поскольку Unit — это единственный (синглтон) объект.Any
— это корневой тип для всех не-nullable типов в Kotlin, аналогично Object в Java. Он используется, когда нужно принять или вернуть любой объект, кроме null.
-
- Проверяемые (Checked exceptions): Исключения, которые должны быть явно перехвачены или объявлены в сигнатуре метода (только в Java).
- Непроверяемые (Unchecked exceptions): Исключения, которые могут возникать во время выполнения программы и не требуют обязательного перехвата (в Java и Kotlin).
-
- Unit - 1, потому что Singleton.
- Nothing - 0, потому что приватный конструктор.
-
В Kotlin все исключения являются непроверяемыми (unchecked), в отличие от Java, где существуют проверяемые (checked) и непроверяемые исключения. Kotlin не требует обязательного указания исключений в сигнатуре метода или оборачивания вызова в блок try-catch.
-
Корутины — это легковесные потоки, которые упрощают асинхронное программирование, позволяя писать асинхронный код последовательно. Они помогают избежать коллбэков и сложной цепочки промисов, делая код чище и понятнее.
-
Ключевое слово suspend используется для маркировки функций, которые могут приостанавливать выполнение корутины без блокировки потока. Такие функции могут быть вызваны только из другой suspend функции или из корутины.
-
- launch: Запускает новую корутину без блокировки текущего потока и возвращает ссылку на Job. Не предоставляет прямой доступ к результату выполнения.
- async и await: async используется для запуска корутины, которая возвращает Deferred — обещание результата. await используется для ожидания этого результата без блокировки потока.
- withContext: Позволяет изменить контекст выполнения корутины, например, для переключения между потоками, и возвращает результат выполнения блока кода.
-
- Dispatchers.Main
Используется для выполнения корутин в главном потоке пользовательского интерфейса (UI). Это критически важно для Android разработки, поскольку изменения UI должны производиться в главном потоке. - Dispatchers.IO
Предназначен для выполнения операций ввода-вывода, таких как сетевые запросы, чтение и запись файлов, операции с базами данных и т.п. - Dispatchers.Default
Это диспетчер по умолчанию, оптимизированный для выполнения вычислительно-интенсивных задач в общем пуле фоновых потоков. - Dispatchers.Unconfined
Этот диспетчер не привязан к конкретному потоку. Он начинает выполнение корутины в текущем потоке, но после первой приостановки, продолжение будет выполнено в потоке, который первым вызвал его.
- Dispatchers.Main
-
- coroutineScope. Это функция-билдер, которая создает новую область видимости корутины. Все корутины, запущенные внутри этой области, должны быть завершены, прежде чем coroutineScope вернет управление вызывающей стороне. Это полезно для структурирования кода и управления жизненным циклом группы корутин.
- coroutineContext. Это свойство, доступное внутри корутины, которое содержит контекст выполнения корутины, включая Job и Dispatcher. Контекст корутины управляет поведением корутины, например, в каком потоке она будет выполняться и как должны быть обработаны исключения.
- Job. Это ключевой компонент в системе корутин Kotlin, который представляет собой отдельную задачу, выполнение которой может быть отменено. Job позволяет управлять жизненным циклом корутины, включая ее отмену и завершение.
-
- lifecycleScope: Привязан к жизненному циклу Activity или Fragment и автоматически отменяет корутину, когда компонент уничтожается.
- viewModelScope: Привязан к жизненному циклу ViewModel и отменяет корутины при очистке ViewModel.
- GlobalScope: Глобальный скоуп корутины, который не привязан к жизненному циклу и должен использоваться с осторожностью, так как может привести к утечкам памяти.
-
- suspendCoroutine: Это низкоуровневая функция, которая приостанавливает текущую корутину до тех пор, пока не будет вызван один из переданных в нее колбэков. Она позволяет интегрировать корутины с асинхронными колбэк-базированными API.
- suspendCancellableCoroutine: Расширяет suspendCoroutine добавлением поддержки отмены. Если корутина, ожидающая в suspendCancellableCoroutine, отменяется, то можно обработать эту отмену и корректно завершить работу, например, освободить ресурсы.
-
- coroutineScope: Если одна из дочерних корутин внутри coroutineScope завершается с исключением, coroutineScope отменяет все остальные дочерние корутины и пропагирует исключение дальше.
- supervisorScope: В отличие от coroutineScope, supervisorScope позволяет дочерним корутинам завершаться независимо. Если одна дочерняя корутина завершается с исключением, supervisorScope не отменяет остальные дочерние корутины. Это полезно в ситуациях, когда необходимо обеспечить независимое выполнение дочерних корутин внутри одной области видимости.
-
Flow
в Kotlin – это тип, который может асинхронно предоставлять значения. Он поддерживает асинхронные потоки данных и используется для представления значений, которые могут быть доступны в будущем.Flow
позволяет управлять асинхронным кодом более удобно и функционально. -
В сопрограммах поток — это тип, который может выдавать несколько значений последовательно, в отличие от suspend функций, которые возвращают только одно значение
-
Flow
создаётся с помощьюbuilder
функций. Самый базовый builder – этоflow {}
, внутри которого вы можете отправлять значения с помощьюemit()
.Операторы Flow
позволяют трансформировать, фильтровать, комбинировать и выполнять другие операции с потоками данных. Например,map
иfilter
.Collector
– это терминальная операция, которая запускает выполнение flow и обрабатывает каждое значение, отправленное в поток. В примерах выше использовался collect {} как коллектор.
-
flowOn позволяет изменить контекст выполнения операций внутри потока (Flow). Это особенно полезно, когда тяжелые операции должны выполняться в фоновом потоке, а результаты обрабатываться в основном потоке
import kotlinx.coroutines.* import kotlinx.coroutines.flow.* fun main() = runBlocking { flow { for (i in 1..3) { Thread.sleep(100) // Имитация длительной операции emit(i) } }.flowOn(Dispatchers.Default) // Выполнение в фоновом потоке .map { value -> "Преобразованное значение $value" } .collect { value -> println("$value на потоке ${Thread.currentThread().name}") } }
-
Операторы, такие как
filter
,map
,zip
,flatMapConcat
,retry
,debounce
,distinctUntilChanged
,flatMapLatest
- filter: Отфильтровывает элементы, не соответствующие условию.
- map: Преобразует элементы в другие объекты.
- zip: Комбинирует два потока данных, сопоставляя их элементы.
- flatMapConcat: Преобразует каждый элемент в поток и объединяет эти потоки последовательно.
- retry: Повторяет поток при возникновении ошибки.
- debounce: Эмитирует элементы с задержкой, игнорируя быстро последующие элементы.
- distinctUntilChanged: Пропускает элементы, значение которых отличается от предыдущего.
- flatMapLatest: Аналогично flatMapConcat, но при появлении нового элемента отменяет предыдущий преобразованный поток.
-
Терминальные операторы - это те, которые запускают выполнение потока и обычно возвращают результат или вызывают сайд-эффекты (например, collect, toList, toSet, first, reduce).
-
- Cold Flow: Не начинает выполнение до вызова терминального оператора, обеспечивая ленивость и удобство создания потоков данных.
- Hot Flow: Активен независимо от наличия подписчиков, подходит для представления данных, которые изменяются во времени (например, пользовательский ввод).
-
- StateFlow: Хранит текущее состояние и извещает подписчиков о его изменении. Это Hot Flow. Требуется начальное значение, и он выдает его, как только коллектор начинает собирать данные. Он не выдает последовательные повторяющиеся значения. Он выдает значение только в том случае, если оно отличается от предыдущего элемента.
- SharedFlow: Более общий Hot Flow, который может репрезентировать множество значений и имеет более гибкие настройки. Не требует начального значения, поэтому по умолчанию не выдает никаких значений. Он выдает все значения и не заботится об отличиях от предыдущего элемента. Он также выдает последовательные повторяющиеся значения.
- callbackFlow и channelFlow: Позволяют создавать потоки на основе коллбэков или событий, удобно применять для интеграции с API, основанными на коллбэках.
-
Эти операторы используются для преобразования Cold Flow в Hot Flow (StateFlow или SharedFlow соответственно), делая поток активным и позволяя сохранять текущее состояние или делиться им сразу с несколькими подписчиками.
-
- Неэффективное использование ресурсов: Неправильное управление памятью, чрезмерное использование CPU или неправильное использование сетевых запросов может замедлить приложение.
- Проблемы с пользовательским интерфейсом: Сложные или плохо оптимизированные анимации и интерфейс могут вызывать задержки в отклике.
- Выполнение тяжелых операций в главном потоке: Выполнение длительных операций, таких как доступ к базе данных или сетевые запросы, в главном потоке UI может привести к "зависанию" приложения.
- Утечки памяти: Утечки памяти происходят, когда объекты не освобождаются после использования, что со временем может привести к истощению доступной памяти и замедлению приложения.
-
Context
в Android — это интерфейс, который предоставляет доступ к глобальной информации о приложении. Он используется для получения доступа к ресурсам, файловым системам, вызова активностей и служб. Существует три типа контекста:ApplicationContext
,ActivityContext
,BaseContext
.ApplicationContext
связан с жизненным циклом приложенияActivityContext
связан с жизненным циклом активности.BaseContext
в Android является базовым классом контекста (Context
). Он используется как родительский класс дляActivityContext
иApplicationContext
, предоставляя основные функции контекста, которые могут быть расширены или использованы в этих специализированных контекстах.BaseContext
обеспечивает доступ к ресурсам и системным службам, таким как запуск активностей, взаимодействие с различными сервисами Android и управление ресурсами приложения. В контексте Android разработки, когда говорят оBaseContext
, чаще всего имеют в виду фундаментальную функциональность контекста, на которой основаны все другие типы контекстов.
Context необходим для:
- Доступа к базовым функциям приложения, таким как доступ к ресурсам, базам данных и настройкам.
- Запуска других компонентов, таких как активности и службы.
- Взаимодействия с другими приложениями.
-
Основные компоненты Android приложения включают:
- Activity - компонент, который представляет один экран с пользовательским интерфейсом.
- Service - компонент для выполнения долгосрочных операций в фоновом режиме.
- Broadcast Receiver - компонент, который позволяет приложению получать и обрабатывать сообщения от других приложений или от системы. Эти сообщения могут касаться различных событий, таких как изменение состояния сети, низкий уровень заряда батареи или загрузка системы.
- Content Provider - это компонент, который предоставляет доступ к данным приложения другим приложениям и компонентам. Content Providers полезны для чтения и записи данных, которые должны быть доступны другим приложениям, например, контактов, календарных событий или фотографий.
-
AndroidManifest.xml
— это файл в Android приложении, который содержит важную информацию о приложении для Android системы. Он объявляет конфигурацию приложения, включая его компоненты (активности, службы, получатели широковещательных сообщений, поставщики контента), требуемые разрешения, минимальный уровень API Android, используемый приложением, и другие настройки.AndroidManifest.xml
читается системой при установке приложения, и на основе этой информации система знает, как взаимодействовать с приложением. -
Broadcast Receiver - обычно необходимо объявлять, но существует возможность динамической регистрации (в коде, а не в манифесте) для некоторых случаев использования.
-
Класс
Application
в Android представляет собой базовый класс приложения, который содержит глобальное состояние приложения. Он выполняется до того, как любой другой код приложения будет запущен, и именно здесь можно инициализировать глобальные ресурсы. КлассApplication
можно использовать для выполнения действий и настройки компонентов, которые должны быть доступны во всем приложении, например, инициализации библиотек, настройки управления сессиями или предоставления контекста для использования в других компонентах приложения. -
Запуск в контексте системы:-
Нажатие на иконку приложения: Когда пользователь нажимает на иконку, запускается интент запуска (launch intent), который определяет, какое Activity должно быть запущено. Этот интент обрабатывается системным диспетчером задач (ActivityManager).
-
ActivityManager: ActivityManager определяет, запущен ли уже процесс для этого приложения. Если процесс не запущен, система начинает процедуру его создания.
-
Zygote: Android использует механизм под названием Zygote для запуска процессов приложений. Zygote — это предварительно загруженный демон системы, который содержит предзагруженную виртуальную машину Dalvik или ART (в зависимости от версии Android). Когда требуется создать новый процесс приложения, Zygote порождает новый процесс путем вызова fork(). Это позволяет быстро и эффективно запускать приложения, поскольку общий код и ресурсы Android уже загружены в память.
Запуск в контексте приложения:
-
Инициализация приложения: После создания процесса для приложения система загружает Application класс приложения (если он указан) и вызывает его метод onCreate(). Это самый первый код, который исполняется в рамках процесса приложения.
-
Запуск Activity: После инициализации приложения система создает экземпляр основного Activity, указанного в интенте запуска, вызывая его метод onCreate(). Здесь приложение обычно загружает свой пользовательский интерфейс и выполняет начальную настройку.
-
Отображение Activity: После того как Activity было создано и инициализировано, система делает его видимым для пользователя, вызывая методы onStart() и onResume(). В этот момент пользователь видит UI приложения и может с ним взаимодействовать.
-
-
В Android обновление пользовательского интерфейса (UI) не из основного потока (также известного как UI поток) запрещено из-за модели однопоточности, которую Android использует для управления пользовательским интерфейсом.
-
Выполнение тяжелой или блокирующей работы в классе Application может привести к замедлению запуска приложения и ухудшению впечатлений пользователя, поскольку это может заблокировать главный поток UI. Класс Application предназначен для инициализации глобального состояния приложения, а не для выполнения фоновой работы.
-
Иконка приложения добавляется на рабочий стол (launcher screen) когда в манифесте приложения активити определено с интент-фильтром ACTION.MAIN и категорией LAUNCHER. При нажатии на иконку запускается активити, указанное как точка входа приложения.
-
Если установить несколько активити с интент-фильтром для лаунчера, пользователю будет предложен выбор активити для запуска при нажатии на иконку приложения. Это может быть использовано для предоставления различных точек входа в приложение
-
Если бы перед вами стояла задача залогировать самую раннюю точку старта приложения, в каком месте бы вы разместили код?
Самая ранняя точка, где можно разместить код логирования - это метод onCreate() класса Application. Этот метод вызывается при старте приложения до любого другого компонента (Activity, Service и т.д.).
-
Обновлять UI-компоненты (view) напрямую из не-UI потока нельзя из-за ограничений Android на доступ к UI-элементам только из главного потока. Для обновления UI из других потоков используются механизмы синхронизации, такие как
runOnUiThread()
или Handler. -
В Android приложениях не используется метод
main()
в традиционном смысле, как в Java-приложениях. Вместо этого, точкой входа служит компонент Activity (или другие компоненты, такие как Service, BroadcastReceiver), который запускается системой Android, когда необходимо выполнить определенное действие (например, открыть приложение). Система Android использует манифест приложения для определения компонента, который должен быть запущен.
Если объявлен Application, то вызывается он. -
Метод
onCreate()
в классе Application вызывается при создании процесса приложения и служит для инициализации ресурсов, необходимых на протяжении всего существования приложения. ОтсутствиеonDestroy()
объясняется тем, что процесс приложения уничтожается системой без каких-либо предупреждений, когда системе необходимо освободить ресурсы. Поэтому логика, требующая гарантированного выполнения перед завершением работы приложения, должна располагаться в других компонентах (например, в Activity или Service).
-
При восстановлении системой фрагментов (например, при пересоздании активности после изменения конфигурации) используется конструктор без параметров. Если вы создадите фрагмент с конструктором с параметрами и не сохраните эти параметры в onSaveInstanceState, вы можете потерять эти данные при пересоздании фрагмента, что приведет к ошибкам или непредвиденному поведению.
-
Activity - это компонент приложения, предоставляющий экран с пользовательским интерфейсом. Жизненный цикл Activity включает методы: onCreate(), onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy().
- onCreate() вызывается при первом создании активности. Здесь происходит инициализация.
- onStart() вызывается, когда активность становится видимой пользователю.
- onResume() вызывается, когда активность вступает в фокус и готова к взаимодействию с пользователем.
- onPause() вызывается при переходе активности в состояние "пауза", когда она все еще видима, но уже не в фокусе.
- onStop() вызывается, когда активность больше не видна пользователю.
- onDestroy() вызывается перед тем, как активность будет уничтожена.
- onRestart() вызывается после того, как активность была остановлена и снова была запущена пользователем.
-
Основное различие между onCreate() и onStart() заключается в их роли в жизненном цикле Activity: onCreate() служит для инициализации Activity и вызывается один раз, когда Activity создается, в то время как onStart() делает Activity видимым для пользователя и может вызываться многократно каждый раз, когда Activity переходит в видимое состояние.
-
Это может произойти в крайне редких случаях, например, если система уничтожает процесс приложения для освобождения ресурсов в условиях нехватки памяти. В обычной ситуации onPause() и onStop() всегда вызываются перед onDestroy().
-
setContentView() устанавливает макет пользовательского интерфейса для активности и должен быть вызван в onCreate(), чтобы до начала взаимодействия с пользователем активность имела загруженный и готовый интерфейс.
-
- Длительные операции, такие как сетевые запросы или обращение к базе данных, так как это может привести к "зависанию" приложения. Такие операции следует выполнять асинхронно.
- Изменение конфигурации, не связанной с инициализацией UI, которая может быть выполнена в других методах жизненного цикла.
-
onSaveInstanceState()
- Этот метод используется для сохранения данных перед паузойActivity
.onRestoreInstanceState()
- Этот метод используется для восстановления сохраненного состоянияActivity
, когдаActivity
пересоздается после уничтожения. Таким образом,onRestoreInstanceState()
получает пакет, который содержит информацию о состоянии экземпляра.
-
- standard: Запускает активити в новом экземпляре каждый раз.
- singleTop: Если экземпляр активити уже находится на вершине стека, он не создается заново, и вместо этого получает интент.
- singleTask: Создает новый задачный стек и запускает активити в новом экземпляре в этом стеке, но если активити уже существует в любом стеке, система очищает стек до этого активити и отправляет интент.
- singleInstance: Активити запускается в новом задачном стеке, но с гарантией того, что никакие другие активити не будут запущены в этом стеке.
-
Fragment
– это компонент приложения, который имеет свой жизненный цикл, получает события ввода и может быть добавлен в активность. Жизненный цикл фрагмента включает такие методы, как onAttach(), onCreate(), onCreateView(), onActivityCreated(), onStart(), onResume(), onPause(), onStop(), onDestroyView(), onDestroy(), onDetach():- onAttach() - вызывается, когда фрагмент связывается с активностью.
- onCreate() - система вызывает этот метод, когда создает фрагмент. Создайте здесь компоненты, которые необходимы вне видимости пользователя.
- onCreateView() - вызывается для создания макета фрагмента. В этом методе вы должны создать и вернуть макет фрагмента.
- onViewCreated() - Вызывается сразу после onCreateView(), когда представление фрагмента уже создано. Это хорошее место для инициализации представлений.
- onActivityCreated() - вызывается, когда активность, к которой прикреплен фрагмент, завершила свой метод onCreate().
- onStart() - вызывается, когда фрагмент становится видимым.
- onResume() - вызывается, когда фрагмент получает фокус и может взаимодействовать с пользователем.
- onPause() - вызывается, когда пользователь теряет фокус на фрагменте, и можно сохранить изменения или обновления.
- onStop() - вызывается, когда фрагмент больше не видим.
- onDestroyView() - вызывается при удалении макета фрагмента.
- onDestroy() - вызывается при уничтожении фрагмента.
- onDetach() - вызывается, когда фрагмент отсоединяется от активности.
-
- Транзакции фрагментов используются для добавления, удаления, замены фрагментов в активити во время выполнения. Это позволяет динамически изменять состав интерфейса.
- Операции с фрагментами обрабатываются атомарно как единая транзакция.
-
launchMode определяет, как активность будет вставлена в стек задач. singleTask – это режим запуска, при котором в стеке задач может быть только один экземпляр активности. Если активность уже находится в стеке, система очищает все активности, находящиеся над ней, и передает интент этой активности через onNewIntent().
-
Activity действует как контейнер для пользовательского интерфейса и управляет взаимодействием пользователя с экраном. Fragment, с другой стороны, представляет собой повторно используемую часть пользовательского интерфейса с собственным жизненным циклом, которая может быть встроена в активность. Фрагменты добавляют гибкость в проектирование интерфейса, позволяя активности включать несколько фрагментов на одном экране и переиспользовать фрагмент в разных активностях.
-
- Когда у вас есть некоторые компоненты пользовательского интерфейса, которые должны использоваться в различных
Activity
- Когда несколько представлений могут быть отображены бок о бок, как например
ViewPager
- Когда у вас есть некоторые компоненты пользовательского интерфейса, которые должны использоваться в различных
-
FragmentPagerAdapter
: КаждыйFragment
, посещенный пользователем, будет храниться в памяти, но представление будет уничтожено. Когда страница снова посещается, то создается представление, а не экземпляр фрагмента.FragmentStatePagerAdapter
: Здесь экземпляр фрагмента будет уничтожен, когда он не виден пользователю, за исключением сохраненного состояния фрагмента.
-
При добавлении фрагмента, он размещается поверх предыдущего, не удаляя его, в то время как замена удаляет текущий фрагмент и добавляет новый. Использование addToBackStack() позволяет возвращаться к предыдущему фрагменту с помощью кнопки "Назад".
-
- Через ViewModel
- Через Activity
-
По умолчанию,
Fragments
уничтожаются и пересоздаются вместе с их родительскимиActivities
при изменении конфигурации. ВызовsetRetainInstance(true)
позволяет нам обойти этот цикл уничтожения и пересоздания, сигнализируя системе о сохранении текущего экземпляра фрагмента, когдаActivity
пересоздается. -
При вызове
addToBackStack()
, транзакция замены сохраняется в стеке возврата, так что пользователь может отменить транзакцию и вернуть предыдущий фрагмент, нажав кнопку Назад.
-
View объекты являются основными строительными блоками элементов пользовательского интерфейса (UI) в
Android
. View это простая прямоугольная коробка, которая реагирует на действия пользователя. Примеры включаютEditText
,Button
,CheckBox
и т. д. View относится к классуandroid.view.View
, который является базовым классом всех классов UI.
- Measure (Измерение): На этом этапе система определяет размеры View. Родительский элемент спрашивает каждого из своих дочерних элементов, сколько места они нуждаются для отображения. Это делается через вызов метода measure(int, int). Дочерние элементы должны затем самостоятельно вычислить и указать свои размеры, вызывая метод setMeasuredDimension(int, int). Этот процесс рекурсивно повторяется для всех элементов иерархии View.
- Layout (Размещение): После того как были измерены размеры всех View, начинается этап размещения. На этом этапе определяется, где именно в родительском элементе будет находиться каждый дочерний элемент. Это делается путем вызова метода layout(int, int, int, int), который устанавливает фактические положение и размер каждого View. Координаты и размеры задаются относительно родительского элемента.
- Draw (Отрисовка): На последнем этапе система отрисовывает View на экране. Это включает в себя отрисовку фона, содержимого (текст, изображения и т.д.) и любых анимаций или декораций. Отрисовка происходит в методе draw(Canvas), который вызывает такие методы, как onDraw(Canvas), dispatchDraw(Canvas) для дочерних элементов и onDrawForeground(Canvas) для отрисовки элементов переднего плана (например, прокрутки или фокуса).
-
onAttachedToWindow(): Этот метод вызывается, когда View было прикреплено к окну. Это первый шаг, когда View становится видимым в приложении. Здесь можно инициализировать анимации или начать мониторить изменения в системе. Это также сигнализирует, что View теперь может взаимодействовать с пользователем.
-
measure(): Это внутренний метод системы Android, который запускает процесс измерения View путём вызова onMeasure(). Этот метод не предназначен для переопределения, но он важен, так как инициирует определение размера View.
-
onMeasure(int, int): В onMeasure(), View определяет, какого размера оно должно быть, исходя из предложений (specifications) родительского элемента. View должно вызвать setMeasuredDimension(int width, int height) для сохранения измеренных размеров. Это переопределяемый метод, который вы можете использовать, чтобы управлять размером вашего View.
-
layout(): Это другой внутренний метод системы, который вызывается после завершения процесса измерения. Он отвечает за расположение View в родительском контейнере. Этот метод также не предназначен для переопределения.
-
onLayout(boolean, int, int, int, int): После метода layout(), onLayout() вызывается для того, чтобы View расположило себя и все свои дочерние элементы. В случае с ViewGroup, здесь определяется местоположение дочерних View. Это переопределяемый метод, где вы устанавливаете, где должны находиться дочерние элементы внутри ViewGroup.
-
dispatchDraw(Canvas): dispatchDraw используется в ViewGroup для рисования дочерних View. Этот метод вызывается системой для рисования содержимого, и обычно его не нужно переопределять, но вы можете это сделать, чтобы управлять тем, как ваши дочерние View рисуются.
-
draw(Canvas): Это внутренний метод, который управляет всем процессом рисования, включая вызов onDraw(), рисование фона, дочерних представлений, скроллирования и т. д. Этот метод обычно не переопределяется, так как он обрабатывает несколько задач рисования и делегирует основную работу onDraw().
-
onDraw(Canvas): onDraw() — это где вы определяете, как View должно отображать свое содержимое. Это может включать рисование форм, текста, изображений или других графических элементов. Этот метод переопределяется, когда вы создаете пользовательский View и хотите контролировать его визуальное представление.
-
invalidate()
: Вызывается, когда view должно быть перерисовано, но его размер и позиция не изменяются.requestLayout()
: Вызывается, когда view должно быть измерено и размещено заново, что может привести к изменению размеров и позиции как этого view, так и его потомков.
-
- View.GONE - этот параметр делает представление полностью невидимым и не занимает место в макете.
- View.INVISIBLE - представление остается невидимым, но все еще занимает место в макете.
-
Да, можно создать пользовательский View в Android. Это делается путем наследования от класса View или одного из его подклассов и переопределения методов, таких как
onDraw()
, для рисования контента вида илиonMeasure()
для определения размера вида. -
- View - объекты View являются основными строительными блоками элементов пользовательского интерфейса (UI) в
Android
. View это простая прямоугольная коробка, которая реагирует на действия пользователя. Примеры включаютEditText
,Button
,CheckBox
и т. д. View относится к классуandroid.view.View
, который является базовым классом всех классов UI. - ViewGroup - ViewGroup это невидимый контейнер. Он содержит Views и ViewGroups. Например,
LinearLayout
это ViewGroup, который содержитButton
(View), и другие компоновки также. ViewGroup является базовым классом для компоновок.
- View - объекты View являются основными строительными блоками элементов пользовательского интерфейса (UI) в
-
Canvas — это одна из основных функций встроенного в Android фреймворка графического интерфейса, которая позволяет создавать разнообразные пользовательские интерфейсы и реализовывать сложную графику. С помощью этого инструмента разработчики могут реализовывать различные возможности, такие как рисование, анимация и обработка событий.
-
SurfaceView — это подкласс View, который предоставляет отдельный слой, на котором можно рисовать, не блокируя основной поток пользовательского интерфейса. Это полезно для рендеринга анимаций или видео, где требуется высокая производительность и меньшее взаимодействие с основным потоком UI.
-
- Relative Layout позволяет размещать виджеты относительно друг друга или относительно родительского контейнера.
- Linear Layout размещает виджеты в линейной последовательности, горизонтально или вертикально.
-
Constraint Layout — это гибкий и мощный макет в Android, который позволяет создавать сложные пользовательские интерфейсы без вложенных макетов. Он предоставляет широкие возможности для определения ограничений для позиционирования и размера виджетов, что облегчает создание адаптивных интерфейсов.
-
Дерево из View — это иерархическая структура, которая представляет все виджеты (views) и макеты (layouts), составляющие пользовательский интерфейс в Android приложении. Оптимизировать глубину дерева View можно путем уменьшения количества вложенных макетов, использования ConstraintLayout для упрощения иерархии и избегания лишних макетов, которые не вносят вклад в пользовательский интерфейс. ViewTreeObserver может использоваться для прослушивания различных событий в дереве View, таких, как момент перед отрисовкой, что может быть полезно для дополнительных оптимизаций.
-
-
ListView — это виджет для отображения списка данных в вертикальном порядке, который был частью Android с его первых версий. Он поддерживает базовую прокрутку и может отображать список, который подгружается в адаптер. Однако он имеет ограниченные возможности по настройке и не поддерживает разные типы элементов списка или сложные анимации.
-
RecyclerView — это более продвинутый и гибкий компонент для отображения списков данных, введенный в Android Lollipop (API уровня 21). Он поддерживает как вертикальную, так и горизонтальную прокрутку, разные типы элементов, сложные анимации, а также имеет более эффективное переиспользование элементов списка через механизм ViewHolder. RecyclerView также позволяет легко реализовать разные виды лэйаутов, такие как список, сетка и стаггеред грид.
-
-
RecyclerView работает на основе адаптера, который управляет данными списка и связывает эти данные с видами, отображаемыми в RecyclerView. Ключевым компонентом является паттерн ViewHolder, который хранит ссылки на все подвиды каждого элемента списка для быстрого доступа без необходимости повторного поиска. При прокрутке списка RecyclerView переиспользует виды, которые вышли за пределы экрана, применяя к ним новые данные, что значительно повышает производительность и эффективность использования памяти.
-
- Использование метода setHasFixedSize(true), если размер RecyclerView не изменяется, что позволяет оптимизировать производительность.
- Эффективное использование ViewHolder для минимизации вызовов findViewById внутри адаптера.
- Избегание сложных операций в методе onBindViewHolder адаптера, таких как загрузка изображений без кэширования. Вместо этого рекомендуется использовать библиотеки для асинхронной загрузки изображений, например, Glide.
- Оптимизация макетов элементов списка, избегая лишней вложенности.
-
- Убедитесь, что внешний RecyclerView и вложенные RecyclerView используют setHasFixedSize(true), если их размеры не изменяются.
- Используйте пулинг вьюхолдеров с помощью setRecycledViewPool(RecyclerView.RecycledViewPool) для переиспользования элементов в разных RecyclerView.
- Минимизируйте количество вызовов notifyDataSetChanged() и используйте более конкретные методы уведомления об изменениях, такие как notifyItemChanged(int), для улучшения производительности.
-
SnapHelper — это класс помощник, предназначенный для вспомогательной привязки (snapping) элементов RecyclerView к определенной точке на экране, обычно в центре или краях экрана, когда прокрутка останавливается. Это удобно, например, для создания галерей или каруселей, где требуется, чтобы элементы выравнивались относительно определенных точек.
-
DiffUtil — это утилита, предоставляемая Android SDK, которая помогает вычислить разницу между двумя списками и выдать набор операций обновления, необходимых для преобразования одного списка в другой. Это может использоваться в адаптере RecyclerView для минимизации количества обновлений и повышения производительности, особенно когда изменения в данных минимальны. DiffUtil вычисляет минимальное количество изменений и автоматически обновляет список с анимациями, делая пользовательский интерфейс более плавным и отзывчивым.
-
Dialog — это небольшое окно, которое появляется перед пользователем для выполнения действия или для отображения информации, не перекрывая полностью основной контент. Диалоги могут быть использованы для сбора пользовательского ввода через формы, отображения сообщений, выбора дат и других интерактивных элементов.
-
Toast — это маленькое всплывающее сообщение, которое отображается на короткое время и автоматически исчезает после истечения времени отображения. Toast обычно используется для отображения неинтерактивных сообщений, информирующих пользователя о каком-либо процессе (например, "Сообщение отправлено").
-
Основная разница между Dialog и DialogFragment заключается в управлении жизненным циклом и встраивании в архитектуру приложения:
- Dialog — это базовый класс для диалогов, который может быть использован напрямую, но его управление в контексте жизненного цикла активности может быть сложным.
- DialogFragment — это фрагмент, который предоставляет API для отображения диалога. Использование DialogFragment рекомендуется, так как он лучше интегрируется в жизненный цикл активности или фрагмента, позволяя управлять диалогом так же, как и другими компонентами Android.
-
Intent — это абстрактное описание операции, которое должно быть выполнено. В Android Intent используется для запуска активностей, сервисов и для передачи сообщений между компонентами приложения или разными приложениями.
-
Неявный Intent не указывает прямо на компонент, который должен обработать действие. Вместо этого он определяет общее действие, которое может быть выполнено любым приложением или компонентом, способным его обработать. Примером может служить намерение открыть веб-страницу, где не указан конкретный браузер, но задано действие ACTION_VIEW.
-
Явный Intent указывает непосредственно на компонент, который должен обработать действие, включая имя пакета или полное имя класса компонента. Явные Intent обычно используются для запуска компонентов внутри того же приложения.
-
BroadcastReceiver — это компонент приложения, который позволяет реагировать на широковещательные сообщения от других приложений или от самой системы. Эти сообщения могут касаться различных событий, таких как изменение заряда батареи, получение SMS или изменение сетевого состояния.
-
Липкий Intent (Sticky Intent) — это широковещательное сообщение, которое после отправки "застревает" в системе, позволяя новым подписчикам на BroadcastReceiver получить данные события даже после его завершения. Примером использования может быть запрос текущего состояния батареи. Следовательно, вы можете использовать это для определения состояния батареи без обязательной регистрации всех будущих изменений состояния батареи.
-
Broadcasts и intents позволяют компонентам приложения общаться между собой без необходимости напрямую связывать их код. Intents могут быть использованы для явного запуска компонентов внутри приложения или для неявного запроса действий от других приложений. Broadcasts позволяют отправлять широковещательные сообщения системе или приложениям, на которые подписаны BroadcastReceivers, реагирующие на определенные события или интенты.
-
PendingIntent — это токен, который вы передаете внешнему приложению (например, уведомлению, виджету), который позволяет этому приложению использовать разрешение вашего приложения для выполнения предопределенной операции. В основном, PendingIntent используется для уведомлений, чтобы открыть активность или выполнить сервис при нажатии на уведомление.
-
Service – это компонент приложения, который может выполнять длительные операции в фоновом режиме и не предоставляет пользовательского интерфейса. Другими словами, это способ выполнить некоторые действия в фоне, например, проигрывание музыки, загрузку файлов и т. д., без взаимодействия с пользовательским интерфейсом. Сервисы могут быть как связанными (bound), так и несвязанными. Связанный сервис предлагает клиент-серверный интерфейс, который позволяет компонентам (например, активности) взаимодействовать с сервисом, отправлять запросы, получать результаты через IPC (Межпроцессное взаимодействие). Несвязанный сервис обычно выполняется на заднем плане и не связан с каким-либо пользовательским интерфейсом или компонентом, который его запустил.
-
Сервис переднего плана (Foreground Service) – это сервис, который выполняется на переднем плане и обязательно показывает уведомление в строке состояния. Это уведомление информирует пользователя о том, что приложение выполняет некоторую задачу, которая требует внимания пользователя. Сервисы переднего плана обычно используются для задач, которые непосредственно влияют на работу пользователя с устройством, например, для проигрывания аудио или для выполнения активной задачи, такой как навигация. Использование сервиса переднего плана помогает обеспечить, что система не остановит ваше приложение, пока выполняется важная задача.
-
JobScheduler – это системный сервис, доступный начиная с Android 5.0 (API уровень 21), который позволяет разработчикам планировать различные виды задач (работы) для выполнения в определенных условиях. С помощью JobScheduler можно управлять выполнением задач в зависимости от таких условий, как состояние сети, заряд батареи и другие. Это позволяет приложениям выполнять фоновые задачи более эффективно, экономя заряд батареи и ресурсы системы. Работы, запланированные с помощью JobScheduler, могут выполняться даже после перезагрузки устройства.
-
- Интенты (Intents): Интенты позволяют приложениям запрашивать функциональность от других приложений. Например, приложение может использовать интент для запуска камеры или выбора контакта из списка контактов.
- Сервисы (Services): Приложение может использовать сервисы другого приложения для выполнения фоновых задач без взаимодействия с пользовательским интерфейсом.
- Content Providers: Позволяют делиться данными между приложениями. Например, приложение может обращаться к базе данных контактов через ContentProvider.
- Межпроцессное взаимодействие (IPC): С использованием AIDL (Android Interface Definition Language) приложения могут обмениваться данными и объектами.
-
Да, приложение Android может работать в нескольких процессах. Это можно настроить в манифесте приложения, указав атрибут android:process в теге компонента (
<activity>
,<service>
,<receiver>
,<provider>
). Каждый компонент может быть настроен на запуск в отдельном процессе, что позволяет приложению выполнять различные задачи в разных процессах одновременно. -
AIDL (Android Interface Definition Language) используется для создания интерфейсов, которые позволяют клиентам взаимодействовать с сервисом через межпроцессное взаимодействие (IPC). Для создания ограниченного сервиса через AIDL необходимо выполнить следующие шаги:
- Определение интерфейса AIDL: Создайте файл AIDL с описанием методов, которые должны быть доступны для клиентов.
- Реализация интерфейса: Создайте класс, который реализует интерфейс, определенный в AIDL. Этот класс будет обрабатывать вызовы от клиентов.
- Регистрация сервиса в манифесте: Укажите сервис в AndroidManifest.xml, добавив соответствующий тег .
- Привязка к сервису: Клиенты могут привязаться к сервису, используя bindService(), чтобы начать взаимодействие с ним.
-
- Сервисы (Services): Для выполнения длительных задач на фоне.
- JobScheduler: Для планирования задач, которые должны выполняться при определенных условиях.
- WorkManager: Для гарантированного выполнения фоновых задач и работы с ограничениями, накладываемыми системой на фоновую работу.
- IntentService: Для обработки асинхронных запросов через интенты на фоне.
-
ContentProvider – это компонент приложения, который позволяет делиться данными между разными приложениями. Он предоставляет структурированный интерфейс к данным, хранящимся в файловой системе, базе данных SQLite, вебе или любом другом постоянном хранилище данных, которым управляет приложение. ContentProvider чаще всего используется для доступа к данным в разных приложениях, например, для доступа к контактам, календарю и другим личным данным пользователя.
-
-
Future и ExecutorService: Создайте пул потоков с помощью ExecutorService и запустите задачи в виде объектов Callable или Runnable. Используйте Future для отслеживания состояния выполнения каждой задачи. После завершения всех задач можно выполнить обратный вызов.
-
CountDownLatch: Этот инструмент позволяет одному или нескольким потокам ожидать завершения определенного количества операций в других потоках. Инициализируйте CountDownLatch с числом параллельных задач и уменьшайте счетчик при завершении каждой задачи. Когда счетчик достигнет нуля, можно выполнить обратный вызов.
-
RxJava: Библиотека для реактивного программирования, которая позволяет эффективно управлять асинхронными задачами и обработкой событий. С помощью операторов zip или merge можно объединить результаты нескольких асинхронных операций и получить уведомление при их завершении.
-
Kotlin Coroutines: В Kotlin для параллельного выполнения задач и получения результатов можно использовать корутины с применением функций async и await внутри CoroutineScope. Это позволяет писать асинхронный код, который выглядит как синхронный, и легко получать результаты выполнения задач.
-
-
ANR (Application Not Responding) – это ошибка, которая происходит, когда приложение не отвечает на ввод пользователя в течение 5 секунд. Чаще всего ANR возникает, если приложение выполняет длительные операции в главном потоке (UI thread).
Предотвращение ANR:- Использование фоновых потоков: Выполняйте длительные операции (например, сетевые запросы, обращение к базе данных) в фоновых потоках, не блокируя главный поток.
- AsyncTask (до API уровня 30): AsyncTask позволял выполнять асинхронные операции на фоне, но был объявлен устаревшим. Вместо него рекомендуется использовать другие механизмы асинхронной работы.
- Использование WorkManager, JobScheduler: Эти компоненты предназначены для выполнения фоновых задач, учитывая ограничения системы и оптимизируя использование ресурсов.
-
AsyncTask позволял выполнять асинхронные операции в фоне и публиковать результаты на главном потоке без необходимости напрямую управлять потоками. Однако начиная с API уровня 30, AsyncTask был объявлен устаревшим из-за ряда недостатков и ограничений.
-
- Утечки памяти: AsyncTask может удерживать ссылку на контекст активности, что может привести к утечке памяти, если активность будет уничтожена до завершения задачи.
- Сложность управления жизненным циклом: AsyncTask не связан с жизненным циклом компонентов UI, что затрудняет управление ресурсами и обновление UI по завершении задачи.
- Ограничения на параллельное выполнение: По умолчанию задачи, выполняемые с помощью AsyncTask, запускались последовательно, что могло замедлять выполнение параллельных операций.
-
- Looper управляет очередью сообщений (Message Queue) для потока. Он зацикливает поток, ожидая входящие задачи (сообщения или выполнимые объекты) и распределяет их для обработки.
- Handler связывает Looper с кодом, позволяя отправлять и обрабатывать сообщения и выполнимые объекты в очереди Looper. Handler может использоваться для обновления пользовательского интерфейса из фонового потока.
- HandlerThread – это вспомогательный класс, создающий поток с собственным Looper, что делает его подходящим для фоновой обработки задач, требующих последовательного выполнения.
-
Утечка памяти в Android происходит, когда объекты, которые больше не нужны приложению, по-прежнему удерживаются в памяти из-за ссылок, не позволяя сборщику мусора очистить их. Это может привести к уменьшению доступной памяти и в конечном итоге к завершению работы приложения.
- Избегайте длительных ссылок на контексты: Используйте контекст приложения, когда это возможно, и избегайте удержания активностей и служб в статических переменных.
- Используйте WeakReference: Когда необходимо удерживать ссылки на объекты, которые могут быть уничтожены, используйте WeakReference.
- Отписывайтесь от слушателей и отменяйте задачи: При уничтожении активности или фрагмента отписывайтесь от слушателей событий и отменяйте фоновые задачи, чтобы избежать удержания ссылок на уничтоженные объекты.
- Можно использовать несколько подходов, включая AsyncTask, Loader, IntentService, HandlerThread. С появлением Kotlin в Android разработке стали популярны корутины для управления асинхронными задачами благодаря их эффективности и простоте использования.
- Основной UI поток (Main Thread) отвечает за обработку пользовательского интерфейса, включая отрисовку элементов на экране и обработку пользовательских взаимодействий. Выполнение длительных операций в главном потоке приводит к "зависанию" приложения и плохому пользовательскому опыту. Асинхронность позволяет выполнять тяжелые задачи в фоновых потоках, не блокируя UI поток.
-
- AsyncTask и Loader: Предоставляют фреймворки для выполнения фоновых задач с возможностью обновления UI по завершении.
- IntentService: Позволяет выполнить операции в фоновом сервисе.
- Handler и Looper: Управление потоками с помощью сообщений и выполнение кода в других потоках.
- Kotlin Coroutines: Современный и эффективный способ для асинхронного программирования, позволяет выполнять асинхронные задачи с минимальными накладными расходами и упрощенным кодом.
- RxJava: Библиотека для композиции асинхронного и событийно-ориентированного программирования с использованием наблюдаемых последовательностей.
-
- SharedPreferences: Легковесный механизм для хранения пар ключ-значение.
- Внутренняя память: Сохранение данных в файловой системе приложения.
- Внешняя память: Использование общедоступного или частного пространства на внешнем носителе.
- SQLite: Встраиваемая база данных для хранения структурированных данных.
- Room: Абстракция над SQLite, упрощающая работу с базой данных и интеграцию с LiveData, Flow для реактивного доступа к данным.
-
Это механизм для хранения и извлечения примитивных данных в виде пар ключ-значение. Используется для сохранения пользовательских настроек или другой небольшой информации, требующей быстрого доступа.
-
Данные, сохраненные в SharedPreferences, хранятся в виде XML-файлов в каталоге /data/data/<package_name>/shared_prefs/. Каждый файл SharedPreferences представляет собой набор пар ключ-значение.
-
- Serializable является стандартным Java интерфейсом и проще в использовании, но медленнее из-за использования рефлексии.
- Parcelable специфичен для Android и требует реализации некоторых методов, но он намного быстрее, что делает его предпочтительным для передачи данных между компонентами Android.
- Json. Использовать одну из любых библиотек по типу gson, moshi, kotlin serialization, jackson и т.д.
-
- Эффективность: Parcelable разработан с учетом эффективности использования памяти и скорости сериализации/десериализации, что критически важно для производительности мобильных приложений.
- Контроль: Разработчик имеет полный контроль над процессом сериализации с Parcelable, что позволяет оптимизировать процесс. Например, можно исключить из сериализации ненужные поля.
- Реализация: Parcelable требует явной реализации методов сериализации, что позволяет избежать использования рефлексии и снижает накладные расходы, связанные с Serializable.
- В целом, использование Parcelable рекомендуется для передачи данных между компонентами Android, особенно если требуется высокая производительность.
-
- Doze — это режим энергосбережения, добавленный в Android 6.0 (Marshmallow), который снижает потребление батареи, ограничивая фоновую активность приложений, когда устройство не используется в течение длительного времени.
- App Standby — это функция, также введенная в Android 6.0 (Marshmallow), которая ограничивает фоновую активность приложений, если пользователь не активно использует эти приложения.
-
Перерисовка (или re-draw) — это процесс, при котором элементы интерфейса пользователя перерисовываются на экране. Это может происходить при изменении данных, которые отображаются в элементе пользовательского интерфейса, или при изменении состояния видимости элемента. Частые перерисовки могут негативно сказаться на производительности и потреблении батареи.
-
Android Runtime (ART) — это среда выполнения приложений для операционной системы Android, которая заменила Dalvik в качестве основной среды выполнения с Android версии 5.0 (Lollipop). ART оптимизирует приложения при их установке, предварительно компилируя их в машинный код, что улучшает производительность и эффективность работы приложений по сравнению с предыдущей системой Dalvik, которая компилировала код во время выполнения приложения (Just-In-Time, JIT компиляция).
-
- Dalvik — это виртуальная машина, используемая в Android до версии 5.0 (Lollipop), которая использовала JIT (Just-In-Time) компиляцию, что означает компиляцию кода приложения в момент его выполнения.
- ART (Android Runtime) — среда выполнения, которая использует подход AOT (Ahead-Of-Time) компиляции, предварительно компилируя приложения в машинный код при их установке, что повышает производительность.
- JIT (Just-In-Time) — метод компиляции, при котором код компилируется в момент его выполнения, а не заранее.
- AOT (Ahead-Of-Time) — метод компиляции, при котором код приложения компилируется в машинный код заранее, при установке приложения, что ускоряет его запуск и работу.
-
Основное различие между Dalvik и ART заключается в методе компиляции. Dalvik использовал JIT компиляцию, компилируя код во время его выполнения, что могло замедлять запуск приложений. ART использует AOT компиляцию, компилируя приложения заранее при их установке, что ускоряет их запуск и улучшает производительность. ART также обеспечивает лучшую управляемость памятью и более эффективное выполнение приложений.
-
DEX (Dalvik Executable) — это формат исполняемых файлов, используемых в Android. Это упакованный файл, содержащий скомпилированный код, который может быть исполнен виртуальной машиной Dalvik или ART. Файлы DEX оптимизированы для минимизации потребления памяти и улучшения производительности на устройствах Android.
-
Multidex — это решение в Android для обхода ограничения Dalvik на максимальное количество методов, которое может быть в одном файле DEX — 65,536. При использовании Multidex приложение может содержать несколько файлов DEX, что позволяет включать больше методов, чем допускает одиночный DEX файл. Это особенно полезно для крупных приложений с большим количеством библиотек.
-
Да, в Android можно вручную вызвать сборщик мусора с помощью метода
System.gc()
. Однако следует отметить, что вызов сборщика мусора не гарантирует немедленной очистки памяти. Виртуальная машина Java (JVM) сама решает, когда именно выполнять сборку мусора. Этот метод может использоваться для предложения виртуальной машине выполнить сборку мусора, но без гарантии, что это произойдет немедленно.
-
Android Jetpack – это набор библиотек, инструментов и архитектурных руководств, предназначенный для упрощения разработки приложений. Он предоставляет расширенные компоненты архитектуры, которые помогают построить качественное, тестируемое и модульное приложение. Jetpack ускоряет разработку приложений, помогая автоматизировать повторяющиеся задачи, такие как работа с базой данных, навигация, управление жизненным циклом компонентов и многое другое. Использование Jetpack способствует следованию лучшим практикам, улучшает поддержку различных устройств и версий Android, а также облегчает написание кода, который хорошо работает с основными функциями Android, такими как отложенные задачи и поддержка различных экранов.
-
ViewModel – это компонент архитектуры Android, который предназначен для хранения и управления данными, связанными с пользовательским интерфейсом, прозрачно для изменений конфигурации, таких как поворот экрана. Это позволяет данным пережить пересоздание активности или фрагмента, обеспечивая более эффективное и правильное управление данными UI. ViewModel помогает уменьшить количество кода в активности или фрагменте, связанного с управлением данными, делая архитектуру приложения более чистой, тестируемой и модульной.
-
Компоненты архитектуры Android – это набор библиотек, который помогает разработчикам строить стабильные, тестируемые и поддерживаемые приложения. Включает в себя такие компоненты как LiveData, ViewModel, Room (для работы с базами данных), Paging (для постраничной загрузки данных), WorkManager (для фоновых задач) и многие другие. Эти компоненты предназначены для работы вместе, обеспечивая эффективную и оптимальную разработку приложений.
-
LiveData – это обсервабл-класс данных, который следит за изменениями и уведомляет подписчиков (например, активности или фрагменты) о данных, если эти подписчики находятся в активном состоянии жизненного цикла. Это обеспечивает автоматическое и безопасное управление данными UI, так как данные автоматически обновляются в соответствии с текущим состоянием жизненного цикла компонента.
-
LiveData учитывает жизненный цикл компонентов, автоматически останавливая и возобновляя наблюдение в зависимости от их состояния, что обеспечивает предотвращение утечек памяти и несанкционированного доступа к компонентам UI. ObservableField является частью библиотеки Data Binding и не учитывает жизненный цикл компонентов, что может привести к утечкам памяти, если не управлять подписками вручную.
-
setValue() вызывается в главном потоке и немедленно изменяет данные в LiveData, уведомляя подписчиков. postValue() предназначен для использования из фонового потока и ставит данные в очередь на обновление в главном потоке, что может быть не немедленно.
-
Для общего использования ViewModel между фрагментами можно использовать ViewModelProvider с активностью в качестве области видимости. Это позволяет фрагментам получать экземпляр ViewModel, связанный с их активностью, обеспечивая обмен данными между фрагментами через общую ViewModel.
-
WorkManager – это компонент архитектуры Android, который позволяет гибко управлять фоновыми задачами, учитывая ограничения устройства и системы, такие как состояние заряда батареи или подключение к сети. WorkManager идеально подходит для задач, требующих гарантированного выполнения, например, синхронизация данных, резервное копирование, обработка изображений и т.д., даже если приложение закрыто или устройство перезагружается.
-
ViewModel работает, сохраняя данные в специально отведенном хранилище, которое не уничтожается при изменении конфигурации, таких как поворот экрана. При изменении конфигурации активности или фрагмента, система автоматически пересоздает UI-компоненты, но ViewModel сохраняет свое состояние и данные, делая их немедленно доступными для нового экземпляра UI. Это достигается за счет хранения ViewModel в специализированном хранилище, которое привязано к ViewModelStoreOwner (например, активности или фрагменту), и не зависит от жизненного цикла отдельных компонентов UI.
-
Почему класс
Bundle
используется для передачи данных и почему мы не можем использовать простую структуру данных Map?Bundle — это специализированная структура данных, предназначенная для передачи данных между компонентами приложения (например, между активностями). Основные причины использования Bundle вместо Map:
- Сериализация: Bundle может легко сериализоваться и десериализоваться системой Android, что важно для передачи данных между процессами.
- Типизированные методы доступа: Bundle предоставляет типизированные методы для доступа к данным, что упрощает извлечение и хранение данных без явного приведения типов.
- Интеграция с Android: Bundle тесно интегрирован с Android Framework, особенно с компонентами, такими как Intents и Fragments, что облегчает работу с данными в различных частях приложения.
-
- Логирование и анализ: Используйте инструменты логирования (например, Logcat) и аналитики (например, Firebase Crashlytics) для выявления и анализа сбоев.
- Отладка: Применяйте отладчик для пошагового выполнения кода и выявления источника проблемы.
- Тестирование: Разрабатывайте и запускайте тесты (юнит-тесты, интеграционные, UI-тесты), чтобы проверить исправление и обнаружить другие потенциальные проблемы.
- Обновление зависимостей: Убедитесь, что все используемые библиотеки и SDK обновлены до последних версий.
- Пользовательская обратная связь: Отслеживайте отзывы пользователей для выявления сбоев, которые не были обнаружены во время тестирования.
-
- Serializable: Стандартный механизм Java для сериализации объектов. Прост в использовании, но менее эффективен, так как использует рефлексию и создает много временных объектов.
- Parcelable: Механизм сериализации, специфичный для Android, оптимизированный для быстрой передачи данных между компонентами. Требует явной реализации методов сериализации, но работает значительно быстрее Serializable.
Хотя Serializable проще в реализации, Parcelable является предпочтительным выбором для Android из-за его высокой производительности, особенно при передаче сложных объектов между компонентами.
-
Jetpack Compose — это современный инструментарий для создания интерфейсов на языке Kotlin в приложениях Android. Он был разработан командой Google для упрощения и ускорения процесса разработки интерфейсов, сделав его более декларативным и интуитивно понятным. В отличие от традиционного подхода с использованием XML для разметки интерфейсов, Jetpack Compose позволяет разработчикам строить интерфейс напрямую в коде, используя Kotlin.
-
- Composition: На этом этапе Compose строит или обновляет дерево компонентов UI, основываясь на текущем состоянии приложения. Компоненты (Composables) описывают, какие UI элементы должны быть отображены.
- Layout: После составления дерева UI, система определяет размеры и позиционирование каждого элемента на экране. Это включает расчёт размеров в соответствии с ограничениями родительских элементов и расположение дочерних элементов.
- Draw: На последнем этапе Compose отрисовывает UI на экране. Это включает в себя рисование текста, фигур, изображений и других визуальных элементов.
-
Composable функция в Jetpack Compose — это специальный тип функции, аннотированный с @Composable. Эти функции являются строительными блоками пользовательского интерфейса в Compose и используются для описания, как должен выглядеть и функционировать UI. В отличие от традиционных функций, composable функции могут содержать другие composable вызовы, позволяя создавать сложный и динамичный интерфейс путем комбинирования более мелких, повторно используемых компонентов.
-
В общем случае, composable функции предназначены для вызова только из других composable функций. Это связано с тем, что они используют специальный контекст композиции, который управляет их жизненным циклом, состоянием и оптимизацией перекомпозиции. Попытка вызвать composable функцию вне контекста композиции (например, из обычной функции Kotlin или из функции активити) приведет к ошибке компиляции.
Это ограничение обусловлено тем, как работает система композиции в Jetpack Compose. Когда composable функция вызывается, она регистрирует себя и свои параметры в текущем контексте композиции, что позволяет системе Compose отслеживать зависимости и определять, когда и какие части UI нужно перерисовать. Без этого контекста композиции система не сможет корректно управлять жизненным циклом composable функций, их состоянием и оптимизацией перекомпозиции. -
- remember: Это функция, используемая в Compose для сохранения состояния во время перекомпозиции. Когда параметры функции remember изменяются, она сбрасывает и пересоздаёт состояние с новыми значениями. Это полезно для сохранения объектов или значений, которые должны сохраняться между перекомпозициями, но не нуждаются в сохранении через процессы пересоздания активити или фрагмента.
- rememberSaveable: Расширяет возможности remember, позволяя сохранять простые данные через процессы пересоздания активити или фрагмента, например, при повороте экрана. rememberSaveable использует механизм сохранения состояния в Bundle и подходит для примитивных типов данных и некоторых стандартных типов, таких как String.
Чтобы использовать сохранение объектов, то необходимо пометить класс как Parcelize или использовать MapSaver, ListSaver.
-
mutableStateOf - это функция, создающая объект состояния, который может быть изменяемым. Этот объект реализует паттерн наблюдателя, так что Compose может автоматически отслеживать изменения этого состояния и перерисовывать UI компоненты, которые зависят от этого состояния. mutableStateOf идеально подходит для управления состояниями в Compose, так как обеспечивает реактивное обновление UI.
-
- derivedStateOf - эта функция используется для создания производного состояния, значение которого вычисляется на основе других состояний. derivedStateOf предоставляет оптимизированный способ отслеживания изменений, так как Compose будет перерисовывать UI только тогда, когда действительно изменится производное значение, а не каждый раз при изменении исходных состояний.
- mutableStateOf предназначен для хранения изменяемых данных и уведомления Compose о необходимости перерисовки при их изменении. Он является основой для управления состоянием в Compose.
- В отличие от mutableStateOf, derivedStateOf не хранит изменяемые данные, а предоставляет механизм для создания значения, зависящего от одного или нескольких других состояний, оптимизируя при этом процесс перерисовки.
-
Recomposition — это процесс, в ходе которого Compose автоматически перерисовывает UI компоненты при изменении состояния, от которого они зависят. Это ключевой аспект реактивного подхода к построению интерфейса в Compose, позволяющий UI динамически адаптироваться к изменениям данных. Во время перекомпозиции Compose оптимизированно обновляет только те части интерфейса, которые действительно изменились, что делает процесс быстрым и эффективным.
Recomposition активизируется изменениями в объектах состояния (например, значениях, обернутых в mutableStateOf), и Compose следит за этими изменениями, автоматически запуская перекомпозицию для затронутых компонентов. Это обеспечивает актуальность отображаемых данных и визуальных элементо -
State hoisting (подъем состояния) — это паттерн проектирования в Jetpack Compose, который рекомендуется для управления состоянием UI компонентов. Суть паттерна заключается в перемещении (или "подъеме") состояния из компонента (composable функции) на более высокий уровень в иерархии, чтобы сделать компонент более декларативным и упростить повторное использование и тестирование.
При использовании подъема состояния компонент принимает текущее состояние и функции для изменения этого состояния через свои параметры, вместо того чтобы самостоятельно их создавать и управлять ими. Это означает, что состояние хранится вне компонента и передается в него, делая компонент более универсальным и предсказуемым. -
- DisposableEffect используется для управления ресурсами, которые требуют очистки при изменении ключей или при удалении композиции из UI дерева. Это может включать отписку от подписок, остановку анимации или закрытие соединений.
- LaunchedEffect запускает корутину в контексте жизненного цикла композиции. Он полезен для выполнения асинхронных операций, таких как загрузка данных с сервера, при сохранении синхронизации с жизненным циклом Composable.
- rememberCoroutineScope предоставляет доступ к CoroutineScope для запуска корутин в контексте текущей композиции, позволяя выполнять асинхронные операции с возможностью отмены и без привязки к жизненному циклу композиции.
- rememberUpdatedState позволяет "запомнить" самое последнее значение передаваемого состояния или колбэка, так что даже если асинхронная операция завершилась после того, как состояние было изменено, используется актуальное значение.
- produceState используется для создания состояния в Composable, которое может быть асинхронно обновлено. Оно идеально подходит для инкапсуляции асинхронной логики загрузки данных непосредственно в состоянии.
- derivedStateOf используется для создания состояния, которое вычисляется на основе других состояний. Это помогает оптимизировать перерисовку, так как Compose будет перерисовывать UI только при изменении результата вычисления.
- SideEffect используется для выполнения побочных эффектов, которые должны запускаться при каждой перекомпозиции, но не требуют очистки или отмены. Это может быть полезно для обновления заголовка экрана, логирования и т.д.
-
Modifier является ключевым концептом, который позволяет вам изменять или дополнять поведение компонентов (Composables) без изменения их кода. Это мощный инструмент для реализации таких вещей, как стилизация, расположение и обработка событий ввода.
Основные аспекты Modifier:- Цепочечная природа: Modifiers могут быть соединены в цепочку, позволяя применять несколько модификаций последовательно. Каждый модификатор в цепочке вносит свой вклад, изменяя или дополняя предыдущие.
- Повторное использование: Modifiers могут быть определены один раз и повторно использованы в разных компонентах, способствуя сокращению дублирования кода и улучшению его читаемости.
- Расширяемость: Система Modifier в Compose предоставляет большое количество встроенных модификаторов, но также позволяет создавать собственные, что делает ее крайне гибкой и мощной.
-
Темы в Jetpack Compose позволяют вам управлять визуальной консистентностью вашего приложения на высоком уровне, определяя цвета, типографику и другие аспекты дизайна, которые могут быть использованы по всему приложению
-
В Jetpack Compose, система Layout позволяет разработчикам определять, как UI компоненты должны быть размещены и отображены на экране. Это включает в себя расположение элементов (как они позиционируются относительно друг друга), их размеры и пространство между ними. Compose предлагает гибкую систему Layout, которая может быть адаптирована под различные потребности дизайна.
Ключевые аспекты:- Модификаторы для управления макетом: padding, size, fillMaxSize, и др.
- Встроенные контейнеры макета: Column, Row, Box, и ConstraintLayout для сложных макетов.
- Пользовательские макеты: Создание собственных макетных контейнеров с использованием низкоуровневых API для полного контроля над расположением и рендерингом.
-
Для отображения списков данных, Compose предлагает LazyColumn и LazyRow, которые аналогичны RecyclerView в классическом Android UI Toolkit. Эти компоненты лениво отображают элементы, т.е., рендерят только те элементы, которые видны на экране, что делает их высокоэффективными для отображения больших списков.
Ключевые аспекты:- Эффективность: Ленивая загрузка элементов улучшает производительность при работе с большими объемами данных.
- Гибкость: Поддержка горизонтального и вертикального скроллинга, вложенных списков и динамически изменяемых элементов.
- Пользовательские элементы: Возможность создавать сложные и настраиваемые элементы списка.
-
Обработка жестов в Compose позволяет реагировать на различные пользовательские взаимодействия, такие как касания, свайпы, длительные нажатия и многое другое. Compose предоставляет удобные модификаторы и API для интеграции жестов в компоненты UI.
Ключевые аспекты:- Модификаторы для обработки жестов: clickable, onLongPress, swipeable, и т.д.
- Интеграция с анимациями: Жесты могут быть легко интегрированы с анимационными эффектами для создания плавных и отзывчивых интерактивных элементов.
-
Система анимации в Jetpack Compose предлагает мощные и гибкие инструменты для добавления анимаций в ваше приложение. Анимации могут быть простыми, как изменение цвета кнопки, так и сложными, как сложные переходы и трансформации.
Ключевые аспекты:- Анимированные состояния: Использование animate*AsState для создания анимированных переходов между состояниями.
- Переходные анимации: AnimatedVisibility и updateTransition для управления сложными анимациями с несколькими свойствами.
- Низкоуровневые API: Для полного контроля над анимационным процессом.
-
CompositionLocal предоставляет механизм для передачи данных вниз по дереву композиции без необходимости явно передавать пропсы через каждый уровень вложенности. Это полезно для доступа к общим данным, таким как темы, локализация или доступ к данным окружения.
Ключевые аспекты:- Избегание prop drilling: Упрощает передачу данных в глубоко вложенные компоненты.
- Доступ к данным окружения: Идеально подходит для тем, локализации и других настроек, которые должны быть доступны во всем приложении.
- Пользовательские CompositionLocal: Возможность создания собственных объектов CompositionLocal для управления специфичными для приложения данными.
-
OkHttp Interceptor
позволяет перехватывать и модифицировать запросы и ответы перед и после их отправки/получения соответственно. Это может быть полезно для добавления, удаления или изменения заголовков запросов и ответов, логирования запросов/ответов, управления кэшированием и т.д.
-
Архитектура программного обеспечения — это фундаментальная структура системы, описывающая её компоненты, их взаимодействия и связи с окружением. Архитектура нужна для обеспечения масштабируемости, удобства поддержки и разработки, а также повышения эффективности работы команды. Архитектура может не требоваться для очень маленьких или прототипных проектов, где основной целью является быстрая проверка идеи, но даже в таких случаях минимальное планирование архитектуры может сэкономить время на последующих этапах.
-
Если бы перед вами стояла задача написать приложение-будильник, с какими проблемами бы вы столкнулись?
- Точность таймеров и будильников: Необходимо обеспечить точное срабатывание будильника в установленное время, что может быть затруднено спящим режимом устройства.
- Работа в фоне: Сохранение работоспособности приложения в фоновом режиме, особенно на новых версиях Android с ограниченными возможностями фоновой работы.
- Энергопотребление: Оптимизация использования батареи приложением, особенно важно для будильников, которые должны работать всю ночь.
- Взаимодействие с пользователем: Создание удобного пользовательского интерфейса для управления множеством будильников, выбора мелодий, настройки громкости и вибрации.
- Поведение в разных часовых поясах: Учет смены часовых поясов пользователем, что может повлиять на срабатывание будильника.
- Разрешения и доступ к системным настройкам: Получение необходимых разрешений для запуска мелодии, вибрации и изменения системных настроек.
-
- Model также представляет бизнес-логику и данные.
- View отображает данные и отправляет действия пользователя в Presenter. В отличие от MVVM, View здесь может быть более "умной", например, имея непосредственное знание о своем Presenter.
- Presenter действует как посредник между View и Model, обрабатывая всю логику представления, но в отличие от ViewModel, Presenter напрямую обновляет View, обеспечивая более тесную связь между этими компонентами.
MVP можно запомнить через Presenter, который напрямую взаимодействует с View, обновляя её и реагируя на действия пользователя, подчеркивая тесную связь между ними.
-
MVVM (Model-View-ViewModel) - это архитектурный паттерн, используемый в разработке программного обеспечения для упрощения разработки пользовательских интерфейсов. Он разделяет программу на три основных компонента:
- Model представляет собой бизнес-логику и данные. Это могут быть классы для работы с базой данных, сетевые запросы и т.д.
- View отображает данные (UI пользователя) и перенаправляет действия пользователя в ViewModel.
- ViewModel является посредником между View и Model. Она обрабатывает все логику представления и реакции на действия пользователя, но не имеет прямого доступа к компонентам интерфейса.
MVVM можно ассоциировать с ViewModel, который является ключевым компонентом, делая акцент на автоматическом связывании данных между View и Model через наблюдаемые объекты.
-
MVI (Model-View-Intent) - это архитектурный паттерн, в котором
Intent
обозначает намерение изменить состояние приложения. Этот подход поощряет приложения работать в более реактивном стиле.- Model содержит состояние приложения. Оно реактивно и изменяется в ответ на Intent'ы.
- View отображает состояние Model и отправляет Intent'ы, которые представляют действия пользователя.
- Intent является уникальной особенностью MVI и представляет собой действия пользователя, которые изменяют состояние Model. Это создает однонаправленный поток данных, где View отправляет Intent'ы, которые изменяют Model, а новое состояние Model отображается в View.
MVI можно запомнить через Intent — уникальную концепцию этого паттерна, подчеркивая однонаправленный поток данных и реактивное обновление состояния приложения.
-
Архитектурные паттерны MVC (Model-View-Controller), MVP (Model-View-Presenter), MVVM (Model-View-ViewModel) и MVI (Model-View-Intent) широко используются в разработке программного обеспечения для структурирования и организации кода. Каждый из этих паттернов имеет свои преимущества и недостатки, а их применение зависит от конкретных требований проекта, используемых технологий и предпочтений команды разработчиков.
... -
Clean Architecture — это концепция архитектуры программного обеспечения, разработанная Робертом Мартином (Uncle Bob), которая направлена на создание программного обеспечения, которое легко тестировать, поддерживать и развивать благодаря четкому разделению кода на слои с определенными задачами. Применение Clean Architecture в разработке приложений для Android помогает решить многие проблемы, связанные с жизненным циклом приложения, зависимостями между компонентами и сложностью поддержки кода при его расширении.
- Presentation Layer (Презентационный слой): Включает в себя компоненты пользовательского интерфейса, такие как Activity, Fragment и ViewModel в контексте Android. Задача этого слоя — отображение данных пользователю и обработка пользовательского ввода.
- Domain Layer (Доменный слой): Является ядром приложения, содержит бизнес-логику (entities и use cases). Use Cases (или Interactors) описывают конкретные действия или бизнес-операции, которые могут быть выполнены в приложении. Entities представляют собой основные бизнес-модели. Этот слой не зависит от других слоев и определяет правила и случаи использования, специфичные для предметной области приложения.
- Data Layer (Слой данных): Отвечает за доступ к данным, будь то локальное хранилище (например, база данных SQLite) или внешние источники данных (например, веб-сервисы). Слой данных включает в себя репозитории, которые абстрагируют источники данных от остальной части приложения, предоставляя данные для доменного слоя.
Принципы Clean Architecture обеспечивают, что каждый слой зависит только от внутренних слоев, а не от внешних. Это означает, что изменения в одном слое минимально влияют на другие слои, что упрощает тестирование и обновление приложения.
- Стек работает по схеме LIFO (последним вошел, первым вышел). Всякий раз, когда вызывается новый метод, содержащий примитивные значения или ссылки на объекты, то на вершине стека под них выделяется блок памяти. Из этого можно сделать вывод, что стек хранит значения примитивных переменных, создаваемых в методах, а также ссылки на объекты в куче на которые ссылается метод.
Когда метод завершает выполнение, блок памяти (frame), отведенный для его нужд, очищается, и пространство становится доступным для следующего метода. При этом поток выполнения программы возвращается к месту вызова этого метода с последующим переходом к следующей строке кода. - Куча. Эта область памяти используется для динамического выделения памяти для объектов и классов JRE во время выполнения. Новые объекты всегда создаются в куче, а ссылки на них хранятся в стеке.
Эти объекты имеют глобальный доступ и могут быть получены из любого места программы.
- Young Generation — область где размещаются недавно созданные объекты. Когда она заполняется, происходит быстрая сборка мусора
- Old (Tenured) Generation — здесь хранятся долгоживущие объекты. Когда объекты из Young Generation достигают определенного порога «возраста», они перемещаются в Old Generation
- Permanent Generation — эта область содержит метаинформацию о классах и методах приложения, но начиная с Java 8 данная область памяти была упразднена.
-
- JDK (Java Development Kit) - это полный пакет для разработчиков Java, который включает в себя JRE (Java Runtime Environment) и компилятор Java (javac), а также другие инструменты, необходимые для разработки Java-приложений, такие как документирование (javadoc) и инструмент для архивации (jar). JDK позволяет разрабатывать и тестировать Java-приложения.
- JRE (Java Runtime Environment) - это часть программного обеспечения, которая используется для запуска Java-приложений. Она включает в себя JVM (Java Virtual Machine), библиотеки классов и другие файлы, необходимые для выполнения программ, написанных на языке Java. JRE необходима для запуска, но не для разработки Java-приложений.
- JVM (Java Virtual Machine) - это виртуальная машина, которая исполняет байт-код Java. Она отвечает за загрузку кода, проверку кода на безопасность, его выполнение и предоставление среды выполнения, которая изолирует приложение от операционной системы. JVM делает Java платформо-независимым языком, так как байт-код может выполняться на любой машине, на которой установлена JVM.
- JIT (Just-In-Time Compiler) - это компонент JVM, который увеличивает скорость выполнения Java-приложений, компилируя байт-код в родной машинный код во время выполнения программы. Вместо интерпретации байт-кода на лету, JIT компилирует часто выполняемые части кода в машинный код для увеличения производительности.
- ClassLoader - это часть JVM, которая загружает классы Java во время выполнения. ClassLoader загружает байт-код из файлов классов (.class) в Java Runtime Environment. Он играет важную роль в концепции безопасности Java, так как разделяет пространство имен классов в соответствии с их источниками (например, классы из локальной файловой системы и классы из сетевых источников).
-
ООП (Объектно-Ориентированное Программирование) в
Java
включает в себя четыре основных концепта:- Инкапсуляция: Сокрытие внутренней реализации класса и защита его данных.
- Наследование: Создание новых классов на основе существующих.
- Полиморфизм: Один и тот же метод может работать по-разному в зависимости от объекта, где он вызван, и данных, которые ему передали.
- Абстракция: это когда мы сосредотачиваемся только на существенных для задачи деталях и игнорируем всё остальное. В ООП абстракция означает, что для каждого объекта мы задаём минимальное количество методов, полей и описаний, которые позволят нам решить задачу. Чем меньше характеристик, тем лучше абстракция, но ключевые характеристики убирать нельзя.
-
- Абстрактный класс - это класс, который содержит как конкретные, так и абстрактные методы (методы без реализации). Абстрактный метод должен быть реализован подклассами абстрактного класса. Абстрактные классы не могут быть инстанцированы и должны быть расширены для использования.
- Интерфейс подобен чертежу/контракту класса (или его можно рассматривать как класс с методами, но без их реализации). Он содержит пустые методы, которые представляют, что должно быть общим у всех его подклассов. Подклассы обеспечивают реализацию каждого из этих методов. Интерфейсы реализуются.
-
- Перегрузка метода (Method Overloading): Это когда несколько методов в одном классе имеют одно и то же имя, но различаются по типу и/или количеству параметров.
- Переопределение метода (Method Overriding): Это когда подкласс заменяет метод своего суперкласса.
-
В
Java
существует четыре модификатора доступа (от строжайшего к наиболее либеральному):private
переменные, методы, конструкторы или внутренние классы видны только внутри своего класса и его методов. Этот модификатор чаще всего используется, например, для доступа к переменным только через геттеры и сеттеры или для скрытия внутренней реализации классов, которые не должны использоваться пользователем, тем самым поддерживая инкапсуляцию. Конструктор Singleton также помечается как private, чтобы избежать нежелательного инстанцирования извне.Default
(ключевое слово не используется) этот модификатор может быть применен к классам, переменным, конструкторам и методам и позволяет доступ из классов и методов внутри того же пакета.protected
может использоваться для переменных, методов и конструкторов, тем самым разрешая доступ только подклассам и классам внутри того же пакета, что и класс защищенных членов.public
модификатор широко используется для классов, переменных, конструкторов и методов для предоставления доступа из любого класса и метода где угодно. Его не следует использовать повсеместно, поскольку это подразумевает, что данные, помеченные как public, не являются чувствительными и не могут быть использованы для нанесения вреда программе.
-
Да, интерфейс может реализовать другой интерфейс (и более одного), но ему нужно использовать ключевое слово
extends
, а неimplements
. И хотя вы не можете удалять методы из родительского интерфейса, вы можете свободно добавлять новые к своему подинтерфейсу. -
- Полиморфизм: Это способность одного и того же кода вести себя по-разному в зависимости от контекста, в основном достигается через переопределение метода.
- Наследование: Это механизм в
Java
, позволяющий одному классу использовать поля и методы другого класса.
-
- Arrays: Простые, фиксированной длины структуры данных для хранения элементов одного типа.
- ArrayLists: Реализация изменяемого массива в
Java
, которая может автоматически увеличиваться и уменьшаться.
-
ArrayList это динамический массив, оптимизированный для быстрого доступа к элементам, а LinkedList — как список со связями между элементами, оптимизированный для операций вставки и удаления. Сценарии использования: если вам часто нужен доступ к элементам по индексу, выбирайте ArrayList; если в приоритете вставка и удаление, то LinkedList.
-
HashMap — это структура данных на основе хеш-таблицы для хранения пар ключ-значение, обеспечивающая быстрый доступ O(1) к элементам. Ключи уникальны, и каждый ключ соответствует одному значению. Внутренне HashMap использует массив бакетов для хранения элементов, где индекс каждого бакета определяется хеш-функцией от ключа. В случае коллизий, когда разные ключи приводят к одному индексу, используются связные списки (до Java 8) или красно-черные деревья (с Java 8), что позволяет эффективно управлять множественными элементами в одном бакете и сохранять производительность поиска. HashMap автоматически перестраивается при росте числа элементов для оптимизации производительности, но не гарантирует порядок элементов.
-
- HashSet: Реализация множества, использующая хеш-таблицу для хранения элементов. Не гарантирует порядка элементов.
- TreeSet: Реализация множества, основанная на красно-черном дереве, которая поддерживает упорядоченность элементов по возрастанию.
-
- HashMap: Коллекция, использующая пары ключ-значение для хранения данных, где каждый ключ уникален.
- Set: Интерфейс, представляющий коллекцию уникальных элементов, не допускающий дублирования.
-
Дженерики в
Java
позволяют программистам использовать типы (классы и интерфейсы) в качестве параметров при определении классов, интерфейсов и методов. Они обеспечивают строгую проверку типов во время компиляции и поддерживают обобщенное программирование, что делает код более безопасным и легче читаемым.
-
В
Java
нет примитивного варианта классаString
- все строки являются просто обертками вокруг базового массива символов, который объявлен как final. Это означает, что, как только объектString
создан, он не может быть изменен с помощью обычных инструментов языка (Reflection все еще может ужасно все испортить, потому что вJava
никакой объект на самом деле не является полностью неизменяемым). Именно поэтому переменные классаString
являются первыми кандидатами для использования, когда вы хотите переопределить hashCode() и equals() вашего класса - вы можете быть уверены, что все их требуемые контракты будут выполнены. -
Это означает, что после создания объекта
String
, его содержимое (массив символов) не может быть изменено. Любые операции, которые кажутся изменяющими строку, на самом деле создают новый объектString
. -
Это корневой класс в иерархии классов Java. Все классы наследуются от Object. Ключевые методы класса Object включают
equals()
,hashCode()
,toString()
,getClass()
,clone()
,notify()
,notifyAll()
, иwait()
. -
В Java хэш-код объекта — это целое число, используемое для распределения объектов в хэш-таблице. Если метод
hashCode()
не переопределен, класс Object предоставляет реализацию, которая преобразует внутренний адрес объекта в целое число. -
byte
short
int
long
float
double
char
boolean
-
- Числовые типы: byte (8 бит), short (16 бит), int (32 бита), long (64 бита) представляют целочисленные значения разного размера. float (32 бита) и double (64 бита) представляют числа с плавающей точкой.
- Логический тип: boolean представляет значения истина/ложь.
- Символьный тип: char (16 бит) представляет символы в Unicode.
-
- int: примитивный тип данных, представляющий целочисленные значения.
- Integer: класс-обертка, предоставляющий методы для работы с объектами, содержащими целочисленные значения, и позволяющий использовать
int
в качестве объектов.
-
В
Java
все параметры передаются по значению. Для объектов значение ссылки на объект копируется в параметр метода. -
Когда объект в Java становится недостижимым (т.е., на него не остается активных ссылок), он становится кандидатом на сборку мусора, что означает, что память, занимаемая этим объектом, может быть освобождена.
-
https://javarush.com/quests/lectures/questservlets.level18.lecture03
Сборщик мусора вJava
автоматически удаляет объекты, на которые больше нет ссылок, тем самым освобождая память для повторного использования. Все объекты размещаются в куче, управляемой JVM. Пока на объект ссылаются, JVM считает его живым. Как только на объект больше не ссылаются и, следовательно, он недоступен для кода приложения, сборщик мусора удаляет его и освобождает неиспользуемую память.
В контексте GC важно понятие "графа достижимости" (reachability graph). В этом графе объекты представляются узлами, а ссылки между объектами — рёбрами. Корни этого графа — это набор "живых" объектов, доступных напрямую (например, локальные переменные стека, активные потоки выполнения, статические поля классов и т.п.). Объект считается "живым" и не подлежит удалению GC, если он достижим, то есть до него можно добраться по цепочке ссылок от одного из корней. -
- Strong Reference (Сильная ссылка): Обычная ссылка, предотвращающая сбор мусора для объекта, пока существует сильная ссылка.
- Soft Reference (Мягкая ссылка): Сбор мусора для таких объектов происходит только в случае нехватки памяти.
- Weak Reference (Слабая ссылка): Объект может быть собран мусором при следующей сборке мусора, даже если есть слабые ссылки.
- Phantom Reference (Фантомная ссылка): Объект, на который есть только фантомные ссылки, будет собран мусором, но перед этим будет помещен в очередь ссылок, что позволяет системе очистить его ресурсы.
-
Утечка памяти происходит, когда программа постоянно использует больше памяти, чем освобождает, что в конечном итоге приводит к исчерпанию доступной памяти. При переполнении памяти программа может замедлиться или аварийно завершиться.
-
В контексте сборки мусора (GC) в Java, GC Roots (корни сборки мусора) представляют собой набор объектов, которые являются отправной точкой для сборщика мусора при его работе. Эти объекты считаются "живыми" и не могут быть собраны сборщиком мусора. Сборщик мусора использует корни GC как начальные точки для определения доступных (достижимых) объектов. Любой объект, который можно достичь, пройдя от корней GC через цепочку ссылок, также считается живым и, следовательно, не подлежит уничтожению.
GC Roots включают в себя:-
Локальные переменные: Переменные, находящиеся в стеке вызовов (то есть переменные, принадлежащие активным методам).
-
Активные потоки: Объекты потока, которые все еще выполняются.
-
Статические поля: Переменные класса, поскольку они принадлежат классу, который находится в области PermGen (до Java 8) или Metaspace (начиная с Java 8) и доступен из любой точки приложения.
-
JNI (Java Native Interface) ссылки: Объекты, на которые ссылаются из нативного кода.
Эти корни формируют основу для графа достижимости — структуры данных, которую сборщик мусора использует для определения, какие объекты в куче живы и какие можно уничтожить. Процесс определения достижимых объектов начинается с этих корней и рекурсивно проходит через все объекты, на которые имеются ссылки. Объекты, до которых невозможно добраться от корней GC, считаются недостижимыми и могут быть собраны сборщиком мусора, тем самым освобождая ресурсы и память, которые они занимали.
-
-
- Deadlock возникает, когда два или более потоков в системе ожидают ресурсов, занятых друг другом, в результате чего они вечно ждут и не могут продолжить выполнение.
- Livelock – это ситуация, аналогичная deadlock, но потоки не заблокированы; они активно пытаются разрешить вопрос, но так и не достигают прогресса, постоянно меняя своё состояние в ответ на состояние друг друга.
-
Ситуация в многопоточной программе, когда несколько потоков пытаются одновременно изменить общие данные, и конечный результат зависит от того, какой поток завершит свою работу первым. Это может привести к непредсказуемым и ошибочным результатам.
-
Ключевое слово
synchronized
используется для обозначения методов или блоков кода, которые могут быть выполнены только одним потоком одновременно для предотвращения проблем совместного доступа. Это помогает предотвратить race conditions при доступе к общим ресурсам. -
Классы из пакета java.util.concurrent.atomic, которые поддерживают атомарные операции на одном значении без использования синхронизации. Благодаря использованию низкоуровневых атомарных инструкций процессора, такие операции могут быть выполнены без блокировок, что делает их очень быстрыми и эффективными.
-
ThreadPoolExecutor
- это реализация пула потоков вJava
, которая управляет пулом рабочих потоков, позволяя эффективно выполнять множество асинхронных задач. -
Указывает компилятору и виртуальной машине Java, что значение переменной может изменяться разными потоками. Это обеспечивает, что значение переменной будет считываться из основной памяти, а не из кэша CPU, гарантируя, что чтение переменной всегда дает последнее записанное значение.
-
Классы в пакете atomic предоставляют общий набор методов: get, set, lazyset, compareAndSet, и weakCompareAndSet.
- get: возвращает текущее значение.
- set: устанавливает новое значение.
- lazyset: устанавливает новое значение с гарантией порядка изменений только в однопоточных контекстах.
- compareAndSet: атомарно устанавливает новое значение, если текущее значение соответствует ожидаемому.
- weakCompareAndSet: вариант
compareAndSet
с возможностью неудачи в некоторых ситуациях для улучшения производительности.
-
- try{}: блок кода, в котором могут возникнуть исключения.
- catch{}: блок кода, обрабатывающий исключение, возникшее в блоке try.
- finally{}: блок кода, который выполняется после блоков try/catch, независимо от того, возникло исключение или нет.
-
- Проверяемые исключения (Checked Exceptions): это исключения, которые должны быть явно перехвачены или объявлены в методе.
- Непроверяемые исключения (Unchecked Exceptions): это исключения, которые не требуют явного перехвата или объявления в методе.
-
Сериализация - это процесс преобразования объекта в последовательность битов для сохранения или передачи. В
Java
это можно реализовать с помощью интерфейсаSerializable
. -
Модификатор
transient
используется для исключения полей класса при сериализации. -
Анонимные классы - это классы без имени, объявленные и инициализированные одновременно, обычно используются для создания экземпляров простых интерфейсов или классов на месте.
-
- ==: проверяет равенство ссылок на объекты.
- .equals(): проверяет равенство содержимого объектов.
-
Методы
hashCode()
иequals()
используются для сравнения объектов.hashCode()
возвращает хеш-код объекта, аequals()
проверяет, равны ли два объекта. -
Ключевое слово
final
используется для объявления констант, переменных, которые не могут быть изменены после инициализации. -
- final: используется для объявления констант, методов, которые не могут быть переопределены, и классов, которые не могут быть наследованы.
- finally: блок кода, который выполняется после блока try/catch в конструкции обработки исключений, независимо от того, было ли исключение.
- finalize(): метод, вызываемый перед утилизацией объекта сборщиком мусора.
-
- throw: используется для явного выброса исключения.
- throws: указывает на то, что метод может выбросить исключение, и требует его обработки или дальнейшего объявления.
-
Ключевое слово
static
используется для объявления членов класса, которые могут быть доступны без создания экземпляра класса. -
Статические методы не могут быть переопределены в том смысле, как переопределяются обычные методы. Они могут быть "скрыты" в подклассе, если в подклассе объявлен статический метод с тем же именем.
-
Статический блок выполняется при первой загрузке класса в JVM.
-
Рефлексия в
Java
позволяет программе исследовать или "размышлять" о себе самой, изменять свое поведение во время выполнения. -
Внедрение зависимостей - это дизайн-паттерн, который позволяет классам получать свои зависимости извне, вместо их создания внутри себя, улучшая модульность и тестируемость.
-
- StringBuffer: потокобезопасная версия изменяемой последовательности символов.
- StringBuilder: не потокобезопасная версия, более быстрая по сравнению с
StringBuffer
при выполнении операций в однопоточном режиме.
-
Быстро-сбойные итераторы немедленно выдают
ConcurrentModificationException
, если коллекция была изменена после создания итератора любым способом, кроме самого итератора. Это предназначено для обнаружения ошибок в многопоточных программах. Безопасно-сбойные итераторы используют механизмы, такие как копирование коллекции, что позволяет итераторам проходить по коллекции без выбрасывания исключения, даже если основная коллекция была изменена во время итерации. -
Монитор в
Java
- это механизм, обеспечивающий возможность синхронизированного доступа к блоку кода или методу, так что в каждый момент времени только один поток может исполнять блок кода или метод, помеченный какsynchronized
. Синхронизация используется для предотвращения проблем совместного доступа и состояния гонки, обеспечивая, чтобы только один поток мог выполнить определенный участок кода, работающий с общими ресурсами, в данный момент времени.
SOLID — это аббревиатура, обозначающая пять базовых принципов объектно-ориентированного программирования и дизайна, которые помогают разработчикам создавать более устойчивые, удобные в обслуживании и расширяемые системы.
-
Этот принцип гласит, что класс должен иметь только одну причину для изменения. Это означает, что класс должен выполнять только одну задачу или иметь одну область ответственности.
// Нарушение принципа class UserSettings { fun changeUserEmail(user: User, newEmail: String) { // Изменение email } fun printUserReport(user: User) { // Печать отчета пользователя } } // Соблюдение принципа class EmailManager { fun changeUserEmail(user: User, newEmail: String) { // Изменение email } } class ReportPrinter { fun printUserReport(user: User) { // Печать отчета пользователя } }
-
Классы должны быть открыты для расширения, но закрыты для модификации. Это означает, что можно добавлять новые функциональности, не изменяя существующий код.
// Нарушение принципа class GraphicEditor { fun drawShape(shape: Shape) { when (shape) { is Circle -> drawCircle(shape) is Square -> drawSquare(shape) // При добавлении новых фигур необходимо изменять этот класс } } fun drawCircle(circle: Circle) { /*...*/ } fun drawSquare(square: Square) { /*...*/ } } // Соблюдение принципа abstract class Shape { abstract fun draw() } class Circle : Shape() { override fun draw() { /* Рисование круга */ } } class Square : Shape() { override fun draw() { /* Рисование квадрата */ } } class GraphicEditor { fun drawShape(shape: Shape) { shape.draw() } }
-
Объекты в программе можно заменить их наследниками без изменения свойств программы. Наследуемый класс должен дополнять, а не заменять поведение базового класса.
// Нарушение принципа open class Bird { open fun fly() { // реализация полета } } class Penguin : Bird() { override fun fly() { throw UnsupportedOperationException("Пингвины не умеют летать.") } } // Соблюдение принципа open class Bird open class FlyingBird : Bird() { open fun fly() { // реализация полета } } class Penguin : Bird()
-
Клиенты не должны быть вынуждены реализовывать интерфейсы, которые они не используют. Интерфейсы должны быть специфическими, а не универсальными.
// Нарушение принципа interface Worker { fun work() fun eat() } class Human : Worker { override fun work() { /* Работаем */ } override fun eat() { /* Едим */ } } class Robot : Worker { override fun work() { /* Работаем */ } override fun eat() { /* Роботы не едят, нарушение принципа */ } } // Соблюдение принципа interface Workable { fun work() } interface Eatable { fun eat() } class Human : Workable, Eatable { override fun work() { /* Работаем */ } override fun eat() { /* Едим */ } } class Robot : Workable { override fun work() { /* Работаем */ } }
-
Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба типа модулей должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
// Нарушение принципа class LightBulb { fun turnOn() { /* Включение */ } fun turnOff() { /* Выключение */ } } class ElectricPowerSwitch { var bulb: LightBulb = LightBulb() fun press() { if (bulb.turnOn()) { bulb.turnOff() } else { bulb.turnOn() } } } // Соблюдение принципа interface Switchable { fun turnOn() fun turnOff() } class LightBulb : Switchable { override fun turnOn() { /* Включение */ } override fun turnOff() { /* Выключение */ } } class ElectricPowerSwitch(var device: Switchable) { fun press() { if (device.turnOn()) { device.turnOff() } else { device.turnOn() } } }
-
Эти паттерны отвечают за удобное и безопасное создание новых объектов или даже целых семейств объектов.
- Фабричный метод (Factory Method)
- Абстрактная фабрика (Abstract Factory)
- Строитель (Builder)
- Прототип (Prototype)
- Одиночка (Singleton)
-
Эти паттерны отвечают за построение удобных в поддержке иерархий классов.
- Адаптер (Adapter)
- Мост (Bridge)
- Компоновщик (Composite)
- Декоратор (Decorator)
- Фасад (Facade)
- Легковес (Flyweight)
- Заместитель (Proxy)
-
Эти паттерны решают задачи эффективного и безопасного взаимодействия между объектами программы.
- Цепочка обязанностей (Chain Of Responsibility)
- Команда (Command)
- Итератор (Iterator)
- Посредник (Mediator)
- Снимок (Memento)
- Наблюдатель (Observer)
- Состояние (State)
- Стратегия (Strategy)
- Шаблонный метод (Template Method)
- Посетитель (Visitor)
Алгоритмы, которые могут спросить на собеседованиях зачастую берут с LeetCode сложности easy и medium: