Skip to content

Всё, что связано с собеседованием Android

Notifications You must be signed in to change notification settings

Senchick/android-interview

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 

Repository files navigation

Android Interview

Источники: Android Interview Questions, Mobile System Design Interview, GPT4, Собеседования на YouTube, мои походы на собеседования

Disclaimer

Этот репозиторий написан в первую очередь автором для самообразования и подготовку к собеседованиям. Если Вы найдёте какой-то недочёт в ответах, то можете сообщить об этом.

Оглавление

Kotlin

  • В чем разница между Java и Kotlin?

    • Языковые особенности: Kotlin предоставляет более современный синтаксис, поддерживает функциональное программирование, null-безопасность, extension functions, и множество других возможностей, которых нет в Java.
    • Совместимость: Kotlin полностью совместим с Java, позволяя использовать Java-библиотеки и фреймворки. В то же время, Kotlin предлагает более краткий и выразительный синтаксис.
    • Компиляция: Kotlin компилируется в байт-код JVM, как и Java, но также может компилироваться в JavaScript или в исполняемый код для платформы Android и iOS через Kotlin/Native.
  • Каково преимущество использования const в Kotlin?

    const используется для определения компиляционных констант. Значения, объявленные как const, встраиваются непосредственно в код в места их использования, что улучшает производительность и уменьшает количество объектов во время выполнения.

  • Когда использовать ключевое слово lateinit в Kotlin?

    lateinit используется для отложенной инициализации переменных, тип которых не может быть null. Это позволяет объявить переменную без начальной инициализации и инициализировать ее позже.

  • Что такое inline функция в Kotlin?

    Inline функции встраивают код функции в место ее вызова, что уменьшает накладные расходы на вызов функций, особенно полезно для функций с лямбда-параметрами.

  • Что такое companion object в Kotlin?

    Companion object позволяет объявить члены класса, доступные без создания экземпляра этого класса, аналогично статическим членам в Java.

  • Удаление дубликатов из массива в Kotlin

    Для удаления дубликатов можно использовать distinct() или преобразовать массив в Set.

  • Что такое аннотация JvmStatic в Kotlin?

    @JvmStatic используется в companion object для указания, что аннотированный член должен быть сгенерирован как статический метод в Java.

  • Что такое аннотация JvmField в Kotlin?

    @JvmField предотвращает генерацию геттеров и сеттеров для переменной, делая ее публичным полем в Java.

  • Что такое аннотация JvmOverloads в Kotlin?

    @JvmOverloads генерирует перегруженные версии функций для Java, предоставляя значения по умолчанию для параметров.

  • noinline в Kotlin

    noinline используется для указания, что лямбда-параметр не должен инлайниться, например, если он передается в другую функцию как аргумент или сохраняется в переменной.

    inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { }
  • crossinline в Kotlin

    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
      }
    }
  • Функции области видимости в Kotlin

    Функции области видимости (let, run, with, apply, also) позволяют упростить работу с объектами, временно изменяя их контекст или обеспечивая дополнительную область видимости для выполнения кода.

  • Что такое ключевое слово reified в Kotlin?

    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 в Kotlin

    lateinit используется для отложенной инициализации мутабельных переменных, а lazy - для иммутабельных переменных, инициализируемых при первом обращении.

  • Что такое блок init в Kotlin?

    Инициализационный блок init используется для выполнения кода в момент создания экземпляра класса.

  • Разница между == и === в Kotlin

    == проверяет равенство значений, в то время как === проверяет идентичность объектов (ссылаются ли переменные на один и тот же объект в памяти).

  • Что такое функции высшего порядка в Kotlin?

    Функции высшего порядка - это функции, которые принимают функции в качестве параметров или возвращают их. Это позволяет создавать выразительный и гибкий код.

  • Что такое Лямбды в Kotlin?

    Лямбда-выражения или лямбды - это короткие блоки кода, которые могут быть переданы в функции высшего порядка.

  • AssociateBy - List to Map в Kotlin

    associateBy - это функция коллекций, которая создает Map из List, используя предоставленную функцию для определения ключей.

  • Ключевое слово Open в Kotlin

    В Kotlin все классы по умолчанию закрыты (final) для наследования. Чтобы класс мог быть унаследован, он должен быть помечен как open.

  • Модификатор видимости internal в Kotlin

    internal делает член класса видимым только внутри модуля, в котором он объявлен.

  • partition - функция фильтрации в Kotlin

    partition разделяет коллекцию на пару списков по условию: один для элементов, соответствующих условию, и один для остальных.

    val (positives, negatives) = list.partition { it > 0 }
  • infix в Kotlin

    infix позволяет вызывать функции с одним параметром без скобок и точки.

    infix fun Int.add(other: Int): Int = this + other
    val sum = 1 add 2
  • Как работает Kotlin Multiplatform?

    Kotlin Multiplatform - это фреймворк, позволяющий разрабатывать кросс-платформенные приложения, используя Kotlin. Код, не зависящий от платформы, пишется один раз и может быть использован на разных платформах.

  • Приостанавливающие vs Блокирующие функции в Kotlin Coroutines

    • Приостанавливающие функции (suspend) позволяют асинхронно выполнять длительные операции без блокирования потока.
    • Блокирующие операции заставляют поток ожидать завершения операции.
  • Расскажите некоторые преимущества Kotlin.

    Kotlin предлагает безопасность по отношению к null, сокращенный и более читаемый синтаксис по сравнению с Java, поддержку функционального программирования, совместимость с Java, поддержку корутин для асинхронного программирования.

  • В чем разница между val и var?

    val объявляет иммутабельную переменную (константу), значение которой не может быть изменено после инициализации. var объявляет мутабельную переменную, значение которой может быть изменено.

  • Как проверить, инициализирована ли переменная lateinit?

    if (::name.isInitialized) {
      println(name)
    }
  • Как выполнить ленивую инициализацию переменных в Kotlin?

    lazy предоставляет механизм ленивой инициализации для val, когда значение инициализируется только при первом обращении к переменной.

    val lazyValue: String by lazy {
      println("computed!")
      "Hello"
    }
  • Какие существуют модификаторы видимости в Kotlin?

    Kotlin предоставляет четыре модификатора видимости: private, protected, internal, и public (по умолчанию). private ограничивает видимость областью объявления, protected также как private, но видимо в подклассах, internal видимо в пределах одного модуля, public видимо везде.

  • Что является эквивалентом статических методов Java в Kotlin?

    В Kotlin нет прямого эквивалента статических методов Java, но можно использовать companion object или объектные объявления (object) для создания функций, доступных без экземпляра класса.

  • Что такое data класс в Kotlin?

    Data классы в Kotlin автоматически генерируют методы equals(), hashCode(), toString(), а также componentN() для каждого свойства и copy(). Они идеально подходят для классов, которые служат для хранения данных.

  • Как создать Singleton класс в Kotlin?

    Для создания синглтона в Kotlin используется object.

  • В чем разница между open и public в Kotlin?

    public - это модификатор доступа, который делает член класса доступным из любого места. open указывает, что класс или член класса может быть переопределен в подклассе.

  • Объясните использование let, run, with, also, apply в Kotlin.

    • let используется для выполнения блока кода с объектом и возвращает результат блока.
    • run комбинирует let и with, выполняя блок кода с объектом и возвращая результат.
    • with принимает объект и блок кода, выполняет блок с объектом как контекстом и возвращает результат.
    • apply и also возвращают объект после выполнения блока кода, что удобно для инициализации.
  • Разница между типами List и Array в Kotlin

    Array - это изменяемая коллекция фиксированного размера, в то время как List может быть неизменяемой (listOf) или изменяемой (mutableListOf), и размер ее может меняться.

  • Что такое Labels в Kotlin?

    Метки позволяют управлять потоком выполнения, особенно во вложенных циклах или при использовании лямбд.

    loop@ for (i in 1..100) {
      for (j in 1..100) {
          if (i + j > 100) break@loop
      }
    }
  • Что такое Корутины в Kotlin?

    Корутины - это легковесные потоки, позволяющие асинхронно выполнять длительные операции, не блокируя основной поток.

  • Что такое Coroutine Scope?

    Coroutine Scope определяет область видимости корутины, управляя ее жизненным циклом.

  • Что такое Coroutine Context?

    Coroutine Context содержит настройки корутины, такие как диспетчер, который определяет, в каком потоке будет выполняться корутина.

  • Launch vs Async в Kotlin Корутинах

    launch запускает корутину без возвращения результата и возвращает Job, в то время как async запускает корутину, которая возвращает результат в виде Deferred<T>.

  • Когда использовать запечатанные классы (sealed classes) Kotlin?

    Sealed class - это специальный класс, который ограничивает иерархию наследования. Все подклассы sealed class должны быть объявлены в том же файле, что и сам sealed class. Это позволяет использовать sealed classes в качестве выразительного средства для представления ограниченного набора типов и обеспечивает большую безопасность при использовании в when выражениях, так как компилятор может проверить, обработаны ли все случаи.

    // В файле A.kt
    sealed class MySealedClass
    
    // В файле B.kt, попытка наследования приведет к ошибке
    class MySubclass : MySealedClass() // Это вызовет ошибку компиляции, так как MySubclass находится в другом файле
  • Зачем были созданы sealed классы, если есть enum классы?

    • Большая гибкость: Sealed классы позволяют определять различные свойства и методы для каждого подкласса, в то время как enum константы ограничены теми свойствами и методами, которые определены в самом enum классе.
    • Иерархия типов: Sealed классы позволяют создавать сложные иерархии типов с различным поведением, что невозможно с enum классами.
    • Поддержка состояний с данными: Подклассы sealed класса могут иметь свои собственные конструкторы с параметрами, что позволяет хранить данные в каждом состоянии. В enum классах данные могут быть только фиксированными и общими для всех констант.
  • Расскажите о Collections в Kotlin

    Kotlin предоставляет множество стандартных коллекций, таких как списки (List), множества (Set), карты (Map), как изменяемые, так и неизменяемые версии.

  • Extension функции

    Расширяющие функции позволяют добавлять новые функции к существующим классам без их модификации.

    fun String.addExclamation(): String = "$this!"
  • Что делает ?: в Kotlin? (Оператор Элвиса)

    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 vs unit vs any

    • Nothing — специальный тип в Kotlin, который не имеет значений. Он используется для обозначения операций, которые никогда не завершаются (например, бесконечный цикл или исключение). Функция, возвращающая Nothing, указывает, что она никогда не вернет управление (то есть всегда прервет выполнение, выбросив исключение)
    • Unit в Kotlin аналогичен void в Java. Он используется, когда функция не возвращает значимого результата. Функции, возвращающие Unit, могут возвращать результат явно или не возвращать его вовсе, поскольку Unit — это единственный (синглтон) объект.
    • Any — это корневой тип для всех не-nullable типов в Kotlin, аналогично Object в Java. Он используется, когда нужно принять или вернуть любой объект, кроме null.
  • Какие бывают исключения?

    • Проверяемые (Checked exceptions): Исключения, которые должны быть явно перехвачены или объявлены в сигнатуре метода (только в Java).
    • Непроверяемые (Unchecked exceptions): Исключения, которые могут возникать во время выполнения программы и не требуют обязательного перехвата (в Java и Kotlin).
  • Сколько инстансов Unit и Nothing мы можем создать в приложении?

    • Unit - 1, потому что Singleton.
    • Nothing - 0, потому что приватный конструктор.
  • В чем разница между исключениями в Kotlin и Java?

    В Kotlin все исключения являются непроверяемыми (unchecked), в отличие от Java, где существуют проверяемые (checked) и непроверяемые исключения. Kotlin не требует обязательного указания исключений в сигнатуре метода или оборачивания вызова в блок try-catch.

Kotlin Coroutines

  • корутины

    Корутины — это легковесные потоки, которые упрощают асинхронное программирование, позволяя писать асинхронный код последовательно. Они помогают избежать коллбэков и сложной цепочки промисов, делая код чище и понятнее.

  • suspend

    Ключевое слово suspend используется для маркировки функций, которые могут приостанавливать выполнение корутины без блокировки потока. Такие функции могут быть вызваны только из другой suspend функции или из корутины.

  • launch, async-await, withContext

    • launch: Запускает новую корутину без блокировки текущего потока и возвращает ссылку на Job. Не предоставляет прямой доступ к результату выполнения.
    • async и await: async используется для запуска корутины, которая возвращает Deferred — обещание результата. await используется для ожидания этого результата без блокировки потока.
    • withContext: Позволяет изменить контекст выполнения корутины, например, для переключения между потоками, и возвращает результат выполнения блока кода.
  • Dispatchers

    • Dispatchers.Main
      Используется для выполнения корутин в главном потоке пользовательского интерфейса (UI). Это критически важно для Android разработки, поскольку изменения UI должны производиться в главном потоке.
    • Dispatchers.IO
      Предназначен для выполнения операций ввода-вывода, таких как сетевые запросы, чтение и запись файлов, операции с базами данных и т.п.
    • Dispatchers.Default
      Это диспетчер по умолчанию, оптимизированный для выполнения вычислительно-интенсивных задач в общем пуле фоновых потоков.
    • Dispatchers.Unconfined
      Этот диспетчер не привязан к конкретному потоку. Он начинает выполнение корутины в текущем потоке, но после первой приостановки, продолжение будет выполнено в потоке, который первым вызвал его.
  • coroutineScope, coroutineContext, job

    • coroutineScope. Это функция-билдер, которая создает новую область видимости корутины. Все корутины, запущенные внутри этой области, должны быть завершены, прежде чем coroutineScope вернет управление вызывающей стороне. Это полезно для структурирования кода и управления жизненным циклом группы корутин.
    • coroutineContext. Это свойство, доступное внутри корутины, которое содержит контекст выполнения корутины, включая Job и Dispatcher. Контекст корутины управляет поведением корутины, например, в каком потоке она будет выполняться и как должны быть обработаны исключения.
    • Job. Это ключевой компонент в системе корутин Kotlin, который представляет собой отдельную задачу, выполнение которой может быть отменено. Job позволяет управлять жизненным циклом корутины, включая ее отмену и завершение.
  • lifecycleScope, viewModelScope, GlobalScope

    • lifecycleScope: Привязан к жизненному циклу Activity или Fragment и автоматически отменяет корутину, когда компонент уничтожается.
    • viewModelScope: Привязан к жизненному циклу ViewModel и отменяет корутины при очистке ViewModel.
    • GlobalScope: Глобальный скоуп корутины, который не привязан к жизненному циклу и должен использоваться с осторожностью, так как может привести к утечкам памяти.
  • suspendCoroutine, suspendCancellableCoroutine

    • suspendCoroutine: Это низкоуровневая функция, которая приостанавливает текущую корутину до тех пор, пока не будет вызван один из переданных в нее колбэков. Она позволяет интегрировать корутины с асинхронными колбэк-базированными API.
    • suspendCancellableCoroutine: Расширяет suspendCoroutine добавлением поддержки отмены. Если корутина, ожидающая в suspendCancellableCoroutine, отменяется, то можно обработать эту отмену и корректно завершить работу, например, освободить ресурсы.
  • coroutineScope, supervisorScope

    • coroutineScope: Если одна из дочерних корутин внутри coroutineScope завершается с исключением, coroutineScope отменяет все остальные дочерние корутины и пропагирует исключение дальше.
    • supervisorScope: В отличие от coroutineScope, supervisorScope позволяет дочерним корутинам завершаться независимо. Если одна дочерняя корутина завершается с исключением, supervisorScope не отменяет остальные дочерние корутины. Это полезно в ситуациях, когда необходимо обеспечить независимое выполнение дочерних корутин внутри одной области видимости.

Kotlin Flow API

  • Что такое Flow?

    Flow в Kotlin – это тип, который может асинхронно предоставлять значения. Он поддерживает асинхронные потоки данных и используется для представления значений, которые могут быть доступны в будущем. Flow позволяет управлять асинхронным кодом более удобно и функционально.

  • Flow vs Coroutines

    В сопрограммах поток — это тип, который может выдавать несколько значений последовательно, в отличие от suspend функций, которые возвращают только одно значение

  • Flow Builder, Operator, Collector

    • Flow создаётся с помощью builder функций. Самый базовый builder – это flow {}, внутри которого вы можете отправлять значения с помощью emit().
    • Операторы Flow позволяют трансформировать, фильтровать, комбинировать и выполнять другие операции с потоками данных. Например, map и filter.
    • Collector – это терминальная операция, которая запускает выполнение flow и обрабатывает каждое значение, отправленное в поток. В примерах выше использовался collect {} как коллектор.
  • flowOn, Dispatchers

    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

    • Cold Flow: Не начинает выполнение до вызова терминального оператора, обеспечивая ленивость и удобство создания потоков данных.
    • Hot Flow: Активен независимо от наличия подписчиков, подходит для представления данных, которые изменяются во времени (например, пользовательский ввод).
  • StateFlow, SharedFlow, callbackFlow, channelFlow

    • StateFlow: Хранит текущее состояние и извещает подписчиков о его изменении. Это Hot Flow. Требуется начальное значение, и он выдает его, как только коллектор начинает собирать данные. Он не выдает последовательные повторяющиеся значения. Он выдает значение только в том случае, если оно отличается от предыдущего элемента.
    • SharedFlow: Более общий Hot Flow, который может репрезентировать множество значений и имеет более гибкие настройки. Не требует начального значения, поэтому по умолчанию не выдает никаких значений. Он выдает все значения и не заботится об отличиях от предыдущего элемента. Он также выдает последовательные повторяющиеся значения.
    • callbackFlow и channelFlow: Позволяют создавать потоки на основе коллбэков или событий, удобно применять для интеграции с API, основанными на коллбэках.
  • StateIn, SharedIn

    Эти операторы используются для преобразования Cold Flow в Hot Flow (StateFlow или SharedFlow соответственно), делая поток активным и позволяя сохранять текущее состояние или делиться им сразу с несколькими подписчиками.

Android

База

  • Почему возникают лаги в Android приложении?

    • Неэффективное использование ресурсов: Неправильное управление памятью, чрезмерное использование CPU или неправильное использование сетевых запросов может замедлить приложение.
    • Проблемы с пользовательским интерфейсом: Сложные или плохо оптимизированные анимации и интерфейс могут вызывать задержки в отклике.
    • Выполнение тяжелых операций в главном потоке: Выполнение длительных операций, таких как доступ к базе данных или сетевые запросы, в главном потоке UI может привести к "зависанию" приложения.
    • Утечки памяти: Утечки памяти происходят, когда объекты не освобождаются после использования, что со временем может привести к истощению доступной памяти и замедлению приложения.
  • Что такое Context? Зачем он используется?

    Context в Android — это интерфейс, который предоставляет доступ к глобальной информации о приложении. Он используется для получения доступа к ресурсам, файловым системам, вызова активностей и служб. Существует три типа контекста: ApplicationContext, ActivityContext, BaseContext.

    1. ApplicationContext связан с жизненным циклом приложения
    2. ActivityContext связан с жизненным циклом активности.
    3. BaseContext в Android является базовым классом контекста (Context). Он используется как родительский класс для ActivityContext и ApplicationContext, предоставляя основные функции контекста, которые могут быть расширены или использованы в этих специализированных контекстах. BaseContext обеспечивает доступ к ресурсам и системным службам, таким как запуск активностей, взаимодействие с различными сервисами Android и управление ресурсами приложения. В контексте Android разработки, когда говорят о BaseContext, чаще всего имеют в виду фундаментальную функциональность контекста, на которой основаны все другие типы контекстов.

    Context необходим для:

    1. Доступа к базовым функциям приложения, таким как доступ к ресурсам, базам данных и настройкам.
    2. Запуска других компонентов, таких как активности и службы.
    3. Взаимодействия с другими приложениями.
  • Назови все основные компоненты Android приложения? (4)

    Основные компоненты Android приложения включают:

    1. Activity - компонент, который представляет один экран с пользовательским интерфейсом.
    2. Service - компонент для выполнения долгосрочных операций в фоновом режиме.
    3. Broadcast Receiver - компонент, который позволяет приложению получать и обрабатывать сообщения от других приложений или от системы. Эти сообщения могут касаться различных событий, таких как изменение состояния сети, низкий уровень заряда батареи или загрузка системы.
    4. Content Provider - это компонент, который предоставляет доступ к данным приложения другим приложениям и компонентам. Content Providers полезны для чтения и записи данных, которые должны быть доступны другим приложениям, например, контактов, календарных событий или фотографий.
  • Что такое AndroidManifest.xml?

    AndroidManifest.xml — это файл в Android приложении, который содержит важную информацию о приложении для Android системы. Он объявляет конфигурацию приложения, включая его компоненты (активности, службы, получатели широковещательных сообщений, поставщики контента), требуемые разрешения, минимальный уровень API Android, используемый приложением, и другие настройки. AndroidManifest.xml читается системой при установке приложения, и на основе этой информации система знает, как взаимодействовать с приложением.

  • Какой компонент необязательно указывать в манифесте?

    Broadcast Receiver - обычно необходимо объявлять, но существует возможность динамической регистрации (в коде, а не в манифесте) для некоторых случаев использования.

  • Что такое Application класс?

    Класс Application в Android представляет собой базовый класс приложения, который содержит глобальное состояние приложения. Он выполняется до того, как любой другой код приложения будет запущен, и именно здесь можно инициализировать глобальные ресурсы. Класс Application можно использовать для выполнения действий и настройки компонентов, которые должны быть доступны во всем приложении, например, инициализации библиотек, настройки управления сессиями или предоставления контекста для использования в других компонентах приложения.

  • Как происходит запуск Android приложения?


    Запуск в контексте системы:

    • Нажатие на иконку приложения: Когда пользователь нажимает на иконку, запускается интент запуска (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 приложения и может с ним взаимодействовать.

  • Почему view нельзя обновить не из ui потока?

    В Android обновление пользовательского интерфейса (UI) не из основного потока (также известного как UI поток) запрещено из-за модели однопоточности, которую Android использует для управления пользовательским интерфейсом.

  • Почему нельзя выполнять фоновую работу в классе Application?

    Выполнение тяжелой или блокирующей работы в классе Application может привести к замедлению запуска приложения и ухудшению впечатлений пользователя, поскольку это может заблокировать главный поток UI. Класс Application предназначен для инициализации глобального состояния приложения, а не для выполнения фоновой работы.

  • Как появляется иконка приложения на рабочем столе и что происходит при нажатии на нее?

    Иконка приложения добавляется на рабочий стол (launcher screen) когда в манифесте приложения активити определено с интент-фильтром ACTION.MAIN и категорией LAUNCHER. При нажатии на иконку запускается активити, указанное как точка входа приложения.

  • Что произойдет, если установить несколько активити как лаунчеры?

    Если установить несколько активити с интент-фильтром для лаунчера, пользователю будет предложен выбор активити для запуска при нажатии на иконку приложения. Это может быть использовано для предоставления различных точек входа в приложение

  • Если бы перед вами стояла задача залогировать самую раннюю точку старта приложения, в каком месте бы вы разместили код?

    Самая ранняя точка, где можно разместить код логирования - это метод onCreate() класса Application. Этот метод вызывается при старте приложения до любого другого компонента (Activity, Service и т.д.).

  • Можно ли обновить view из не-UI потока и с чем связано это ограничение?

    Обновлять UI-компоненты (view) напрямую из не-UI потока нельзя из-за ограничений Android на доступ к UI-элементам только из главного потока. Для обновления UI из других потоков используются механизмы синхронизации, такие как runOnUiThread() или Handler.

  • Где находится метод main() в Android приложении?

    В Android приложениях не используется метод main() в традиционном смысле, как в Java-приложениях. Вместо этого, точкой входа служит компонент Activity (или другие компоненты, такие как Service, BroadcastReceiver), который запускается системой Android, когда необходимо выполнить определенное действие (например, открыть приложение). Система Android использует манифест приложения для определения компонента, который должен быть запущен.
    Если объявлен Application, то вызывается он.

  • Почему в классе Application есть метод onCreate, но нет метода onDestroy?

    Метод onCreate() в классе Application вызывается при создании процесса приложения и служит для инициализации ресурсов, необходимых на протяжении всего существования приложения. Отсутствие onDestroy() объясняется тем, что процесс приложения уничтожается системой без каких-либо предупреждений, когда системе необходимо освободить ресурсы. Поэтому логика, требующая гарантированного выполнения перед завершением работы приложения, должна располагаться в других компонентах (например, в Activity или Service).

Activity и Fragment

  • Почему рекомендуется использовать только конструктор по умолчанию для создания Fragment?

    При восстановлении системой фрагментов (например, при пересоздании активности после изменения конфигурации) используется конструктор без параметров. Если вы создадите фрагмент с конструктором с параметрами и не сохраните эти параметры в onSaveInstanceState, вы можете потерять эти данные при пересоздании фрагмента, что приведет к ошибкам или непредвиденному поведению.

  • Что такое Activity и его жизненный цикл?

    Activity - это компонент приложения, предоставляющий экран с пользовательским интерфейсом. Жизненный цикл Activity включает методы: onCreate(), onStart(), onResume(), onPause(), onStop(), onRestart(), onDestroy().

    • onCreate() вызывается при первом создании активности. Здесь происходит инициализация.
    • onStart() вызывается, когда активность становится видимой пользователю.
    • onResume() вызывается, когда активность вступает в фокус и готова к взаимодействию с пользователем.
    • onPause() вызывается при переходе активности в состояние "пауза", когда она все еще видима, но уже не в фокусе.
    • onStop() вызывается, когда активность больше не видна пользователю.
    • onDestroy() вызывается перед тем, как активность будет уничтожена.
    • onRestart() вызывается после того, как активность была остановлена и снова была запущена пользователем.
  • В чем разница между onCreate() и onStart()?

    Основное различие между onCreate() и onStart() заключается в их роли в жизненном цикле Activity: onCreate() служит для инициализации Activity и вызывается один раз, когда Activity создается, в то время как onStart() делает Activity видимым для пользователя и может вызываться многократно каждый раз, когда Activity переходит в видимое состояние.

  • Когда вызывается только onDestroy для Activity без onPause() и onStop()?

    Это может произойти в крайне редких случаях, например, если система уничтожает процесс приложения для освобождения ресурсов в условиях нехватки памяти. В обычной ситуации onPause() и onStop() всегда вызываются перед onDestroy().

  • Почему нам нужно вызывать setContentView() в onCreate() класса Activity?

    setContentView() устанавливает макет пользовательского интерфейса для активности и должен быть вызван в onCreate(), чтобы до начала взаимодействия с пользователем активность имела загруженный и готовый интерфейс.

  • Что нельзя делать в onCreate?

    • Длительные операции, такие как сетевые запросы или обращение к базе данных, так как это может привести к "зависанию" приложения. Такие операции следует выполнять асинхронно.
    • Изменение конфигурации, не связанной с инициализацией UI, которая может быть выполнена в других методах жизненного цикла.
  • Что такое onSaveInstanceState() и onRestoreInstanceState() в Activity?

    • onSaveInstanceState() - Этот метод используется для сохранения данных перед паузой Activity.
    • onRestoreInstanceState() - Этот метод используется для восстановления сохраненного состояния Activity, когда Activity пересоздается после уничтожения. Таким образом, onRestoreInstanceState() получает пакет, который содержит информацию о состоянии экземпляра.
  • Какие launch modes существуют для активити?

    • standard: Запускает активити в новом экземпляре каждый раз.
    • singleTop: Если экземпляр активити уже находится на вершине стека, он не создается заново, и вместо этого получает интент.
    • singleTask: Создает новый задачный стек и запускает активити в новом экземпляре в этом стеке, но если активити уже существует в любом стеке, система очищает стек до этого активити и отправляет интент.
    • singleInstance: Активити запускается в новом задачном стеке, но с гарантией того, что никакие другие активити не будут запущены в этом стеке.
  • Что такое Fragment и его жизненный цикл?

    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 launchMode в Android?

    launchMode определяет, как активность будет вставлена в стек задач. singleTask – это режим запуска, при котором в стеке задач может быть только один экземпляр активности. Если активность уже находится в стеке, система очищает все активности, находящиеся над ней, и передает интент этой активности через onNewIntent().

  • В чем разница между Fragment и Activity? Объясните взаимосвязь между ними.

    Activity действует как контейнер для пользовательского интерфейса и управляет взаимодействием пользователя с экраном. Fragment, с другой стороны, представляет собой повторно используемую часть пользовательского интерфейса с собственным жизненным циклом, которая может быть встроена в активность. Фрагменты добавляют гибкость в проектирование интерфейса, позволяя активности включать несколько фрагментов на одном экране и переиспользовать фрагмент в разных активностях.

  • Когда следует использовать Fragment вместо Activity?

    • Когда у вас есть некоторые компоненты пользовательского интерфейса, которые должны использоваться в различных Activity
    • Когда несколько представлений могут быть отображены бок о бок, как например ViewPager
  • В чем разница между FragmentPagerAdapter и FragmentStatePagerAdapter?

    • FragmentPagerAdapter: Каждый Fragment, посещенный пользователем, будет храниться в памяти, но представление будет уничтожено. Когда страница снова посещается, то создается представление, а не экземпляр фрагмента.
    • FragmentStatePagerAdapter: Здесь экземпляр фрагмента будет уничтожен, когда он не виден пользователю, за исключением сохраненного состояния фрагмента.
  • В чем разница между добавлением/заменой фрагмента в backstack?

    При добавлении фрагмента, он размещается поверх предыдущего, не удаляя его, в то время как замена удаляет текущий фрагмент и добавляет новый. Использование addToBackStack() позволяет возвращаться к предыдущему фрагменту с помощью кнопки "Назад".

  • Как осуществляется коммуникация между двумя Fragments?

    • Через ViewModel
    • Через Activity
  • Что такое retained Fragment?

    По умолчанию, Fragments уничтожаются и пересоздаются вместе с их родительскими Activities при изменении конфигурации. Вызов setRetainInstance(true) позволяет нам обойти этот цикл уничтожения и пересоздания, сигнализируя системе о сохранении текущего экземпляра фрагмента, когда Activity пересоздается.

  • Какова цель addToBackStack() при выполнении транзакции фрагмента?

    При вызове addToBackStack(), транзакция замены сохраняется в стеке возврата, так что пользователь может отменить транзакцию и вернуть предыдущий фрагмент, нажав кнопку Назад.

Views и ViewGroups

  • Что такое View в Android?

    View объекты являются основными строительными блоками элементов пользовательского интерфейса (UI) в Android. View это простая прямоугольная коробка, которая реагирует на действия пользователя. Примеры включают EditText, Button, CheckBox и т. д. View относится к классу android.view.View, который является базовым классом всех классов UI.
  • Процесс отрисовки View

  1. Measure (Измерение): На этом этапе система определяет размеры View. Родительский элемент спрашивает каждого из своих дочерних элементов, сколько места они нуждаются для отображения. Это делается через вызов метода measure(int, int). Дочерние элементы должны затем самостоятельно вычислить и указать свои размеры, вызывая метод setMeasuredDimension(int, int). Этот процесс рекурсивно повторяется для всех элементов иерархии View.
  2. Layout (Размещение): После того как были измерены размеры всех View, начинается этап размещения. На этом этапе определяется, где именно в родительском элементе будет находиться каждый дочерний элемент. Это делается путем вызова метода layout(int, int, int, int), который устанавливает фактические положение и размер каждого View. Координаты и размеры задаются относительно родительского элемента.
  3. Draw (Отрисовка): На последнем этапе система отрисовывает View на экране. Это включает в себя отрисовку фона, содержимого (текст, изображения и т.д.) и любых анимаций или декораций. Отрисовка происходит в методе draw(Canvas), который вызывает такие методы, как onDraw(Canvas), dispatchDraw(Canvas) для дочерних элементов и onDrawForeground(Canvas) для отрисовки элементов переднего плана (например, прокрутки или фокуса).
  • Жизненный цикл View:

    view lifecycle

  1. onAttachedToWindow(): Этот метод вызывается, когда View было прикреплено к окну. Это первый шаг, когда View становится видимым в приложении. Здесь можно инициализировать анимации или начать мониторить изменения в системе. Это также сигнализирует, что View теперь может взаимодействовать с пользователем.

  2. measure(): Это внутренний метод системы Android, который запускает процесс измерения View путём вызова onMeasure(). Этот метод не предназначен для переопределения, но он важен, так как инициирует определение размера View.

  3. onMeasure(int, int): В onMeasure(), View определяет, какого размера оно должно быть, исходя из предложений (specifications) родительского элемента. View должно вызвать setMeasuredDimension(int width, int height) для сохранения измеренных размеров. Это переопределяемый метод, который вы можете использовать, чтобы управлять размером вашего View.

  4. layout(): Это другой внутренний метод системы, который вызывается после завершения процесса измерения. Он отвечает за расположение View в родительском контейнере. Этот метод также не предназначен для переопределения.

  5. onLayout(boolean, int, int, int, int): После метода layout(), onLayout() вызывается для того, чтобы View расположило себя и все свои дочерние элементы. В случае с ViewGroup, здесь определяется местоположение дочерних View. Это переопределяемый метод, где вы устанавливаете, где должны находиться дочерние элементы внутри ViewGroup.

  6. dispatchDraw(Canvas): dispatchDraw используется в ViewGroup для рисования дочерних View. Этот метод вызывается системой для рисования содержимого, и обычно его не нужно переопределять, но вы можете это сделать, чтобы управлять тем, как ваши дочерние View рисуются.

  7. draw(Canvas): Это внутренний метод, который управляет всем процессом рисования, включая вызов onDraw(), рисование фона, дочерних представлений, скроллирования и т. д. Этот метод обычно не переопределяется, так как он обрабатывает несколько задач рисования и делегирует основную работу onDraw().

  8. onDraw(Canvas): onDraw() — это где вы определяете, как View должно отображать свое содержимое. Это может включать рисование форм, текста, изображений или других графических элементов. Этот метод переопределяется, когда вы создаете пользовательский View и хотите контролировать его визуальное представление.

  • В чем разница между методами invalidateLayout и requestLayout?

    • invalidate(): Вызывается, когда view должно быть перерисовано, но его размер и позиция не изменяются.
    • requestLayout(): Вызывается, когда view должно быть измерено и размещено заново, что может привести к изменению размеров и позиции как этого view, так и его потомков.
  • Разница между View.GONE и View.INVISIBLE?

    • View.GONE - этот параметр делает представление полностью невидимым и не занимает место в макете.
    • View.INVISIBLE - представление остается невидимым, но все еще занимает место в макете.
  • Можете ли вы создать пользовательский View? Как?

    Да, можно создать пользовательский View в Android. Это делается путем наследования от класса View или одного из его подклассов и переопределения методов, таких как onDraw(), для рисования контента вида или onMeasure() для определения размера вида.

  • Что такое ViewGroup и чем они отличаются от View?

    • View - объекты View являются основными строительными блоками элементов пользовательского интерфейса (UI) в Android. View это простая прямоугольная коробка, которая реагирует на действия пользователя. Примеры включают EditText, Button, CheckBox и т. д. View относится к классу android.view.View, который является базовым классом всех классов UI.
    • ViewGroup - ViewGroup это невидимый контейнер. Он содержит Views и ViewGroups. Например, LinearLayout это ViewGroup, который содержит Button(View), и другие компоновки также. ViewGroup является базовым классом для компоновок.
  • Что такое Canvas?

    Canvas — это одна из основных функций встроенного в Android фреймворка графического интерфейса, которая позволяет создавать разнообразные пользовательские интерфейсы и реализовывать сложную графику. С помощью этого инструмента разработчики могут реализовывать различные возможности, такие как рисование, анимация и обработка событий.

  • Что такое SurfaceView?

    SurfaceView — это подкласс View, который предоставляет отдельный слой, на котором можно рисовать, не блокируя основной поток пользовательского интерфейса. Это полезно для рендеринга анимаций или видео, где требуется высокая производительность и меньшее взаимодействие с основным потоком UI.

  • Relative Layout против Linear Layout.

    • Relative Layout позволяет размещать виджеты относительно друг друга или относительно родительского контейнера.
    • Linear Layout размещает виджеты в линейной последовательности, горизонтально или вертикально.
  • Расскажите о Constraint Layout

    Constraint Layout — это гибкий и мощный макет в Android, который позволяет создавать сложные пользовательские интерфейсы без вложенных макетов. Он предоставляет широкие возможности для определения ограничений для позиционирования и размера виджетов, что облегчает создание адаптивных интерфейсов.

  • Знаете ли вы, что такое дерево View? Как можно оптимизировать его глубину? (ViewTreeObserver)

    Дерево из View — это иерархическая структура, которая представляет все виджеты (views) и макеты (layouts), составляющие пользовательский интерфейс в Android приложении. Оптимизировать глубину дерева View можно путем уменьшения количества вложенных макетов, использования ConstraintLayout для упрощения иерархии и избегания лишних макетов, которые не вносят вклад в пользовательский интерфейс. ViewTreeObserver может использоваться для прослушивания различных событий в дереве View, таких, как момент перед отрисовкой, что может быть полезно для дополнительных оптимизаций.

Отображение списков контента

  • Чем отличается ListView от RecyclerView?

    • ListView — это виджет для отображения списка данных в вертикальном порядке, который был частью Android с его первых версий. Он поддерживает базовую прокрутку и может отображать список, который подгружается в адаптер. Однако он имеет ограниченные возможности по настройке и не поддерживает разные типы элементов списка или сложные анимации.

    • RecyclerView — это более продвинутый и гибкий компонент для отображения списков данных, введенный в Android Lollipop (API уровня 21). Он поддерживает как вертикальную, так и горизонтальную прокрутку, разные типы элементов, сложные анимации, а также имеет более эффективное переиспользование элементов списка через механизм ViewHolder. RecyclerView также позволяет легко реализовать разные виды лэйаутов, такие как список, сетка и стаггеред грид.

  • Как внутренне работает RecyclerView?

    RecyclerView работает на основе адаптера, который управляет данными списка и связывает эти данные с видами, отображаемыми в RecyclerView. Ключевым компонентом является паттерн ViewHolder, который хранит ссылки на все подвиды каждого элемента списка для быстрого доступа без необходимости повторного поиска. При прокрутке списка RecyclerView переиспользует виды, которые вышли за пределы экрана, применяя к ним новые данные, что значительно повышает производительность и эффективность использования памяти.

  • Оптимизация RecyclerView

    • Использование метода setHasFixedSize(true), если размер RecyclerView не изменяется, что позволяет оптимизировать производительность.
    • Эффективное использование ViewHolder для минимизации вызовов findViewById внутри адаптера.
    • Избегание сложных операций в методе onBindViewHolder адаптера, таких как загрузка изображений без кэширования. Вместо этого рекомендуется использовать библиотеки для асинхронной загрузки изображений, например, Glide.
    • Оптимизация макетов элементов списка, избегая лишней вложенности.
  • Оптимизация вложенного RecyclerView

    • Убедитесь, что внешний RecyclerView и вложенные RecyclerView используют setHasFixedSize(true), если их размеры не изменяются.
    • Используйте пулинг вьюхолдеров с помощью setRecycledViewPool(RecyclerView.RecycledViewPool) для переиспользования элементов в разных RecyclerView.
    • Минимизируйте количество вызовов notifyDataSetChanged() и используйте более конкретные методы уведомления об изменениях, такие как notifyItemChanged(int), для улучшения производительности.
  • Что такое SnapHelper?

    SnapHelper — это класс помощник, предназначенный для вспомогательной привязки (snapping) элементов RecyclerView к определенной точке на экране, обычно в центре или краях экрана, когда прокрутка останавливается. Это удобно, например, для создания галерей или каруселей, где требуется, чтобы элементы выравнивались относительно определенных точек.

  • Что такое DiffUtils?

    DiffUtil — это утилита, предоставляемая Android SDK, которая помогает вычислить разницу между двумя списками и выдать набор операций обновления, необходимых для преобразования одного списка в другой. Это может использоваться в адаптере RecyclerView для минимизации количества обновлений и повышения производительности, особенно когда изменения в данных минимальны. DiffUtil вычисляет минимальное количество изменений и автоматически обновляет список с анимациями, делая пользовательский интерфейс более плавным и отзывчивым.

Диалоги и Toast

  • Что такое Dialog?

    Dialog — это небольшое окно, которое появляется перед пользователем для выполнения действия или для отображения информации, не перекрывая полностью основной контент. Диалоги могут быть использованы для сбора пользовательского ввода через формы, отображения сообщений, выбора дат и других интерактивных элементов.

  • Что такое Toast?

    Toast — это маленькое всплывающее сообщение, которое отображается на короткое время и автоматически исчезает после истечения времени отображения. Toast обычно используется для отображения неинтерактивных сообщений, информирующих пользователя о каком-либо процессе (например, "Сообщение отправлено").

  • В чем разница между Dialog и DialogFragment?

    Основная разница между Dialog и DialogFragment заключается в управлении жизненным циклом и встраивании в архитектуру приложения:

    • Dialog — это базовый класс для диалогов, который может быть использован напрямую, но его управление в контексте жизненного цикла активности может быть сложным.
    • DialogFragment — это фрагмент, который предоставляет API для отображения диалога. Использование DialogFragment рекомендуется, так как он лучше интегрируется в жизненный цикл активности или фрагмента, позволяя управлять диалогом так же, как и другими компонентами Android.

Intents и Broadcasts

  • Что такое Intent?

    Intent — это абстрактное описание операции, которое должно быть выполнено. В Android Intent используется для запуска активностей, сервисов и для передачи сообщений между компонентами приложения или разными приложениями.

  • Что такое неявный Intent (Implicit Intent)?

    Неявный Intent не указывает прямо на компонент, который должен обработать действие. Вместо этого он определяет общее действие, которое может быть выполнено любым приложением или компонентом, способным его обработать. Примером может служить намерение открыть веб-страницу, где не указан конкретный браузер, но задано действие ACTION_VIEW.

  • Что такое явный Intent (Explicit Intent)?

    Явный Intent указывает непосредственно на компонент, который должен обработать действие, включая имя пакета или полное имя класса компонента. Явные Intent обычно используются для запуска компонентов внутри того же приложения.

  • Что такое BroadcastReceiver?

    BroadcastReceiver — это компонент приложения, который позволяет реагировать на широковещательные сообщения от других приложений или от самой системы. Эти сообщения могут касаться различных событий, таких как изменение заряда батареи, получение SMS или изменение сетевого состояния.

  • Что такое липкий Intent (Sticky Intent)?

    Липкий Intent (Sticky Intent) — это широковещательное сообщение, которое после отправки "застревает" в системе, позволяя новым подписчикам на BroadcastReceiver получить данные события даже после его завершения. Примером использования может быть запрос текущего состояния батареи. Следовательно, вы можете использовать это для определения состояния батареи без обязательной регистрации всех будущих изменений состояния батареи.

  • Опишите, как работают Broadcasts и intents, чтобы передавать сообщения по вашему приложению?

    Broadcasts и intents позволяют компонентам приложения общаться между собой без необходимости напрямую связывать их код. Intents могут быть использованы для явного запуска компонентов внутри приложения или для неявного запроса действий от других приложений. Broadcasts позволяют отправлять широковещательные сообщения системе или приложениям, на которые подписаны BroadcastReceivers, реагирующие на определенные события или интенты.

  • Что такое PendingIntent?

    PendingIntent — это токен, который вы передаете внешнему приложению (например, уведомлению, виджету), который позволяет этому приложению использовать разрешение вашего приложения для выполнения предопределенной операции. В основном, PendingIntent используется для уведомлений, чтобы открыть активность или выполнить сервис при нажатии на уведомление.

  • Какие существуют различные типы Broadcasts?

Services

  • Что такое Service?

    Service – это компонент приложения, который может выполнять длительные операции в фоновом режиме и не предоставляет пользовательского интерфейса. Другими словами, это способ выполнить некоторые действия в фоне, например, проигрывание музыки, загрузку файлов и т. д., без взаимодействия с пользовательским интерфейсом. Сервисы могут быть как связанными (bound), так и несвязанными. Связанный сервис предлагает клиент-серверный интерфейс, который позволяет компонентам (например, активности) взаимодействовать с сервисом, отправлять запросы, получать результаты через IPC (Межпроцессное взаимодействие). Несвязанный сервис обычно выполняется на заднем плане и не связан с каким-либо пользовательским интерфейсом или компонентом, который его запустил.

  • Service vs IntentService

  • Что такое сервис переднего плана (Foreground Service)?

    Сервис переднего плана (Foreground Service) – это сервис, который выполняется на переднем плане и обязательно показывает уведомление в строке состояния. Это уведомление информирует пользователя о том, что приложение выполняет некоторую задачу, которая требует внимания пользователя. Сервисы переднего плана обычно используются для задач, которые непосредственно влияют на работу пользователя с устройством, например, для проигрывания аудио или для выполнения активной задачи, такой как навигация. Использование сервиса переднего плана помогает обеспечить, что система не остановит ваше приложение, пока выполняется важная задача.

  • Что такое JobScheduler?

    JobScheduler – это системный сервис, доступный начиная с Android 5.0 (API уровень 21), который позволяет разработчикам планировать различные виды задач (работы) для выполнения в определенных условиях. С помощью JobScheduler можно управлять выполнением задач в зависимости от таких условий, как состояние сети, заряд батареи и другие. Это позволяет приложениям выполнять фоновые задачи более эффективно, экономя заряд батареи и ресурсы системы. Работы, запланированные с помощью JobScheduler, могут выполняться даже после перезагрузки устройства.

Межпроцессорное взаимодействие (IPC)

  • Как могут взаимодействовать два разных приложения Android?

    • Интенты (Intents): Интенты позволяют приложениям запрашивать функциональность от других приложений. Например, приложение может использовать интент для запуска камеры или выбора контакта из списка контактов.
    • Сервисы (Services): Приложение может использовать сервисы другого приложения для выполнения фоновых задач без взаимодействия с пользовательским интерфейсом.
    • Content Providers: Позволяют делиться данными между приложениями. Например, приложение может обращаться к базе данных контактов через ContentProvider.
    • Межпроцессное взаимодействие (IPC): С использованием AIDL (Android Interface Definition Language) приложения могут обмениваться данными и объектами.
  • Возможно ли запускать приложение Android в нескольких процессах? Как?

    Да, приложение Android может работать в нескольких процессах. Это можно настроить в манифесте приложения, указав атрибут android:process в теге компонента (<activity>, <service>, <receiver>, <provider>). Каждый компонент может быть настроен на запуск в отдельном процессе, что позволяет приложению выполнять различные задачи в разных процессах одновременно.

  • Что такое AIDL? Перечислите шаги по созданию ограниченного сервиса через AIDL.

    AIDL (Android Interface Definition Language) используется для создания интерфейсов, которые позволяют клиентам взаимодействовать с сервисом через межпроцессное взаимодействие (IPC). Для создания ограниченного сервиса через AIDL необходимо выполнить следующие шаги:

    1. Определение интерфейса AIDL: Создайте файл AIDL с описанием методов, которые должны быть доступны для клиентов.
    2. Реализация интерфейса: Создайте класс, который реализует интерфейс, определенный в AIDL. Этот класс будет обрабатывать вызовы от клиентов.
    3. Регистрация сервиса в манифесте: Укажите сервис в AndroidManifest.xml, добавив соответствующий тег .
    4. Привязка к сервису: Клиенты могут привязаться к сервису, используя bindService(), чтобы начать взаимодействие с ним.
  • Что можно использовать для фоновой обработки в Android?

    • Сервисы (Services): Для выполнения длительных задач на фоне.
    • JobScheduler: Для планирования задач, которые должны выполняться при определенных условиях.
    • WorkManager: Для гарантированного выполнения фоновых задач и работы с ограничениями, накладываемыми системой на фоновую работу.
    • IntentService: Для обработки асинхронных запросов через интенты на фоне.
  • Что такое ContentProvider и для чего он обычно используется?

    ContentProvider – это компонент приложения, который позволяет делиться данными между разными приложениями. Он предоставляет структурированный интерфейс к данным, хранящимся в файловой системе, базе данных SQLite, вебе или любом другом постоянном хранилище данных, которым управляет приложение. ContentProvider чаще всего используется для доступа к данным в разных приложениях, например, для доступа к контактам, календарю и другим личным данным пользователя.

Длительные операции

  • Как выполнить параллельные задачи и получить обратный вызов, когда все они завершены?

    • Future и ExecutorService: Создайте пул потоков с помощью ExecutorService и запустите задачи в виде объектов Callable или Runnable. Используйте Future для отслеживания состояния выполнения каждой задачи. После завершения всех задач можно выполнить обратный вызов.

    • CountDownLatch: Этот инструмент позволяет одному или нескольким потокам ожидать завершения определенного количества операций в других потоках. Инициализируйте CountDownLatch с числом параллельных задач и уменьшайте счетчик при завершении каждой задачи. Когда счетчик достигнет нуля, можно выполнить обратный вызов.

    • RxJava: Библиотека для реактивного программирования, которая позволяет эффективно управлять асинхронными задачами и обработкой событий. С помощью операторов zip или merge можно объединить результаты нескольких асинхронных операций и получить уведомление при их завершении.

    • Kotlin Coroutines: В Kotlin для параллельного выполнения задач и получения результатов можно использовать корутины с применением функций async и await внутри CoroutineScope. Это позволяет писать асинхронный код, который выглядит как синхронный, и легко получать результаты выполнения задач.

  • Что такое ANR? Как можно предотвратить ANR?

    ANR (Application Not Responding) – это ошибка, которая происходит, когда приложение не отвечает на ввод пользователя в течение 5 секунд. Чаще всего ANR возникает, если приложение выполняет длительные операции в главном потоке (UI thread).

    Предотвращение ANR:

    • Использование фоновых потоков: Выполняйте длительные операции (например, сетевые запросы, обращение к базе данных) в фоновых потоках, не блокируя главный поток.
    • AsyncTask (до API уровня 30): AsyncTask позволял выполнять асинхронные операции на фоне, но был объявлен устаревшим. Вместо него рекомендуется использовать другие механизмы асинхронной работы.
    • Использование WorkManager, JobScheduler: Эти компоненты предназначены для выполнения фоновых задач, учитывая ограничения системы и оптимизируя использование ресурсов.
  • Что такое AsyncTask (Устарел в API уровня 30)?

    AsyncTask позволял выполнять асинхронные операции в фоне и публиковать результаты на главном потоке без необходимости напрямую управлять потоками. Однако начиная с API уровня 30, AsyncTask был объявлен устаревшим из-за ряда недостатков и ограничений.

  • Какие проблемы у AsyncTask?

    • Утечки памяти: AsyncTask может удерживать ссылку на контекст активности, что может привести к утечке памяти, если активность будет уничтожена до завершения задачи.
    • Сложность управления жизненным циклом: AsyncTask не связан с жизненным циклом компонентов UI, что затрудняет управление ресурсами и обновление UI по завершении задачи.
    • Ограничения на параллельное выполнение: По умолчанию задачи, выполняемые с помощью AsyncTask, запускались последовательно, что могло замедлять выполнение параллельных операций.
  • Объясните Looper, Handler и HandlerThread.

    • Looper управляет очередью сообщений (Message Queue) для потока. Он зацикливает поток, ожидая входящие задачи (сообщения или выполнимые объекты) и распределяет их для обработки.
    • Handler связывает Looper с кодом, позволяя отправлять и обрабатывать сообщения и выполнимые объекты в очереди Looper. Handler может использоваться для обновления пользовательского интерфейса из фонового потока.
    • HandlerThread – это вспомогательный класс, создающий поток с собственным Looper, что делает его подходящим для фоновой обработки задач, требующих последовательного выполнения.
  • Утечка памяти в Android и сборка мусора

    Утечка памяти в Android происходит, когда объекты, которые больше не нужны приложению, по-прежнему удерживаются в памяти из-за ссылок, не позволяя сборщику мусора очистить их. Это может привести к уменьшению доступной памяти и в конечном итоге к завершению работы приложения.

    • Избегайте длительных ссылок на контексты: Используйте контекст приложения, когда это возможно, и избегайте удержания активностей и служб в статических переменных.
    • Используйте WeakReference: Когда необходимо удерживать ссылки на объекты, которые могут быть уничтожены, используйте WeakReference.
    • Отписывайтесь от слушателей и отменяйте задачи: При уничтожении активности или фрагмента отписывайтесь от слушателей событий и отменяйте фоновые задачи, чтобы избежать удержания ссылок на уничтоженные объекты.

Асинхронность

  • Как выполнять асинхронные операции в Android?

    Можно использовать несколько подходов, включая AsyncTask, Loader, IntentService, HandlerThread. С появлением Kotlin в Android разработке стали популярны корутины для управления асинхронными задачами благодаря их эффективности и простоте использования.
  • Почему в Android возникла необходимость асинхронности? Почему мы не можем выполнять все в одном потоке?

    Основной UI поток (Main Thread) отвечает за обработку пользовательского интерфейса, включая отрисовку элементов на экране и обработку пользовательских взаимодействий. Выполнение длительных операций в главном потоке приводит к "зависанию" приложения и плохому пользовательскому опыту. Асинхронность позволяет выполнять тяжелые задачи в фоновых потоках, не блокируя UI поток.
  • Какие подходы существуют в Android для обеспечения асинхронности?

    • AsyncTask и Loader: Предоставляют фреймворки для выполнения фоновых задач с возможностью обновления UI по завершении.
    • IntentService: Позволяет выполнить операции в фоновом сервисе.
    • Handler и Looper: Управление потоками с помощью сообщений и выполнение кода в других потоках.
    • Kotlin Coroutines: Современный и эффективный способ для асинхронного программирования, позволяет выполнять асинхронные задачи с минимальными накладными расходами и упрощенным кодом.
    • RxJava: Библиотека для композиции асинхронного и событийно-ориентированного программирования с использованием наблюдаемых последовательностей.

Работа с мультимедиа контентом

  • Как вы обрабатываете изображения в формате bitmap в Android, так как они занимают слишком много памяти?

  • Расскажите о пуле bitmap.

Сохранение данных

  • Jetpack DataStore Preferences

  • Как сохранить данные в приложении Android?

    • SharedPreferences: Легковесный механизм для хранения пар ключ-значение.
    • Внутренняя память: Сохранение данных в файловой системе приложения.
    • Внешняя память: Использование общедоступного или частного пространства на внешнем носителе.
    • SQLite: Встраиваемая база данных для хранения структурированных данных.
    • Room: Абстракция над SQLite, упрощающая работу с базой данных и интеграцию с LiveData, Flow для реактивного доступа к данным.
  • Что такое SharedPreferences?

    Это механизм для хранения и извлечения примитивных данных в виде пар ключ-значение. Используется для сохранения пользовательских настроек или другой небольшой информации, требующей быстрого доступа.

  • Как выглядят данные, сохраненные в SharedPreferences, в пакете приложения?

    Данные, сохраненные в SharedPreferences, хранятся в виде XML-файлов в каталоге /data/data/<package_name>/shared_prefs/. Каждый файл SharedPreferences представляет собой набор пар ключ-значение.

  • Как сохранить наш класс на диск?

    • Serializable является стандартным Java интерфейсом и проще в использовании, но медленнее из-за использования рефлексии.
    • Parcelable специфичен для Android и требует реализации некоторых методов, но он намного быстрее, что делает его предпочтительным для передачи данных между компонентами Android.
    • Json. Использовать одну из любых библиотек по типу gson, moshi, kotlin serialization, jackson и т.д.
  • Почему Parcelable быстрее и легче, чем Serializable?

    • Эффективность: Parcelable разработан с учетом эффективности использования памяти и скорости сериализации/десериализации, что критически важно для производительности мобильных приложений.
    • Контроль: Разработчик имеет полный контроль над процессом сериализации с Parcelable, что позволяет оптимизировать процесс. Например, можно исключить из сериализации ненужные поля.
    • Реализация: Parcelable требует явной реализации методов сериализации, что позволяет избежать использования рефлексии и снижает накладные расходы, связанные с Serializable.
    • В целом, использование Parcelable рекомендуется для передачи данных между компонентами Android, особенно если требуется высокая производительность.
  • Что такое ORM? Как это работает?

  • Как сохранить состояние Activity во время поворота экрана?

  • Какие разные способы хранения данных в вашем приложении Android?

  • Объясните Scoped Storage в Android.

  • Как зашифровать данные в Android?

  • Что такое commit() и apply() в SharedPreferences?

Look and Feel

  • Что такое Spannable?

  • Что такое SpannableString?

  • Какие лучшие практики использования текста в Android?

  • Как реализовать темную тему в любом приложении?

Оптимизация памяти

  • Что такое метод onTrimMemory()?

  • Как идентифицировать и устранить проблемы с OutOfMemory?

  • Как найти утечки памяти в приложениях Android?

Оптимизация времени работы батареи

  • Как снизить потребление батареи в приложении Android?

  • Что такое Doze? Что насчет App Standby?

    • Doze — это режим энергосбережения, добавленный в Android 6.0 (Marshmallow), который снижает потребление батареи, ограничивая фоновую активность приложений, когда устройство не используется в течение длительного времени.
    • App Standby — это функция, также введенная в Android 6.0 (Marshmallow), которая ограничивает фоновую активность приложений, если пользователь не активно использует эти приложения.
  • Что такое перерисовка?

    Перерисовка (или re-draw) — это процесс, при котором элементы интерфейса пользователя перерисовываются на экране. Это может происходить при изменении данных, которые отображаются в элементе пользовательского интерфейса, или при изменении состояния видимости элемента. Частые перерисовки могут негативно сказаться на производительности и потреблении батареи.

Поддержка разных экранов

  • Как вы поддерживаете различные типы разрешений экранов?

Разрешения

  • Какие разные уровни защиты в разрешениях?

Native программирование

  • Что такое NDK и почему он полезен?

  • Что такое renderscript?

Внутренности Системы Android

  • Что такое Android Runtime?

    Android Runtime (ART) — это среда выполнения приложений для операционной системы Android, которая заменила Dalvik в качестве основной среды выполнения с Android версии 5.0 (Lollipop). ART оптимизирует приложения при их установке, предварительно компилируя их в машинный код, что улучшает производительность и эффективность работы приложений по сравнению с предыдущей системой Dalvik, которая компилировала код во время выполнения приложения (Just-In-Time, JIT компиляция).

  • Dalvik, ART, JIT и AOT в Android

    • 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 и ART заключается в методе компиляции. Dalvik использовал JIT компиляцию, компилируя код во время его выполнения, что могло замедлять запуск приложений. ART использует AOT компиляцию, компилируя приложения заранее при их установке, что ускоряет их запуск и улучшает производительность. ART также обеспечивает лучшую управляемость памятью и более эффективное выполнение приложений.

  • Что такое DEX?

    DEX (Dalvik Executable) — это формат исполняемых файлов, используемых в Android. Это упакованный файл, содержащий скомпилированный код, который может быть исполнен виртуальной машиной Dalvik или ART. Файлы DEX оптимизированы для минимизации потребления памяти и улучшения производительности на устройствах Android.

  • Что такое Multidex в Android?

    Multidex — это решение в Android для обхода ограничения Dalvik на максимальное количество методов, которое может быть в одном файле DEX — 65,536. При использовании Multidex приложение может содержать несколько файлов DEX, что позволяет включать больше методов, чем допускает одиночный DEX файл. Это особенно полезно для крупных приложений с большим количеством библиотек.

  • Можно ли вручную вызвать сборщик мусора?

    Да, в Android можно вручную вызвать сборщик мусора с помощью метода System.gc(). Однако следует отметить, что вызов сборщика мусора не гарантирует немедленной очистки памяти. Виртуальная машина Java (JVM) сама решает, когда именно выполнять сборку мусора. Этот метод может использоваться для предложения виртуальной машине выполнить сборку мусора, но без гарантии, что это произойдет немедленно.

Android Jetpack

  • Что такое Android Jetpack и зачем его использовать?

    Android Jetpack – это набор библиотек, инструментов и архитектурных руководств, предназначенный для упрощения разработки приложений. Он предоставляет расширенные компоненты архитектуры, которые помогают построить качественное, тестируемое и модульное приложение. Jetpack ускоряет разработку приложений, помогая автоматизировать повторяющиеся задачи, такие как работа с базой данных, навигация, управление жизненным циклом компонентов и многое другое. Использование Jetpack способствует следованию лучшим практикам, улучшает поддержку различных устройств и версий Android, а также облегчает написание кода, который хорошо работает с основными функциями Android, такими как отложенные задачи и поддержка различных экранов.

  • Что такое ViewModel и в чем его польза?

    ViewModel – это компонент архитектуры Android, который предназначен для хранения и управления данными, связанными с пользовательским интерфейсом, прозрачно для изменений конфигурации, таких как поворот экрана. Это позволяет данным пережить пересоздание активности или фрагмента, обеспечивая более эффективное и правильное управление данными UI. ViewModel помогает уменьшить количество кода в активности или фрагменте, связанного с управлением данными, делая архитектуру приложения более чистой, тестируемой и модульной.

  • Что такое компоненты архитектуры Android?

    Компоненты архитектуры Android – это набор библиотек, который помогает разработчикам строить стабильные, тестируемые и поддерживаемые приложения. Включает в себя такие компоненты как LiveData, ViewModel, Room (для работы с базами данных), Paging (для постраничной загрузки данных), WorkManager (для фоновых задач) и многие другие. Эти компоненты предназначены для работы вместе, обеспечивая эффективную и оптимальную разработку приложений.

  • Что такое LiveData в Android?

    LiveData – это обсервабл-класс данных, который следит за изменениями и уведомляет подписчиков (например, активности или фрагменты) о данных, если эти подписчики находятся в активном состоянии жизненного цикла. Это обеспечивает автоматическое и безопасное управление данными UI, так как данные автоматически обновляются в соответствии с текущим состоянием жизненного цикла компонента.

  • Чем LiveData отличается от ObservableField?

    LiveData учитывает жизненный цикл компонентов, автоматически останавливая и возобновляя наблюдение в зависимости от их состояния, что обеспечивает предотвращение утечек памяти и несанкционированного доступа к компонентам UI. ObservableField является частью библиотеки Data Binding и не учитывает жизненный цикл компонентов, что может привести к утечкам памяти, если не управлять подписками вручную.

  • В чем разница между setValue и postValue в LiveData?

    setValue() вызывается в главном потоке и немедленно изменяет данные в LiveData, уведомляя подписчиков. postValue() предназначен для использования из фонового потока и ставит данные в очередь на обновление в главном потоке, что может быть не немедленно.

  • Как поделиться ViewModel между фрагментами в Android?

    Для общего использования ViewModel между фрагментами можно использовать ViewModelProvider с активностью в качестве области видимости. Это позволяет фрагментам получать экземпляр ViewModel, связанный с их активностью, обеспечивая обмен данными между фрагментами через общую ViewModel.

  • Объясните WorkManager и его случаи использования.

    WorkManager – это компонент архитектуры Android, который позволяет гибко управлять фоновыми задачами, учитывая ограничения устройства и системы, такие как состояние заряда батареи или подключение к сети. WorkManager идеально подходит для задач, требующих гарантированного выполнения, например, синхронизация данных, резервное копирование, обработка изображений и т.д., даже если приложение закрыто или устройство перезагружается.

  • Как внутренне работает ViewModel?

    ViewModel работает, сохраняя данные в специально отведенном хранилище, которое не уничтожается при изменении конфигурации, таких как поворот экрана. При изменении конфигурации активности или фрагмента, система автоматически пересоздает UI-компоненты, но ViewModel сохраняет свое состояние и данные, делая их немедленно доступными для нового экземпляра UI. Это достигается за счет хранения ViewModel в специализированном хранилище, которое привязано к ViewModelStoreOwner (например, активности или фрагменту), и не зависит от жизненного цикла отдельных компонентов UI.

Прочее

  • Почему класс Bundle используется для передачи данных и почему мы не можем использовать простую структуру данных Map?

    Bundle — это специализированная структура данных, предназначенная для передачи данных между компонентами приложения (например, между активностями). Основные причины использования Bundle вместо Map:

    1. Сериализация: Bundle может легко сериализоваться и десериализоваться системой Android, что важно для передачи данных между процессами.
    2. Типизированные методы доступа: Bundle предоставляет типизированные методы для доступа к данным, что упрощает извлечение и хранение данных без явного приведения типов.
    3. Интеграция с Android: Bundle тесно интегрирован с Android Framework, особенно с компонентами, такими как Intents и Fragments, что облегчает работу с данными в различных частях приложения.
  • Как вы устраняете сбои приложения?

    1. Логирование и анализ: Используйте инструменты логирования (например, Logcat) и аналитики (например, Firebase Crashlytics) для выявления и анализа сбоев.
    2. Отладка: Применяйте отладчик для пошагового выполнения кода и выявления источника проблемы.
    3. Тестирование: Разрабатывайте и запускайте тесты (юнит-тесты, интеграционные, UI-тесты), чтобы проверить исправление и обнаружить другие потенциальные проблемы.
    4. Обновление зависимостей: Убедитесь, что все используемые библиотеки и SDK обновлены до последних версий.
    5. Пользовательская обратная связь: Отслеживайте отзывы пользователей для выявления сбоев, которые не были обнаружены во время тестирования.
  • Объясните как работает система уведомлений Android.

  • В чем разница между Serializable и Parcelable? Какой подход лучше в Android?

    • Serializable: Стандартный механизм Java для сериализации объектов. Прост в использовании, но менее эффективен, так как использует рефлексию и создает много временных объектов.
    • Parcelable: Механизм сериализации, специфичный для Android, оптимизированный для быстрой передачи данных между компонентами. Требует явной реализации методов сериализации, но работает значительно быстрее Serializable.

    Хотя Serializable проще в реализации, Parcelable является предпочтительным выбором для Android из-за его высокой производительности, особенно при передаче сложных объектов между компонентами.
  • Что такое AAPT?

  • FlatBuffers vs JSON.

  • HashMap, ArrayMap и SparseArray

  • Что такое Аннотации?

  • Как создать пользовательскую Аннотацию?

  • Что такое Support Library? Почему она была введена?

  • Что такое Data Binding в Android?

Jetpack Compose

  • Что такое Compose? В чем различие подхода от XML?

    Jetpack Compose — это современный инструментарий для создания интерфейсов на языке Kotlin в приложениях Android. Он был разработан командой Google для упрощения и ускорения процесса разработки интерфейсов, сделав его более декларативным и интуитивно понятным. В отличие от традиционного подхода с использованием XML для разметки интерфейсов, Jetpack Compose позволяет разработчикам строить интерфейс напрямую в коде, используя Kotlin.

  • Какие фазы есть у Compose?

    • Composition: На этом этапе Compose строит или обновляет дерево компонентов UI, основываясь на текущем состоянии приложения. Компоненты (Composables) описывают, какие UI элементы должны быть отображены.
    • Layout: После составления дерева UI, система определяет размеры и позиционирование каждого элемента на экране. Это включает расчёт размеров в соответствии с ограничениями родительских элементов и расположение дочерних элементов.
    • Draw: На последнем этапе Compose отрисовывает UI на экране. Это включает в себя рисование текста, фигур, изображений и других визуальных элементов. compose phases
  • Что такое composable функция?

    Composable функция в Jetpack Compose — это специальный тип функции, аннотированный с @Composable. Эти функции являются строительными блоками пользовательского интерфейса в Compose и используются для описания, как должен выглядеть и функционировать UI. В отличие от традиционных функций, composable функции могут содержать другие composable вызовы, позволяя создавать сложный и динамичный интерфейс путем комбинирования более мелких, повторно используемых компонентов.

  • Можно ли вызвать composable функцию не из composable функции?

          В общем случае, composable функции предназначены для вызова только из других composable функций. Это связано с тем, что они используют специальный контекст композиции, который управляет их жизненным циклом, состоянием и оптимизацией перекомпозиции. Попытка вызвать composable функцию вне контекста композиции (например, из обычной функции Kotlin или из функции активити) приведет к ошибке компиляции.
          Это ограничение обусловлено тем, как работает система композиции в Jetpack Compose. Когда composable функция вызывается, она регистрирует себя и свои параметры в текущем контексте композиции, что позволяет системе Compose отслеживать зависимости и определять, когда и какие части UI нужно перерисовать. Без этого контекста композиции система не сможет корректно управлять жизненным циклом composable функций, их состоянием и оптимизацией перекомпозиции.

  • remember, rememberSaveable

    • remember: Это функция, используемая в Compose для сохранения состояния во время перекомпозиции. Когда параметры функции remember изменяются, она сбрасывает и пересоздаёт состояние с новыми значениями. Это полезно для сохранения объектов или значений, которые должны сохраняться между перекомпозициями, но не нуждаются в сохранении через процессы пересоздания активити или фрагмента.
    • rememberSaveable: Расширяет возможности remember, позволяя сохранять простые данные через процессы пересоздания активити или фрагмента, например, при повороте экрана. rememberSaveable использует механизм сохранения состояния в Bundle и подходит для примитивных типов данных и некоторых стандартных типов, таких как String.
      Чтобы использовать сохранение объектов, то необходимо пометить класс как Parcelize или использовать MapSaver, ListSaver.
  • Что такое mutablestateof?

    mutableStateOf - это функция, создающая объект состояния, который может быть изменяемым. Этот объект реализует паттерн наблюдателя, так что Compose может автоматически отслеживать изменения этого состояния и перерисовывать UI компоненты, которые зависят от этого состояния. mutableStateOf идеально подходит для управления состояниями в Compose, так как обеспечивает реактивное обновление UI.

  • derivedstateof vs mutablestateof

    • derivedStateOf - эта функция используется для создания производного состояния, значение которого вычисляется на основе других состояний. derivedStateOf предоставляет оптимизированный способ отслеживания изменений, так как Compose будет перерисовывать UI только тогда, когда действительно изменится производное значение, а не каждый раз при изменении исходных состояний.
    • mutableStateOf предназначен для хранения изменяемых данных и уведомления Compose о необходимости перерисовки при их изменении. Он является основой для управления состоянием в Compose.
    • В отличие от mutableStateOf, derivedStateOf не хранит изменяемые данные, а предоставляет механизм для создания значения, зависящего от одного или нескольких других состояний, оптимизируя при этом процесс перерисовки.
  • Что такое Recomposition?

          Recomposition — это процесс, в ходе которого Compose автоматически перерисовывает UI компоненты при изменении состояния, от которого они зависят. Это ключевой аспект реактивного подхода к построению интерфейса в Compose, позволяющий UI динамически адаптироваться к изменениям данных. Во время перекомпозиции Compose оптимизированно обновляет только те части интерфейса, которые действительно изменились, что делает процесс быстрым и эффективным.
          Recomposition активизируется изменениями в объектах состояния (например, значениях, обернутых в mutableStateOf), и Compose следит за этими изменениями, автоматически запуская перекомпозицию для затронутых компонентов. Это обеспечивает актуальность отображаемых данных и визуальных элементо

  • State hoisting

    State hoisting (подъем состояния) — это паттерн проектирования в Jetpack Compose, который рекомендуется для управления состоянием UI компонентов. Суть паттерна заключается в перемещении (или "подъеме") состояния из компонента (composable функции) на более высокий уровень в иерархии, чтобы сделать компонент более декларативным и упростить повторное использование и тестирование.
    При использовании подъема состояния компонент принимает текущее состояние и функции для изменения этого состояния через свои параметры, вместо того чтобы самостоятельно их создавать и управлять ими. Это означает, что состояние хранится вне компонента и передается в него, делая компонент более универсальным и предсказуемым.

  • Side-effects

    • DisposableEffect используется для управления ресурсами, которые требуют очистки при изменении ключей или при удалении композиции из UI дерева. Это может включать отписку от подписок, остановку анимации или закрытие соединений.
    • LaunchedEffect запускает корутину в контексте жизненного цикла композиции. Он полезен для выполнения асинхронных операций, таких как загрузка данных с сервера, при сохранении синхронизации с жизненным циклом Composable.
    • rememberCoroutineScope предоставляет доступ к CoroutineScope для запуска корутин в контексте текущей композиции, позволяя выполнять асинхронные операции с возможностью отмены и без привязки к жизненному циклу композиции.
    • rememberUpdatedState позволяет "запомнить" самое последнее значение передаваемого состояния или колбэка, так что даже если асинхронная операция завершилась после того, как состояние было изменено, используется актуальное значение.
    • produceState используется для создания состояния в Composable, которое может быть асинхронно обновлено. Оно идеально подходит для инкапсуляции асинхронной логики загрузки данных непосредственно в состоянии.
    • derivedStateOf используется для создания состояния, которое вычисляется на основе других состояний. Это помогает оптимизировать перерисовку, так как Compose будет перерисовывать UI только при изменении результата вычисления.
    • SideEffect используется для выполнения побочных эффектов, которые должны запускаться при каждой перекомпозиции, но не требуют очистки или отмены. Это может быть полезно для обновления заголовка экрана, логирования и т.д.
  • Modifier

    Modifier является ключевым концептом, который позволяет вам изменять или дополнять поведение компонентов (Composables) без изменения их кода. Это мощный инструмент для реализации таких вещей, как стилизация, расположение и обработка событий ввода.
    Основные аспекты Modifier:

    • Цепочечная природа: Modifiers могут быть соединены в цепочку, позволяя применять несколько модификаций последовательно. Каждый модификатор в цепочке вносит свой вклад, изменяя или дополняя предыдущие.
    • Повторное использование: Modifiers могут быть определены один раз и повторно использованы в разных компонентах, способствуя сокращению дублирования кода и улучшению его читаемости.
    • Расширяемость: Система Modifier в Compose предоставляет большое количество встроенных модификаторов, но также позволяет создавать собственные, что делает ее крайне гибкой и мощной.
  • Theme

    Темы в Jetpack Compose позволяют вам управлять визуальной консистентностью вашего приложения на высоком уровне, определяя цвета, типографику и другие аспекты дизайна, которые могут быть использованы по всему приложению

  • Layout

    В Jetpack Compose, система Layout позволяет разработчикам определять, как UI компоненты должны быть размещены и отображены на экране. Это включает в себя расположение элементов (как они позиционируются относительно друг друга), их размеры и пространство между ними. Compose предлагает гибкую систему Layout, которая может быть адаптирована под различные потребности дизайна.

    Ключевые аспекты:

    • Модификаторы для управления макетом: padding, size, fillMaxSize, и др.
    • Встроенные контейнеры макета: Column, Row, Box, и ConstraintLayout для сложных макетов.
    • Пользовательские макеты: Создание собственных макетных контейнеров с использованием низкоуровневых API для полного контроля над расположением и рендерингом.
  • List

    Для отображения списков данных, Compose предлагает LazyColumn и LazyRow, которые аналогичны RecyclerView в классическом Android UI Toolkit. Эти компоненты лениво отображают элементы, т.е., рендерят только те элементы, которые видны на экране, что делает их высокоэффективными для отображения больших списков.

    Ключевые аспекты:

    • Эффективность: Ленивая загрузка элементов улучшает производительность при работе с большими объемами данных.
    • Гибкость: Поддержка горизонтального и вертикального скроллинга, вложенных списков и динамически изменяемых элементов.
    • Пользовательские элементы: Возможность создавать сложные и настраиваемые элементы списка.
  • Gestures

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

    Ключевые аспекты:

    • Модификаторы для обработки жестов: clickable, onLongPress, swipeable, и т.д.
    • Интеграция с анимациями: Жесты могут быть легко интегрированы с анимационными эффектами для создания плавных и отзывчивых интерактивных элементов.
  • Animation

    Система анимации в Jetpack Compose предлагает мощные и гибкие инструменты для добавления анимаций в ваше приложение. Анимации могут быть простыми, как изменение цвета кнопки, так и сложными, как сложные переходы и трансформации.

    Ключевые аспекты:

    • Анимированные состояния: Использование animate*AsState для создания анимированных переходов между состояниями.
    • Переходные анимации: AnimatedVisibility и updateTransition для управления сложными анимациями с несколькими свойствами.
    • Низкоуровневые API: Для полного контроля над анимационным процессом.
  • CompositionLocal

    CompositionLocal предоставляет механизм для передачи данных вниз по дереву композиции без необходимости явно передавать пропсы через каждый уровень вложенности. Это полезно для доступа к общим данным, таким как темы, локализация или доступ к данным окружения.

    Ключевые аспекты:

    • Избегание prop drilling: Упрощает передачу данных в глубоко вложенные компоненты.
    • Доступ к данным окружения: Идеально подходит для тем, локализации и других настроек, которые должны быть доступны во всем приложении.
    • Пользовательские CompositionLocal: Возможность создания собственных объектов CompositionLocal для управления специфичными для приложения данными.

Android Библиотеки

  • Объясните OkHttp Interceptor

    OkHttp Interceptor позволяет перехватывать и модифицировать запросы и ответы перед и после их отправки/получения соответственно. Это может быть полезно для добавления, удаления или изменения заголовков запросов и ответов, логирования запросов/ответов, управления кэшированием и т.д.

  • OkHttp - HTTP Кэширование

  • Почему мы используем фреймворк внедрения зависимостей, например Dagger, в Android?

  • Как работает Dagger?

  • Как вы будете выбирать между Dagger 2 и Dagger-Hilt?

  • Что такое Component в Dagger?

  • Что такое Module в Dagger?

  • Как работает кастомная область видимости в Dagger?

  • Когда вызывать dispose и clear на CompositeDisposable в RxJava?

  • Что такое Multipart Request в Сетевых запросах?

  • Что такое Flow в Kotlin?

  • App Startup Library

  • Расскажите что-нибудь о RxJava.

  • Как вы будете обрабатывать ошибки в RxJava?

  • FlatMap против Map Оператор

  • Когда использовать оператор Create и когда использовать оператор fromCallable в RxJava?

  • Когда использовать оператор defer в RxJava?

  • Как используются операторы Timer, Delay и Interval в RxJava?

  • Как сделать два сетевых вызова параллельно с использованием RxJava?

  • Расскажите разницу между Concat и Merge.

  • Объясните Subject в RxJava?

  • Какие типы Observables существуют в RxJava?

  • Как реализовать функцию поиска с использованием RxJava в вашем приложении?

  • Пагинация в RecyclerView с использованием операторов RxJava

  • Как работают библиотеки загрузки изображений Glide и Fresco в Android?

  • Разница между Schedulers.io() и Schedulers.computation() в RxJava.

Android Архитектура

  • Что такое архитектура, когда она нужна и когда нет?

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

  • Архитектура вашего последнего приложения

  • Если бы перед вами стояла задача написать приложение-будильник, с какими проблемами бы вы столкнулись?

    • Точность таймеров и будильников: Необходимо обеспечить точное срабатывание будильника в установленное время, что может быть затруднено спящим режимом устройства.
    • Работа в фоне: Сохранение работоспособности приложения в фоновом режиме, особенно на новых версиях Android с ограниченными возможностями фоновой работы.
    • Энергопотребление: Оптимизация использования батареи приложением, особенно важно для будильников, которые должны работать всю ночь.
    • Взаимодействие с пользователем: Создание удобного пользовательского интерфейса для управления множеством будильников, выбора мелодий, настройки громкости и вибрации.
    • Поведение в разных часовых поясах: Учет смены часовых поясов пользователем, что может повлиять на срабатывание будильника.
    • Разрешения и доступ к системным настройкам: Получение необходимых разрешений для запуска мелодии, вибрации и изменения системных настроек.
  • Описание MVC

MVC

  • Описание MVP

    • Model также представляет бизнес-логику и данные.
    • View отображает данные и отправляет действия пользователя в Presenter. В отличие от MVVM, View здесь может быть более "умной", например, имея непосредственное знание о своем Presenter.
    • Presenter действует как посредник между View и Model, обрабатывая всю логику представления, но в отличие от ViewModel, Presenter напрямую обновляет View, обеспечивая более тесную связь между этими компонентами.

MVP можно запомнить через Presenter, который напрямую взаимодействует с View, обновляя её и реагируя на действия пользователя, подчеркивая тесную связь между ними.

MVVM

  • Описание MVVM

    MVVM (Model-View-ViewModel) - это архитектурный паттерн, используемый в разработке программного обеспечения для упрощения разработки пользовательских интерфейсов. Он разделяет программу на три основных компонента:

    • Model представляет собой бизнес-логику и данные. Это могут быть классы для работы с базой данных, сетевые запросы и т.д.
    • View отображает данные (UI пользователя) и перенаправляет действия пользователя в ViewModel.
    • ViewModel является посредником между View и Model. Она обрабатывает все логику представления и реакции на действия пользователя, но не имеет прямого доступа к компонентам интерфейса.

MVVM можно ассоциировать с ViewModel, который является ключевым компонентом, делая акцент на автоматическом связывании данных между View и Model через наблюдаемые объекты.

MVVM

  • Описание MVI

    MVI (Model-View-Intent) - это архитектурный паттерн, в котором Intent обозначает намерение изменить состояние приложения. Этот подход поощряет приложения работать в более реактивном стиле.

    • Model содержит состояние приложения. Оно реактивно и изменяется в ответ на Intent'ы.
    • View отображает состояние Model и отправляет Intent'ы, которые представляют действия пользователя.
    • Intent является уникальной особенностью MVI и представляет собой действия пользователя, которые изменяют состояние Model. Это создает однонаправленный поток данных, где View отправляет Intent'ы, которые изменяют Model, а новое состояние Model отображается в View.

MVI можно запомнить через Intent — уникальную концепцию этого паттерна, подчеркивая однонаправленный поток данных и реактивное обновление состояния приложения.

MVVM

  • MVC vs MVP vs MVVM vs MVI

    Архитектурные паттерны 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 обеспечивают, что каждый слой зависит только от внутренних слоев, а не от внешних. Это означает, что изменения в одном слое минимально влияют на другие слои, что упрощает тестирование и обновление приложения.

MVVM

Android System Design

  • Разработка библиотеки загрузки изображений

  • Разработка библиотеки загрузки файлов

  • Разработка WhatsApp

  • Разработка историй в Instagram

  • Разработка сетевой библиотеки

  • Разработка приложения Facebook для поиска друзей поблизости

  • Разработка библиотеки кэширования

  • Проблемы дизайна, основанные на приложениях с использованием геолокации

  • Как построить приложение с offline-first подходом? Объясните архитектуру

  • Разработка LRU кэша

  • Разработка библиотеки аналитики

  • HTTP Request vs HTTP Long-Polling vs WebSockets

  • Как работают голосовые и видеозвонки?

  • Разработка приложения Uber

Android Unit Тестирование

  • Модульное тестирование ViewModel с Kotlin Coroutines и LiveData

  • Модульное тестирование ViewModel с Kotlin Flow и StateFlow

  • Что такое Espresso?

  • Что такое Robolectric?

  • Какие недостатки у Robolectric?

  • Что такое UI-Automator?

  • Объясните что такое unit тестирование

  • Объясните что такое инструментальное тестирование

  • Почему используется Mockito?

  • Расскажите, что такое code coverage

Android инструменты и технологии

  • Что такое ADB?

  • Что такое StrictMode?

  • Что такое Lint? Для чего он используется?

  • Git

  • Firebase

  • Как измерить время выполнения метода в Android?

  • Можно ли получить доступ к вашей базе данных SQLite для отладки?

  • На что нужно обратить внимание при использовании Proguard?

  • Как использовать Memory Profiler в Android Studio?

  • Что такое Gradle?

  • Уменьшение размера APK

  • Как можно ускорить сборку Gradle?

  • Расскажите о системе сборки Gradle

  • Расскажите о создании нескольких APK для приложения Android

  • Для чего используется Proguard?

  • Что такое обфускация? Для чего она используется? Что такое минификация?

  • Как изменить некоторые параметры в приложении без обновления приложения?

Java

База

  • Стек (Stack) и куча (Heap)

  1. Стек работает по схеме LIFO (последним вошел, первым вышел). Всякий раз, когда вызывается новый метод, содержащий примитивные значения или ссылки на объекты, то на вершине стека под них выделяется блок памяти. Из этого можно сделать вывод, что стек хранит значения примитивных переменных, создаваемых в методах, а также ссылки на объекты в куче на которые ссылается метод.
    Когда метод завершает выполнение, блок памяти (frame), отведенный для его нужд, очищается, и пространство становится доступным для следующего метода. При этом поток выполнения программы возвращается к месту вызова этого метода с последующим переходом к следующей строке кода.
  2. Куча. Эта область памяти используется для динамического выделения памяти для объектов и классов JRE во время выполнения. Новые объекты всегда создаются в куче, а ссылки на них хранятся в стеке.
    Эти объекты имеют глобальный доступ и могут быть получены из любого места программы.
    • Young Generation — область где размещаются недавно созданные объекты. Когда она заполняется, происходит быстрая сборка мусора
    • Old (Tenured) Generation — здесь хранятся долгоживущие объекты. Когда объекты из Young Generation достигают определенного порога «возраста», они перемещаются в Old Generation
    • Permanent Generation — эта область содержит метаинформацию о классах и методах приложения, но начиная с Java 8 данная область памяти была упразднена.
  • JDK, JRE, JVM, JIT

    • 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 компилирует часто выполняемые части кода в машинный код для увеличения производительности.

JDK

  • ClassLoader

    ClassLoader - это часть JVM, которая загружает классы Java во время выполнения. ClassLoader загружает байт-код из файлов классов (.class) в Java Runtime Environment. Он играет важную роль в концепции безопасности Java, так как разделяет пространство имен классов в соответствии с их источниками (например, классы из локальной файловой системы и классы из сетевых источников).

ООП

  • Объясните концепты ООП в Java.

    ООП (Объектно-Ориентированное Программирование) в Java включает в себя четыре основных концепта:

    • Инкапсуляция: Сокрытие внутренней реализации класса и защита его данных.
    • Наследование: Создание новых классов на основе существующих.
    • Полиморфизм: Один и тот же метод может работать по-разному в зависимости от объекта, где он вызван, и данных, которые ему передали.
    • Абстракция: это когда мы сосредотачиваемся только на существенных для задачи деталях и игнорируем всё остальное. В ООП абстракция означает, что для каждого объекта мы задаём минимальное количество методов, полей и описаний, которые позволят нам решить задачу. Чем меньше характеристик, тем лучше абстракция, но ключевые характеристики убирать нельзя.
  • Различия между абстрактными классами и интерфейсами?

    • Абстрактный класс - это класс, который содержит как конкретные, так и абстрактные методы (методы без реализации). Абстрактный метод должен быть реализован подклассами абстрактного класса. Абстрактные классы не могут быть инстанцированы и должны быть расширены для использования.
    • Интерфейс подобен чертежу/контракту класса (или его можно рассматривать как класс с методами, но без их реализации). Он содержит пустые методы, которые представляют, что должно быть общим у всех его подклассов. Подклассы обеспечивают реализацию каждого из этих методов. Интерфейсы реализуются.
  • Разница между перегрузкой метода и переопределением метода.

    • Перегрузка метода (Method Overloading): Это когда несколько методов в одном классе имеют одно и то же имя, но различаются по типу и/или количеству параметров.
    • Переопределение метода (Method Overriding): Это когда подкласс заменяет метод своего суперкласса.
  • Какие модификаторы доступа вы знаете? Что делает каждый из них?

    В Java существует четыре модификатора доступа (от строжайшего к наиболее либеральному):

    • private переменные, методы, конструкторы или внутренние классы видны только внутри своего класса и его методов. Этот модификатор чаще всего используется, например, для доступа к переменным только через геттеры и сеттеры или для скрытия внутренней реализации классов, которые не должны использоваться пользователем, тем самым поддерживая инкапсуляцию. Конструктор Singleton также помечается как private, чтобы избежать нежелательного инстанцирования извне.
    • Default (ключевое слово не используется) этот модификатор может быть применен к классам, переменным, конструкторам и методам и позволяет доступ из классов и методов внутри того же пакета.
    • protected может использоваться для переменных, методов и конструкторов, тем самым разрешая доступ только подклассам и классам внутри того же пакета, что и класс защищенных членов.
    • public модификатор широко используется для классов, переменных, конструкторов и методов для предоставления доступа из любого класса и метода где угодно. Его не следует использовать повсеместно, поскольку это подразумевает, что данные, помеченные как public, не являются чувствительными и не могут быть использованы для нанесения вреда программе.
  • Может ли интерфейс реализовать другой интерфейс?

    Да, интерфейс может реализовать другой интерфейс (и более одного), но ему нужно использовать ключевое слово extends, а не implements. И хотя вы не можете удалять методы из родительского интерфейса, вы можете свободно добавлять новые к своему подинтерфейсу.

  • Что такое Полиморфизм? Что такое Наследование?

    • Полиморфизм: Это способность одного и того же кода вести себя по-разному в зависимости от контекста, в основном достигается через переопределение метода.
    • Наследование: Это механизм в Java, позволяющий одному классу использовать поля и методы другого класса.

Коллекции и дженерики

  • Arrays против ArrayLists

    • Arrays: Простые, фиксированной длины структуры данных для хранения элементов одного типа.
    • ArrayLists: Реализация изменяемого массива в Java, которая может автоматически увеличиваться и уменьшаться.
  • ArrayList vs LinkedList

    ArrayList это динамический массив, оптимизированный для быстрого доступа к элементам, а LinkedList — как список со связями между элементами, оптимизированный для операций вставки и удаления. Сценарии использования: если вам часто нужен доступ к элементам по индексу, выбирайте ArrayList; если в приоритете вставка и удаление, то LinkedList.

  • Расскажите как работает HashMap

    HashMap — это структура данных на основе хеш-таблицы для хранения пар ключ-значение, обеспечивающая быстрый доступ O(1) к элементам. Ключи уникальны, и каждый ключ соответствует одному значению. Внутренне HashMap использует массив бакетов для хранения элементов, где индекс каждого бакета определяется хеш-функцией от ключа. В случае коллизий, когда разные ключи приводят к одному индексу, используются связные списки (до Java 8) или красно-черные деревья (с Java 8), что позволяет эффективно управлять множественными элементами в одном бакете и сохранять производительность поиска. HashMap автоматически перестраивается при росте числа элементов для оптимизации производительности, но не гарантирует порядок элементов.

  • HashSet против TreeSet

    • HashSet: Реализация множества, использующая хеш-таблицу для хранения элементов. Не гарантирует порядка элементов.
    • TreeSet: Реализация множества, основанная на красно-черном дереве, которая поддерживает упорядоченность элементов по возрастанию.
  • HashMap против Set

    • HashMap: Коллекция, использующая пары ключ-значение для хранения данных, где каждый ключ уникален.
    • Set: Интерфейс, представляющий коллекцию уникальных элементов, не допускающий дублирования.
  • Объясните дженерики в Java.

    Дженерики в Java позволяют программистам использовать типы (классы и интерфейсы) в качестве параметров при определении классов, интерфейсов и методов. Они обеспечивают строгую проверку типов во время компиляции и поддерживают обобщенное программирование, что делает код более безопасным и легче читаемым.

Объекты и примитивы

  • Как реализован класс String? Почему он был сделан неизменяемым?

    В Java нет примитивного варианта класса String - все строки являются просто обертками вокруг базового массива символов, который объявлен как final. Это означает, что, как только объект String создан, он не может быть изменен с помощью обычных инструментов языка (Reflection все еще может ужасно все испортить, потому что в Java никакой объект на самом деле не является полностью неизменяемым). Именно поэтому переменные класса String являются первыми кандидатами для использования, когда вы хотите переопределить hashCode() и equals() вашего класса - вы можете быть уверены, что все их требуемые контракты будут выполнены.

  • Что значит сказать, что строка (String) неизменяема?

    Это означает, что после создания объекта String, его содержимое (массив символов) не может быть изменено. Любые операции, которые кажутся изменяющими строку, на самом деле создают новый объект String.

  • Что такое класс Object?

    Это корневой класс в иерархии классов Java. Все классы наследуются от Object. Ключевые методы класса Object включают equals(), hashCode(), toString(), getClass(), clone(), notify(), notifyAll(), и wait().

  • Что такое хэш-код?

    В Java хэш-код объекта — это целое число, используемое для распределения объектов в хэш-таблице. Если метод hashCode() не переопределен, класс Object предоставляет реализацию, которая преобразует внутренний адрес объекта в целое число.

  • Можете ли вы перечислить 8 примитивных типов в Java?

    • byte
    • short
    • int
    • long
    • float
    • double
    • char
    • boolean
  • В чем разница между примитивами?

    • Числовые типы: byte (8 бит), short (16 бит), int (32 бита), long (64 бита) представляют целочисленные значения разного размера. float (32 бита) и double (64 бита) представляют числа с плавающей точкой.
    • Логический тип: boolean представляет значения истина/ложь.
    • Символьный тип: char (16 бит) представляет символы в Unicode.
  • В чем разница между Integer и int?

    • int: примитивный тип данных, представляющий целочисленные значения.
    • Integer: класс-обертка, предоставляющий методы для работы с объектами, содержащими целочисленные значения, и позволяющий использовать int в качестве объектов.
  • Передаются ли объекты по ссылке или по значению в Java? Раскройте этот вопрос.

    В Java все параметры передаются по значению. Для объектов значение ссылки на объект копируется в параметр метода.

  • Что происходит, когда объект больше не нужен?

    Когда объект в Java становится недостижимым (т.е., на него не остается активных ссылок), он становится кандидатом на сборку мусора, что означает, что память, занимаемая этим объектом, может быть освобождена.

  • Что такое сборщик мусора? Как он работает?

    https://javarush.com/quests/lectures/questservlets.level18.lecture03
    Сборщик мусора в Java автоматически удаляет объекты, на которые больше нет ссылок, тем самым освобождая память для повторного использования. Все объекты размещаются в куче, управляемой JVM. Пока на объект ссылаются, JVM считает его живым. Как только на объект больше не ссылаются и, следовательно, он недоступен для кода приложения, сборщик мусора удаляет его и освобождает неиспользуемую память.
    В контексте GC важно понятие "графа достижимости" (reachability graph). В этом графе объекты представляются узлами, а ссылки между объектами — рёбрами. Корни этого графа — это набор "живых" объектов, доступных напрямую (например, локальные переменные стека, активные потоки выполнения, статические поля классов и т.п.). Объект считается "живым" и не подлежит удалению GC, если он достижим, то есть до него можно добраться по цепочке ссылок от одного из корней.

  • Типы ссылок в Java

    • Strong Reference (Сильная ссылка): Обычная ссылка, предотвращающая сбор мусора для объекта, пока существует сильная ссылка.
    • Soft Reference (Мягкая ссылка): Сбор мусора для таких объектов происходит только в случае нехватки памяти.
    • Weak Reference (Слабая ссылка): Объект может быть собран мусором при следующей сборке мусора, даже если есть слабые ссылки.
    • Phantom Reference (Фантомная ссылка): Объект, на который есть только фантомные ссылки, будет собран мусором, но перед этим будет помещен в очередь ссылок, что позволяет системе очистить его ресурсы.
  • Что такое переполнение памяти при выполнении программы и что происходит при переполнении памяти?

    Утечка памяти происходит, когда программа постоянно использует больше памяти, чем освобождает, что в конечном итоге приводит к исчерпанию доступной памяти. При переполнении памяти программа может замедлиться или аварийно завершиться.

  • Что такое gc roots?

    В контексте сборки мусора (GC) в Java, GC Roots (корни сборки мусора) представляют собой набор объектов, которые являются отправной точкой для сборщика мусора при его работе. Эти объекты считаются "живыми" и не могут быть собраны сборщиком мусора. Сборщик мусора использует корни GC как начальные точки для определения доступных (достижимых) объектов. Любой объект, который можно достичь, пройдя от корней GC через цепочку ссылок, также считается живым и, следовательно, не подлежит уничтожению.
    GC Roots включают в себя:

    • Локальные переменные: Переменные, находящиеся в стеке вызовов (то есть переменные, принадлежащие активным методам).

    • Активные потоки: Объекты потока, которые все еще выполняются.

    • Статические поля: Переменные класса, поскольку они принадлежат классу, который находится в области PermGen (до Java 8) или Metaspace (начиная с Java 8) и доступен из любой точки приложения.

    • JNI (Java Native Interface) ссылки: Объекты, на которые ссылаются из нативного кода.

    Эти корни формируют основу для графа достижимости — структуры данных, которую сборщик мусора использует для определения, какие объекты в куче живы и какие можно уничтожить. Процесс определения достижимых объектов начинается с этих корней и рекурсивно проходит через все объекты, на которые имеются ссылки. Объекты, до которых невозможно добраться от корней GC, считаются недостижимыми и могут быть собраны сборщиком мусора, тем самым освобождая ресурсы и память, которые они занимали.

Многопоточность

  • Что такое deadlock и livelock?

    • Deadlock возникает, когда два или более потоков в системе ожидают ресурсов, занятых друг другом, в результате чего они вечно ждут и не могут продолжить выполнение.
    • Livelock – это ситуация, аналогичная deadlock, но потоки не заблокированы; они активно пытаются разрешить вопрос, но так и не достигают прогресса, постоянно меняя своё состояние в ответ на состояние друг друга.
  • Что такое race condition (гонка потоков)?

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

  • Что означает ключевое слово synchronized?

    Ключевое слово synchronized используется для обозначения методов или блоков кода, которые могут быть выполнены только одним потоком одновременно для предотвращения проблем совместного доступа. Это помогает предотвратить race conditions при доступе к общим ресурсам.

  • Что такое atomic classes и благодаря чему они являются атомарными?

    Классы из пакета java.util.concurrent.atomic, которые поддерживают атомарные операции на одном значении без использования синхронизации. Благодаря использованию низкоуровневых атомарных инструкций процессора, такие операции могут быть выполнены без блокировок, что делает их очень быстрыми и эффективными.

  • Что такое ThreadPoolExecutor?

    ThreadPoolExecutor - это реализация пула потоков в Java, которая управляет пулом рабочих потоков, позволяя эффективно выполнять множество асинхронных задач.

  • Что такое модификатор volatile?

    Указывает компилятору и виртуальной машине Java, что значение переменной может изменяться разными потоками. Это обеспечивает, что значение переменной будет считываться из основной памяти, а не из кэша CPU, гарантируя, что чтение переменной всегда дает последнее записанное значение.

  • Классы в пакете atomic предоставляют общий набор методов: get, set, lazyset, compareAndSet, и weakCompareAndSet.

    • get: возвращает текущее значение.
    • set: устанавливает новое значение.
    • lazyset: устанавливает новое значение с гарантией порядка изменений только в однопоточных контекстах.
    • compareAndSet: атомарно устанавливает новое значение, если текущее значение соответствует ожидаемому.
    • weakCompareAndSet: вариант compareAndSet с возможностью неудачи в некоторых ситуациях для улучшения производительности.

Исключения

  • Как работают try{}, catch{}, finally{}?

    • try{}: блок кода, в котором могут возникнуть исключения.
    • catch{}: блок кода, обрабатывающий исключение, возникшее в блоке try.
    • finally{}: блок кода, который выполняется после блоков try/catch, независимо от того, возникло исключение или нет.
  • В чем разница между Проверяемым Исключением и Непроверяемым Исключением?

    • Проверяемые исключения (Checked Exceptions): это исключения, которые должны быть явно перехвачены или объявлены в методе.
    • Непроверяемые исключения (Unchecked Exceptions): это исключения, которые не требуют явного перехвата или объявления в методе.

Другое

  • Что такое сериализация? Как вы ее реализуете?

    Сериализация - это процесс преобразования объекта в последовательность битов для сохранения или передачи. В Java это можно реализовать с помощью интерфейса Serializable.

  • Что такое модификатор transient?

    Модификатор transient используется для исключения полей класса при сериализации.

  • Что такое анонимные классы?

    Анонимные классы - это классы без имени, объявленные и инициализированные одновременно, обычно используются для создания экземпляров простых интерфейсов или классов на месте.

  • В чем разница между использованием == и .equals на объекте?

    • ==: проверяет равенство ссылок на объекты.
    • .equals(): проверяет равенство содержимого объектов.
  • Для чего используются hashCode() и equals()?

    Методы hashCode() и equals() используются для сравнения объектов. hashCode() возвращает хеш-код объекта, а equals() проверяет, равны ли два объекта.

  • Когда бы вы придали объекту значение final?

    Ключевое слово final используется для объявления констант, переменных, которые не могут быть изменены после инициализации.

  • Что такое ключевые слова final, finally и finalize?

    • final: используется для объявления констант, методов, которые не могут быть переопределены, и классов, которые не могут быть наследованы.
    • finally: блок кода, который выполняется после блока try/catch в конструкции обработки исключений, независимо от того, было ли исключение.
    • finalize(): метод, вызываемый перед утилизацией объекта сборщиком мусора.
  • В чем разница между ключевыми словами "throw" и "throws" в Java?

    • throw: используется для явного выброса исключения.
    • throws: указывает на то, что метод может выбросить исключение, и требует его обработки или дальнейшего объявления.
  • Что означает слово static в Java?

    Ключевое слово static используется для объявления членов класса, которые могут быть доступны без создания экземпляра класса.

  • Можно ли переопределить статический метод в Java?

    Статические методы не могут быть переопределены в том смысле, как переопределяются обычные методы. Они могут быть "скрыты" в подклассе, если в подклассе объявлен статический метод с тем же именем.

  • Когда выполняется статический блок?

    Статический блок выполняется при первой загрузке класса в JVM.

  • Что такое рефлексия?

    Рефлексия в Java позволяет программе исследовать или "размышлять" о себе самой, изменять свое поведение во время выполнения.

  • Что такое Внедрение Зависимостей (DI)?

    Внедрение зависимостей - это дизайн-паттерн, который позволяет классам получать свои зависимости извне, вместо их создания внутри себя, улучшая модульность и тестируемость.

  • Разница между StringBuffer и StringBuilder?

    • StringBuffer: потокобезопасная версия изменяемой последовательности символов.
    • StringBuilder: не потокобезопасная версия, более быстрая по сравнению с StringBuffer при выполнении операций в однопоточном режиме.
  • В чем разница между быстро-сбойными и безопасно-сбойными итераторами в Java?

    Быстро-сбойные итераторы немедленно выдают ConcurrentModificationException, если коллекция была изменена после создания итератора любым способом, кроме самого итератора. Это предназначено для обнаружения ошибок в многопоточных программах. Безопасно-сбойные итераторы используют механизмы, такие как копирование коллекции, что позволяет итераторам проходить по коллекции без выбрасывания исключения, даже если основная коллекция была изменена во время итерации.

  • Монитор и Синхронизация

    Монитор в Java - это механизм, обеспечивающий возможность синхронизированного доступа к блоку кода или методу, так что в каждый момент времени только один поток может исполнять блок кода или метод, помеченный как synchronized. Синхронизация используется для предотвращения проблем совместного доступа и состояния гонки, обеспечивая, чтобы только один поток мог выполнить определенный участок кода, работающий с общими ресурсами, в данный момент времени.

Прочие темы

SOLID

SOLID — это аббревиатура, обозначающая пять базовых принципов объектно-ориентированного программирования и дизайна, которые помогают разработчикам создавать более устойчивые, удобные в обслуживании и расширяемые системы.

  • S — Принцип единственной ответственности (Single Responsibility Principle)

    Этот принцип гласит, что класс должен иметь только одну причину для изменения. Это означает, что класс должен выполнять только одну задачу или иметь одну область ответственности.
    // Нарушение принципа
    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) {
          // Печать отчета пользователя
      }
    }
  • O — Принцип открытости/закрытости (Open/Closed Principle)

    Классы должны быть открыты для расширения, но закрыты для модификации. Это означает, что можно добавлять новые функциональности, не изменяя существующий код.
    // Нарушение принципа
    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()
        }
    }
  • L — Принцип подстановки Барбары Лисков (Liskov Substitution Principle)

    Объекты в программе можно заменить их наследниками без изменения свойств программы. Наследуемый класс должен дополнять, а не заменять поведение базового класса.
      // Нарушение принципа
      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()
    
  • I — Принцип разделения интерфейса (Interface Segregation Principle)

    Клиенты не должны быть вынуждены реализовывать интерфейсы, которые они не используют. Интерфейсы должны быть специфическими, а не универсальными.
    // Нарушение принципа
    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() { /* Работаем */ }
    }
  • D — Принцип инверсии зависимостей (Dependency Inversion Principle)

    Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба типа модулей должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
    // Нарушение принципа
    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()
          }
      }
    }
    

Design Patterns, GoF (Gang of Four) банда четырех

  • Порождающие паттерны (Creational)

    Эти паттерны отвечают за удобное и безопасное создание новых объектов или даже целых семейств объектов.

    • Фабричный метод (Factory Method)
    • Абстрактная фабрика (Abstract Factory)
    • Строитель (Builder)
    • Прототип (Prototype)
    • Одиночка (Singleton)
  • Структурные паттерны (Structural)

    Эти паттерны отвечают за построение удобных в поддержке иерархий классов.

    • Адаптер (Adapter)
    • Мост (Bridge)
    • Компоновщик (Composite)
    • Декоратор (Decorator)
    • Фасад (Facade)
    • Легковес (Flyweight)
    • Заместитель (Proxy)
  • Поведенческие паттерны (Behavioral)

    Эти паттерны решают задачи эффективного и безопасного взаимодействия между объектами программы.

    • Цепочка обязанностей (Chain Of Responsibility)
    • Команда (Command)
    • Итератор (Iterator)
    • Посредник (Mediator)
    • Снимок (Memento)
    • Наблюдатель (Observer)
    • Состояние (State)
    • Стратегия (Strategy)
    • Шаблонный метод (Template Method)
    • Посетитель (Visitor)

Структуры данных и алгоритмы

Алгоритмы, которые могут спросить на собеседованиях зачастую берут с LeetCode сложности easy и medium:

Big O Cheat Sheet

Releases

No releases published

Packages

No packages published