Try   HackMD

Android-developer. Вопросы на собеседование.

Общие вопросы.

Основные принципы ООП.

Основные принципы Объектно-Ориентированного Программирования (ООП) - инкапсуляция, наследование, полиморфизм и абстракция.

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

Композиция.

Композиция - это жесткое отношение между классами, когда объект не только является частью какого-то другого объекта, но и вообще не может принадлежать кому-то еще.
Хороший пример: "Машина" и "Двигатель". Хотя двигатель может быть и без машины, но он вряд ли сможет быть в двух или трех машинах одновременно. В отличии от студента, который может входить и в другие группы тоже.

SOLID.

SOLID - это абревиатура пяти основных принципов проектирования в объектно-ориентированном программировании. Благодаря данным принципам имеется возможность строить на базе ООП масштабируемые и сопровождаемые программные продукты, с понятной бизнес-логикой.
Расшифровка аббревиатуры:

  • Single responsibility - принцип единой ответственности - каждый класс должен иметь только одну обязанность и эта обязанность должна быть полностью инкапсулированна в класс.
  • Open-Close principle - принцип открытости/закрытости - программные сущности, такие как классы, интерфейсы и т.д., должны быть открыты для расширения, но закрыты для изменения, т.е. сущности могут изменять своё поведение без изменения исходного кода.
  • Liskov substitution - принцип подстановки Лисков - функции, которые используют базовые типы, должны иметь возможность использовать подтипы данного базового типа, не зная об этом.
  • Interface segregation - принцип разделения интерфейсов - слишком большие интерфейсы необходимо разделять на более мелкие и специфические, чтобы клиенты данных интерфейсов знали только о тех методах, которые им необходимы.
  • Dependency inversion - принцип инверсии зависимостей - модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций. Абстракции не должны зависеть от деталий. Делати должны зависеть от абстракций.

Clean Architecture.

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

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

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

Паттерны проектирования (дизайн-паттерны).

Паттерны проектирования - это готовые к использованию решения часто возникающих задач при разработке. Дизайн-паттерны деляться на следующие виды: порождающие, структурные и поведенчиские.

  • Порождающие паттерны - это паттерны, которые предоставляют механизмы инициализации, позволяют создавать объекты удобным способом.
    • Singleton - одиночка - паттерн позволяет создавать гарантированно единственный объект какого-то класса.
    • Abstract Factory - абстрактная фабрика - паттерны позволяет создавать семейства взаимосвязанных объектов, без привязки к их конкретным классам.
    • Factory Method - фабричный метод - паттерн позволяет создавать объект определнного типа без указания их в коде.
    • Builder - строитель - паттерн позволяет создавать объект поэтапно, разбивая процесс создания на отдельные этапы.
    • Prototype - прототип - паттерн позволяет создавать новые объекты путем клонирования существующих объектов, вместо создания объектов с нуля.
  • Структурные паттерны - это паттерны, которые определяют отношения между классами и объектами, позволяют работать им совместно.
    • Adapter - адаптер - паттерн, который используется для преобразования данных из одного источника в формат, необходимый для работы с другим компонентом.
    • Decorator - декоратор - паттерн, который используется преобразования объекта, добавления ему нового функционала.
    • Composite - компоновщик - паттерн, который используется для управлением группами объектов, как единым целым.
  • Поведенчиские паттерны - это паттерны, которые используются для того, чтобы упростить взаимодействие между сущностями.
    • Observer - наблюдатель - паттерн, который определяет отношение "один ко многим" между объектами таким образом, что при изменении одного объекта - все завимимые объекты от него уведомляются об этом.
    • Memento - снимок/хранитель - паттерн, который позволяет сохранить состояние объекта таким образом, чтобы в дальнешем его можно было восстановить.
    • Iterator - итератор - паттерн, который предоставляет последовательный доступ к свойствам объекта, не раскрывая его внутренней структуры.

Анти-паттерны.

Анти-паттерны - это повторяющиеся решения при разработке, которые могут привести к нежелательным эффектам, затрудняют поддержку и развитие продукта. Примеры анти-паттернов:

  • God Object - объект-Бог - объекты, которые "знают" слишком много о других объектах и управляют ими, нарушает принцип инкапсуляции.
  • Big Class - большой класс - классы, которые содержат слишком много кода, нарушает принцип единой ответсвенности.
  • Golden Hammer - слепая вера - использование одной и той же технологии или инструмента для решения всех задач без анализа их подходящести.

REST API.

REST API - это архитектурный стиль, который определяет правила для создания веб-сервисов. REST API предоставляет удобный интерфейс для взаимодействия между клиентом и сервером по протоколу HTTP.
Основные принципы REST API:

  • Клиент-серверная архитектура: клиент и сервер взаимодействуют друг с другом через стандартные HTTP-запросы и ответы.
  • Без состояния (Stateless): каждый запрос клиента содержит всю необходимую информацию для выполнения этого запроса. Сервер не сохраняет состояние между запросами.
  • Кэширование: клиент может кэшировать ответы сервера, чтобы уменьшить количество запросов на сервер и ускорить работу приложения.
  • Единообразный интерфейс: сервер предоставляет единообразный интерфейс для работы с ресурсами, используя стандартные HTTP-методы (GET, POST, PUT, DELETE) и форматы данных (JSON, XML).
  • Слои: между клиентом и сервером могут быть добавлены промежуточные слои, такие как кэширование, балансировка нагрузки и безопасность.

Основные HTTP-методы:

  • GET-метод - используется для получения ресурса с сервера. Данный запрос не должен иметь побочных эффектов на сервере, то есть он не должен изменять состояние сервера.
  • POST-метод - используется для отправки данных на сервер для создания нового ресурса. Данный запрос может иметь побочный эффект на сервере, например, создание новой записи в базе данных.
  • PUT-метод - используется для обновления существующего ресурса на сервере. Данный запрос обычно содержит всю информацию, необходимую для полной замены ресурса на сервере.
  • DELETE-метод - используется для удаления ресурса с сервера.
  • HEAD-метод - аналогичен GET-запросу, но сервер отвечает только заголовками, без тела ответа. HEAD-запрос часто используется для проверки доступности ресурса.
  • PATCH-метод - используется для частичного обновления существующего ресурса на сервере.

Протоколы HTTP, HTTPS.

Протоколы HTTP и HTTPS являются стандартными протоколами для передачи данных в сети Интернет. Они определяют правила и формат взаимодействия между клиентскими и серверными приложениями при передаче гипертекстовых данных, таких как веб-страницы, изображения, видео и другие ресурсы.

HTTP - простой протокол обмена данными между клиентом и сервером. Клиент отправляет на сервер HTTP-запрос содержащий:

  • Метод запроса (например GET, POST, PUT, PATCH, DELETE и т.д.).
  • URI ресурса.
  • Заголовки запроса (headers).
  • Тело запроса.

Сервер обрабатывает запрос и отправляет обратно HTTP-ответ содержащий статус ответа (например, 404 Not Found или 200 OK) и, при необходимости, тело запроса.

HTTPS - защищенная версия протокола HTTP, которая используется для обеспечения защищенности передаваемых данных. В отличие от протокола HTTP, который передает данные в открытом виде, HTTPS использует протокод SSL/TLS для шифрования данных и обеспечения целостности данных.

Протокол HTTPS следует использовать для передачи чувствительных данных: пароли, финансовая информация, личная информация и т.д.

Принципы шифрования, сертификаты, SSL/TLS.

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

Сертификаты SSL/TLS - это электронные документы, используемые для аутентификации и установления доверия между клиентом и сервером в протоколе HTTPS. Они выдаются доверенными центрами сертификации (Certificate Authorities - CA) и содержат информацию о владельце сертификата (например, доменное имя), публичном ключе владельца, сроке действия сертификата и цифровой подписи, которая гарантирует его подлинность.

SSL/TLS - это протоколы безопасной передачи данных, используемые для защиты информации, передаваемой между клиентом и сервером через сеть. Они обеспечивают конфиденциальность, целостность и аутентификацию данных.

Процесс SSL/TLS начинается с установки "рукопожатия" (handshake) между клиентом и сервером, в котором они соглашаются на версию протокола, выбирают алгоритмы шифрования и обмениваются сертификатами для аутентификации. Затем они устанавливают защищенное соединение и обмениваются данными, которые шифруются и расшифровываются с использованием ключей шифрования, установленных во время рукопожатия.

Класс, Интерфейс, Абстрактный класс.

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

Java.

Память в Java.

В Java память разделена на две области: стек и кучу.
Стек хранит локальные переменные и состояние вызовов методов. Каждый поток в Java имеет свой собственный стек, который используется для хранения данных, связанных с методами, вызываемыми в потоке. Стек управляется автоматически, и его размер увеличивается и уменьшается по мере необходимости.

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

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

Модификаторы доступа в Java.

  • по умолчанию - класс или член класса доступны всем классам в текущем пакете.
  • public - общедоступный класс или член класса (поля, методы доступны другим классам из текущего пакета и из внешних пакетов).
  • private - закрытый класс или член класса (противоположен public: поля, методы доступны только из текущего класса).
  • protected - поля и методы класса доступны только наследникам.

Специальное слово static.

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

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

Суперкласс Object.

Object - это суперкласс в Java, который является корневым классом для всех других классов, поэтому ссылочная переменная класса Object может ссылаться на объект любого класса, в том числе и на любой массив.

Данный класс имеет большое количество методов, самые основные из них:

  • hashCode() - возвращает хэш-код объекта.
  • toString() - возвращает строку, описывающую объект.
  • clone() - возвращает точно такой же объект.
  • finalize() - метод, который вызывается перед удалением неиспользуемого объекта.

Дженерик.

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

Дженериками могут быть разные сущности. Разберемся подробнее:

  • дженерик-классы (generic classes) — это классы, «схемы» объектов с параметром. При создании объекта ему передается тип, с которым он будет работать;
  • дженерик-методы (generics methods) — это методы, работающие по такому же принципу. Метод — это функция внутри объекта, то, что он может делать. Методу тип передается при вызове, сразу перед аргументами. Так можно создавать более универсальные функции и применять одну и ту же логику к данным разного типа.

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

Методы equals() и hashCode().

Методы equals() и hashCode() используются для сравнения объектов.
Простой оператор == не подходит для сравнения объектов, так как он сравнивает ссылки на объекты в памяти, а не сами объекты.
В Java эти методы необходимо переопределить.

Метод equals() сравнивает объекты по значению их полей.
Метод hashCode() представляет числовое представление объекта и производит сравнение уже по этим числовым представлениям.

Существуют важные правила для методов equals() и hashCode():

  • Должны быть переопределенны оба метода.
  • Если хэшкоды объектов разные, то и объекты разные. Если хэшкоды одинаковые, то не факт, что объекты одинаковые - может произойти коллизия.

Коллекции в Java.

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

На вершине иерархии коллекций находится интерфейс Iterable (необходим для перебора коллекций), ниже находится интерфейс Colletion , от которого наследуются интерфейсы List, Set, Queue и, отдельно от основной иерархии, лежит интерфейс Map .

List - это упорядочный список. Объекты хранятся в порядке их добавления в список.Доступ к элементам осуществляется по индексу. Основные реализации: ArrayList, LinkedList.
Set - множество неповторяющихся объектов. В коллекции данного типа разрешенно наличие только одного типа null. Основные реализации: TreeSet, HashSet.
Queue - коллекция, предназначенная для хранения элементов в порядке, нужном для работы. Очери обычно, но не обязательно, реализуют по принципу FIFO ("first in, first out"). Основные реализации: ArrayDeque.
Map - данный интерфейс используется для отображения элемента из одного множества объектов (ключей) на другое (значений). При этом, каждому элементу из множества ключей ставится в соответствие множество значений. В то же время одному элементу из множества значений может соответствовать 1, 2 и более элементов из множества ключей. Основные реализации: HashMap, TreeMap.

Важно отметить, что интерфейс Map не наследует интерфейс Collection, так как не все методы в интерфейсе Collection можно использовать в Map, например метод remove(Object o), который предназначен для удаления объекта, в то время как интерфейс Map имеет метод remove(Object key), который предназначен для удаления объекта по ключу.

HashMap.

HashMap -

ArrayList - LinkedList.

ArrayList - это список, реализованный на основе массива, а LinkedList - это классический связанный список, основанный на объектах с ссылками между ними.

ArrayList - Vector.

HashMap - HashTable.

Сереализация и десериализация.

Сереализация - это процесс преобразования объектов в набор в байтов.
Десериализация - это процесс преобразованя наборов байт в объекты.

Бывают ситуации, когда какие-то поля нельзя сереализовать (например, поле-объект, который не реализует интерфейс Serializable), тогда необходимо пометить данной поле ключевым словом transient.
При десериализации такие поля в объектах будут равны null.

Error и Exception.

Error - ошибка - это то, что нельзя исправить, об этом можно только сообщить. Например, нет возможности подключиться к базе данных, следовательно работать не получится.
Exception - исключение - непредвиденная, особая ситуация, которую необхимо как-то обработать. Например, если разделить на ноль, то калькулятор не зависнет, а "выкинет" исключение и продолжит работу

Kotlin.

Kotlin - это статически типизированный язык, поддерживающий автоматический вывод типов. Данный язык поддерживает как объектно-ориентированный, так и функциональный стиль программирования. В 2017 году компания Google назвала Kotlin - основным языком разработки под ОС Android.
Основные плюсы Kotlin, которые можно выделить: прагматичность, лаконичность, безопасность и совместимость.

Основы Kotlin.

val и var служат для объеявления неизменяемой (но не константа) и изменяемых переменных соответсвенно.
if теперь является выражением.
when - более мощный аналог switch в Java.
for, while, do-while похожи на свои аналоги в Java, но цикл for - стал намного удобнее, особенно при работе со словарями или коллекциями.
Обработка исключений выполняется почти так же, как и в Java, разница лишь в том, что Kotlin не требует перечислять контролируемые исключения, которые может возбуждать функция.
Всем объявлениям по умолчанию присваиваются модификаторы доступа final и public. Модификтор доступа open отменяет действие модификатора final.

Классы Any, Unit, Nothing.

Any - это корневой класс иерархии классов в Kotlin, является аналогом классу Object в Java. Все классы неявно наследуются от данного класса.
Данный класс предоставляет базовую функциональность для всех объектов в Kotlin. Имеет следующие полезные методы:

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

Unit - это класс, который является подтипом класса Any, представляет отсутсвие или пустое значения, является аналогом void в Java.
Данный класс используется в качестве возвращаемого типа для функций, которые ничего не возвращают.

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

Модификаторы доступа в Kotlin.

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

null-safety.

null-safety - это разграничение типов с поддержкой и без поддержки null значений.

При объявлении переменной, которая может хранить null, нужно явно объявить ее как nullable при помощи оператора ?. Так же существуют операторы, которые помогают работать с переменнами nullable.

  • ?. - оператор безопасного вызова - позволяет сказать компилятору, что значение данной переменной может быть равно null и его стоить проверить перед дальнейшим использованием.
  • !! - оператор NullPointerException - позволяет сказать вызвать NullPointerException, если переменная равна null.
  • ?: - оператор Элвис - оператор используется для того, чтобы указать новое значение, если данная переменная равна null.

Так как код Kotlin может быть совмещен с кодом Java, который не имеет технологию null-safety, поля или переменные в Java необходимо помечать специальными аннотациями Nullable и NotNull.

data class.

data class - это специальный класс данных, который имеет свои особенности.

  • У данных классов изначательно переопреденны методы equals(), hashCode(), copy(), componentN().
  • От данных классов нельзя наследоваться.
  • Данные классы обязаны иметь непустой первичный конструтор.

sealed- class/interface.

sealed class - это специальные классы, которые позволяют создать ограниченную иерархию наследования. Во время компиляции известны все прямые наследники данного класса.
Данные классы еще называют enum-классы "на стероидах". Важная разница в том, что в enum-классах каждая единица существует только в единствнном экземпляре, в то время как наследник изолированного класса может иметь несколько экземпляров.
Пример ипользования данных классов - иерархия серверных ошибок, которые могут возникнуть во время связи с сервером.

enum class.

enum class - это специальные классы, которые используются для перечисления констант, которые будут как-то использованны. Значение каждой константы представляет собой объект, созданный от своего класса-перечисления.
Данные классы могут реализовывать интерфейсы.

companion object.

companion object - это специальный объект, который объявляется внутри класса и используетя для объявления статических методов и переменных для этого класса.
Данный объект является Singleton, т.е. у класса может быть только один companion object.

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

Одним из преимуществ использования companion object является то, что он является частью класса и имеет доступ к его закрытым членам, что обеспечивает контролируемый доступ к статическим ресурсам.

extension функции.

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

inline функции.

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

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

Плюсы данных функций:

  • Уменьшение накладных расходов: Инлайн-функции устраняют расходы на создание объектов и вызов функций, что может привести к повышению производительности в некоторых сценариях.
  • Возможность доступа к контексту вызова: При использовании inline функций вы можете получить доступ к контексту вызова, включая доступ к закрытым или защищенным членам класса.

Минусы данных функций:

  • Увеличение размера кода: Вставка кода функции в каждое место вызова может привести к увеличению размера скомпилированного кода.
  • Возможные проблемы с памятью: Если inline функция содержит большой объем кода или используется внутри циклов, это может привести к увеличению потребления памяти.

typealias

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

typealias не создает новый тип данных, а только предоставляет альтернативное имя для существующего типа. Поэтому взаимозаменяемость между исходным типом и псевдонимом гарантируется компилятором.

lateinit

lateinit - это модификатор свойства, который позволяет отложить инициализацию свойства до его первого использования.

lambda выражения.

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

Лямбда-выражения могут использоваться в различных контекстах. Одним из наиболее распространенных примеров является использование их в функциях высшего порядка, таких как map, filter и reduce, где они могут быть переданы в качестве аргументов для выполнения определенных действий над элементами коллекции.

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

Делегаты в Kotlin.

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

Популярные делегаты: lazy, Delegates.NotNull, Delegates.observable.

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

Коллекции в Kotlin.

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

  • List (список): Представляет упорядоченную коллекцию элементов, которая позволяет дублирование элементов.
  • Set (множество): Представляет коллекцию уникальных элементов без определенного порядка. Не допускается наличие дублирующихся элементов.
  • Map (отображение): Представляет коллекцию пар ключ-значение, где каждый ключ является уникальным.
  • Mutable и Immutable коллекции: Kotlin предоставляет как изменяемые (mutable) коллекции, которые позволяют добавлять, изменять и удалять элементы, так и неизменяемые (immutable) коллекции, которые не позволяют изменять свое содержимое после создания.

Перегрузка операторов.

Перегрузка операторов - это возможность определения специального поведения операторов для пользовательских типов данных. В Kotlin можно переопределить стандартные операторы: +, -, *, /, ==, != для своих классов, чтобы позволить им выполнять операции более удобно. Хороший пример с координатной точкой, где в классе есть два своейства X-координата и Y-координата.

В Kotlin перегрузка операторов осуществляется путем определения специальных функций-членов в классе с использованием ключевого слова operator. Эти функции имеют специальные имена, соответствующие операторам, и определяют, каким образом должны выполняться операции для объектов данного класса. Например, для оператора + -> plus, для оператора * -> times.

Android.

Android Jetpack.

Android Jetpack - то набор бибилиотек и инструментов, созданный командой Google для упрощения разработки под Android. Jetpack-библиотеки используют пакет androidx. Библиотеки можно разделить на четыре вида:

  • UI - библиотеки, помогающие работать с визуалом приложения: fragment, layout, animation, - Jetpack Compose.
  • Architecture Components - библиотеки, используемые для построения архитектуры приложения, - LiveData, ViewModel, Room.
  • Foundation - базовые библиотеки, которые помогают уменьшить шаблонный код, - AppCompact.
  • Behavior - библиотеки-обертки для функциональности, предоставляемой Android SDK, созданы для улучшения стандартного Android API, - Permissions, Notifications.

Android Manifest.

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

Основные аспекты, которые можно задать в файле Android Manifest:

  1. Название пакета: Каждое приложение в Android имеет уникальное имя пакета, которое идентифицирует его в системе. Оно должно быть уникальным в пределах устройства. Название пакета определяется с помощью элемента <manifest> и атрибута <package>.
  2. Версия приложения: Версия приложения определяется с помощью элемента <manifest> и атрибутов android:versionCode и android:versionName. android:versionCode - это числовое значение, которое увеличивается с каждым выпуском новой версии приложения и используется для определения версии программного обеспечения. android:versionName - это строковое значение, предназначенное для отображения пользователю.
  3. Разрешения: В манифесте можно указать различные разрешения, необходимые для работы приложения. Например, доступ к интернету, камере, контактам, геолокации и т.д. Разрешения задаются с помощью элемента <uses-permission>.
  4. Компоненты приложения: Манифест определяет различные компоненты приложения, такие как активности (activities), сервисы (services), приемники (broadcast receivers) и поставщики контента (content providers). Каждый компонент должен быть объявлен в манифесте, чтобы система могла их распознать и связать с другими компонентами приложения.
  5. Фильтры намерений (intent filters): Фильтры намерений позволяют приложению объявлять, какие типы намерений (intents) оно может обрабатывать. Например, приложение может заявить, что может обрабатывать намерение открытия изображения или намерение отправки текстового сообщения. Фильтры намерений определяются с помощью элементов <intent-filter>, которые вложены в определение компонента.
  6. Запуск приложения: Манифест позволяет указать, какой компонент приложения должен быть запущен по умолчанию при старте приложения. Это определяется с помощью элемента <activity> и атрибута android:name в сочетании с фильтром намерений.

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

Способы хранения данных.

Context.

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

Context является базовым классом для классов Activity, Service, Application и других компонентов Android.

Функции Context:

  1. Доступ к ресурсам.
  2. Запуск компонентов.
  3. Работа с файловой системой.
  4. Работа с базами данных.
  5. Взаимодействие со службами системы.
  6. Получение информации о приложении и устройстве.

Важно помнить, что Context является объектом, связанным с жизненным циклом компонента Android, и может быть уязвимым для утечек памяти, если не используется правильно.

Activity.

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

  • onCreate() - создание Activity, вызывается при первой инициализации.
  • onStart() - видимость Activity, отображается пользователю.
  • onResume() - готовность Activity, возможность пользователя взаимодействовать с Activity. Продолжает находиться в данном состоянии пока что-то не сменит фокус.
  • onPause() - приостановка Activity, отображается пользователю, нет возможности взаимодейстовать.
  • onStop() - отсутсвие видимости Activity.
  • onDestroy() - вызывается перед уничтожением Activity.

Fragment.

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

  • onAttach() - прикрепление фрагмента к Activity.
  • onCreateView() - создание компонентов внутри фрагмента.
  • onViewCreated() - вызов сразу после onCreateView, сообщение о созданом интерфейсе.
  • onDestroyView() - уничтожение интерфейса.
  • onDetach() - отвязка фрагмента от Activity.
  • onActivityCreated() - вызывается в момент отработки onCreate у Activity для доступа к компонентам Activity. Признан устаревшим.

Service.

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

  • Загрузка данных или файлов из Интернета.
  • Проигрывание музыки или видео.
  • Постоянный фоновый процесс - подсчет шагов и т.д.

Service в Android может быть реализован как Started Service (запущенный сервис) или Bound Service (связанный сервис).

Started Service запускается с помощью вызова startService() и продолжает выполняться, пока не будет явно остановлен или завершен системой. Он может быть использован для выполнения одноразовой операции, которая не требует непрерывного взаимодействия с пользователем.

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

Методы жизненного цикла StartedService:

  • startService() - вызов сервиса.
  • onCreate() - метод, который вызывается при создании сервиса - можно произвести всю нужную инициализацию.
  • onStartCommand() - метод, который вызывается сразу после метода onCreate() или startService() - сервис начинает работу и возвращает значение, указывающее, как система должна обработать сервис в случае его прерывания или восстановления.
  • onDestroy() - метод, который вызывается при завершении сервиса - можно обработать данные и произвести финальные действия.

Методы жизненного цикла BoundService:

  • bindService() - вызов сервиса.
  • onCreate() - метод, который вызывается при создании сервиса - можно произвести всю нужную инициализацию.
  • onBind() - метод, который вызывается после метода onCreate() - возвращает объект IBinder, который даёт возможность клиенту взаимодейтсовать с серисом.
  • onUnbind() - метод, который вызыватся при отвязки клиента от сервиса с помощью метода unbindService() - можно выполнить необходимые действия при отвзяке сервиса.
  • onDestroy() - метод, который вызывает при завершении сервиса - можно обработать данные и произвести финальные действия.

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

Для разных задач следует использовать разные Service.Например:
StartedService следует использовать для загрузки данных или обработка данных.
BoundService следует использовать для отправки данных или произведения музыки.

Broadcast Receiver.

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

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

  • Системные события - события, которые отправлены системой, например, изменение уровня заряда или изменение статуса сети.
  • События от других приложений - события, которые отправлены другими приложениями, например, приход нового SMS или изменение статуса другого приложения.
  • Свои пользовательские сообщения - события, которые можно определить в в своем приложении.

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

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

BroadcastReceiver в Android не имеет полноценного жизненного цикла, как у других компонентов, таких как активности или сервисы. Однако он все же проходит через несколько этапов при получении и обработке широковещательных сообщений. Рассмотрим эти этапы:

  1. Регистрация приемника - метод registerReceiver().
  2. Получение сообщений - метод onReceive() - вызывается в главном потоке, поэтому не должен содержать длительных операций.
  3. Обработка сообщений - метод onReceive() - также соджержит логику обработки полученных сообщений.
  4. Отмена регистрации - метод unregisterReceiver().

Content Provider.

Content Provider - это один из основных компонентов в Android, который позволяет приложениям обмениваться данными и предоставлять доступ к данным других приложений. Данный компомпонет позволяет безопасно обращаться и работать с данными между приложениями. Поставщики контента необходимо регистрировать в AndroidManifest.

Content Provider обеспечивает структурированный интерфейс для работы с данными, подобно базе данных. Он позволяет другим приложениям запрашивать, изменять и даже удалять данные, предоставляемые Content Provider. Кроме того, Content Provider может обрабатывать запросы на доступ к данным от других приложений и предоставлять им данные через URI (Uniform Resource Identifier).

Преимущества использования Content Provider:

  1. Общий доступ к данным: Content Provider позволяет приложениям предоставлять общий доступ к данным, что способствует интеграции и взаимодействию различных приложений. Например, контактное приложение может предоставить Content Provider, чтобы другие приложения могли получить доступ к контактам.
  2. Безопасность: Content Provider обеспечивает контролируемый доступ к данным, определяя разрешения доступа для других приложений. Таким образом, приложение может контролировать, какие данные могут быть доступны для чтения или записи.
  3. Абстракция данных: Content Provider позволяет скрыть сложность фактического источника данных, предоставляя абстракцию данных приложения. Это означает, что данные могут быть хранены в базе данных, файле или удаленном сервере, но для других приложений это несущественно, так как они получают доступ к данным через Content Provider.
  4. Использование URI: Content Provider использует URI для идентификации данных. URI определяет конкретный набор данных и позволяет другим приложениям обращаться к ним. URI может быть использован для запроса, вставки, обновления и удаления данных.

Для создания Content Provider в Android необходимо создать класс, наследующий класс ContentProvider и переопределить ряд методов, таких как query(), insert(), update() и delete(), которые определяют, какие операции доступны для работы с данными.

В операционной системе Android предустановлены несколько Content Provider'ов, которые предоставляют доступ к различным видам данных. Некоторые из наиболее распространенных Content Provider'ов включают:

  • ContactsContract: Content Provider для работы с контактами и информацией о контактах, такой как имена, номера телефонов, адреса электронной почты и другие связанные данные.
  • MediaStore: Content Provider для доступа к медиафайлам, таким как изображения, видео и аудиофайлы. Можно получить доступ к галерее изображений, альбомам, музыкальным файлам и другим медиа-ресурсам.
  • SettingsProvider: Content Provider, предоставляющий доступ к системным настройкам устройства, таким как звуковые сигналы, язык, блокировка экрана и другие настройки.
  • BrowserProvider: Content Provider, позволяющий получить доступ к закладкам, истории и другим данным браузера на устройстве.

Content Provider является мощным механизмом для обмена данными между приложениями в Android и обеспечивает гибкую и безопасную интеграцию данных.

ViewModel.

ViewModel - это компонент, который отвечает за подготовку и управление данными для Activity или Fragment. Он также обрабатывает взаимодействие между Activity/Fragment с остальной частью приложения, например с классами бизнес-логики.
Жизненный цикл ViewModel очень простой: жив ViewModelScope и метод onCleared(). Именно поэтому данный компонент идеально подходит для управлением данными для View.

LiveData.

LiveData - это класс, который хранит в себе данные и реализовывает паттерн Observer, т.е. LiveData позволяет обновлять данные и автоматически уведомлять подписчиков об изменениях.
В отличие от обычных данных, LiveData учитывает жизненный цикл владельца, т.е. это означает, что LiveData учитывает жизненный цикл компонента приложения, к которому была привязана, например Activity, Fragment, Service, ViewModel.

StateFlow и MutableStateFlow.

StateFlow и MutableStateFlow - это классы из библиотеки Kotlin Flows, предоставляющие удобный способ работы с изменяемым состоянием и реактивным потоком данных.

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

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

Kotlin Flows.

Kotlin Flows - это новая функциональность в языке программирования Kotlin и в библиотеке Kotlin Coroutines, предоставляющая асинхронный и реактивный поток данных. Kotlin Flows позволяют управлять потоком данных с помощью корутин и предоставляют альтернативу RxJava для работы с реактивными потоками.
Kotlin Flows поддерживают два типа потоков - горячие (hot) и холодные (cold).

  • Горячие потоки (hot flows) - это потоки данных, которые активны независимо от того, есть ли подписчики на них или нет. Горячие потоки начинают генерировать данные, как только они созданы, и продолжают генерировать данные независимо от наличия подписчиков. Если подписчик подключается к горячему потоку, он начинает получать данные с текущего момента. Примером горячего потока является бесконечный поток данных, такой как события сенсора или поток событий из внешнего источника данных.
  • Холодные потоки (cold flows) - это потоки данных, которые начинают генерировать данные только после подключения подписчика. Каждый подписчик на холодный поток будет получать данные независимо друг от друга. Если один из подписчиков отключается, это не влияет на других подписчиков.

ViewBinding и DataBinding.

ViewBinding и DataBinding - это два различных подхода к связыванию пользовательского интерфейса (UI) с кодом в Android-приложениях. Они предоставляют удобные способы обращения к элементам пользовательского интерфейса без необходимости явного вызова метода findViewById().

Отличия между ViewBinding и DataBinding следующие:

  • ViewBinding:
    • ViewBinding является официальным механизмом, предоставляемым Android Jetpack.
    • При использовании ViewBinding для каждого разметочного файла (XML) автоматически генерируется связывающий класс (binding class), который содержит ссылки на все элементы пользовательского интерфейса, объявленные в этом файле.
    • Связывающий класс создается во время компиляции и доступен внутри активности или фрагмента для обращения к элементам пользовательского интерфейса.
    • Использование ViewBinding позволяет избежать ошибок, связанных с вызовом неправильных идентификаторов ресурсов или неправильного приведения типов.
    • ViewBinding предоставляет простой и эффективный способ работы с пользовательским интерфейсом без внедрения дополнительной логики.
  • DataBinding:
    • DataBinding также является официальным механизмом Android Jetpack.
    • DataBinding позволяет установить двустороннюю связь между элементами пользовательского интерфейса и данными в коде приложения.
    • При использовании DataBinding разметочные файлы (XML) становятся частью данных и могут быть связаны с объектами модели данных, называемыми "биндингами" (bindings).
    • Биндинги предоставляют доступ к данным и логике, связанной с пользовательским интерфейсом, внутри разметочных файлов.
    • DataBinding обеспечивает автоматическое обновление пользовательского интерфейса при изменении связанных данных, что упрощает разработку и поддержку динамического UI.
    • DataBinding также предоставляет возможность использовать выражения и логические операторы прямо в разметочных файлах для более гибкого управления пользовательским интерфейсом.

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

MVVM.

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

  • Model - представляет собой данные, которые необходимо отобразить пользователю. В большинстве приложений моделью выступает слой, отвественный за получение данных с бэкенда или базы данных.
  • View - отвечает за отображение данных. В большинстве приложений - это Activity/Fragment.
  • ViewModel - отвечает за соединение между View и Model. ViewModel общается с Model, а View подписана на ViewModel. При этом ViewModel ничего не знает о View и не имеет явной ссылки на View. Все подписки реализовываються через паттерн Observer.

MVC.

MVC - это архитектурный паттерн, который используется для разработки пользовательского интерфейса.
Model-View-Controller состоит из следующих частей:

  • Model - представляет собой данные, которые необходимо отобразить пользователю. В большинстве приложений моделью выступает слой, ответсвенный за получение данных с бэкенда или базы данных.
  • View - представляет собой слой, на котором происходит отображение данных. В основном в данном слое используется Activity/Fragment.
  • Controller - представляет собой слой, который ответственен за обработку запросов от пользователя и управлением потоком данных. Контроллер получает запросы от View, обрабатывает их, взаимодействует с данными в Model и отправляет их обратно во View.

MVP.

MVP - это архитектурный паттерн, который используется для разработки пользовательского интерфейса. MVP является вариацей MVC.
Model-View-Presenter состоит из следующих частей:

  • Model - слой, который представляет собой необхимые данные, с которыми будет взаимодействоать пользователь. Зачастую моделью выступает база данных или бэкенд.
  • View - слой, который используется для отображения данных пользователю. Зачастую представлением выступает Activity или Fragment.
  • Presenter - слой, который выступает посредником между Model и View. Он получает запросы от Представления, обрабатывает их, взаимодействует с Моделью для получения/обновления данных и обновляет Представление с новыми данными. Презентер также отвечает за обработку бизнес-логики и принятие решений относительно данных.

Coroutines.

Kotlin Coroutines - это технология, которая даёт возможность писать асинхронный код, так как будто он синхронный. Также корутины называют легковесными потоками.

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

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

Корутины имеют возможность начинаться в одном потоке и заканчиваться в другом.

Для создания корутин используются Корутин-Билдеры, которые являються extension-методами, которые могут быть вызваны на CoroutineScope:

  • launch - блок внутри метода ничего не может вернуть.
  • asyns - блок внутри метода может что-то вернуть.
  • withContext - блок запускает корутину и ожидает выполнения данной корутины; также даёт возможность изменить настройки CoroutineContext.

suspend-функция - это функция, которая может быть запущена для выполнения какого-то другого кода и возобновленна снова. Данные функции могут быть вызваны только из других таких же функций и из suspend-блока.
CoroutineScope - это объект, который определяет время жизни корутины, содержит ссылку на CoroutineContext и имеет методы запуска (launch, asyns, withContext). Например, можно создать viewModelScope, время жизни которой равно ViewModel.
CoroutineContext - это объект, который определяет где и как будет выполняться корутина. Каждый CoroutineContext состоит из следующих частей: Job, CoroutineDispatcher, CoroutineName, CoroutineExceptionHandler.

Job - это основной и обязательный объект, который контролирует и отображает выполнение асинхронного кода. Конкретную работу, которая выполняет корутина представляет именно объект Job.
CoroutineDispatcher - это объект, который определяет где и как будет выполняться корутина: Dispatcher.IO, Dispatcher.Main, Dispatcher.Default.
CoroutineName - это объект, который задаёт имя корутины. Необходим для правильной откладки приложения.
CoroutineExceptionHandler - это объект, который определяет, как будет обрабатываться ошибка.

Важно отметить, что все корутины возвращают объект Job. Также все корутины имеют иерархию.

Main Thread.

Main Thread - основной поток или UI поток, который является первым и основным. Данный поток отвечает за работу с UI. В основном потоке выполняются следующие задачи:

  • Обработка визуальных компонентов, например OnClick.
  • Работа с жизненными циклами компонентов, например Activity.
  • Обработки пользовательских и системных событий.

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

Main Thread может "выкинуть" следующие ошибки:

  • CRASH - аварийное завершение приложения, например при работе с null, делением на ноль и т.д.
  • ANR - Application Not Responding - приложение блокируется и перестает взаимодействовать с пользователем, например, если слишком много действий в основном потоке.

Looper.

Looper - это бесконечный цикл в основном потоке, который используется для получения событий от системы или пользователя (нажатие на экран, сообщения от системы и т.д.).
Благодаря Looper приложение корректно работает и не заканчивает своё выполнение сразу после запуска. Looper существует и работает до тех пор, пока система Android не уберет приложение из памяти.

Handler.

Handler - это класс, который создаёт, обрабатывает и отправляет сообщения для Looper. Каждый Handler привязывается к своёму одному Looper'у.


Основные методы Handler:

  • post() - позволяет выполнить какое-то действие в том потоке, к которому привязан Handler.
  • postDelay() -позволяет выполнить какое-то действие в том потоке, к которому привязан Handler, с "отложением" (т.е. например, через 1000 милисекунд).
  • removeCallbacks() - позволяет отменить все действия по токену, переданному в метод postDelayed.

Другое.

Реактивное программирование.

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

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

Основные понятия, связанные с реактивным программированием, включают следующие:

  1. Потоки данных (Streams): Поток данных представляет собой последовательность событий, которые могут быть произведены или потреблены компонентами системы. Это могут быть данные из внешних источников, пользовательские действия, события операционной системы и так далее.
  2. Подписка (Subscription): Компоненты системы могут подписываться на потоки данных, чтобы получать уведомления о новых событиях. Когда происходят изменения данных, эти изменения автоматически распространяются по подписчикам.
  3. Преобразования потоков (Stream Transformations): Реактивное программирование предлагает множество операторов и функций для преобразования потоков данных. Это может включать фильтрацию, сортировку, объединение, агрегацию и другие операции над потоками.
  4. Обработка ошибок (Error Handling): Реактивное программирование предоставляет механизмы для обработки ошибок в потоках данных. Ошибки могут быть обнаружены и переданы по цепочке потоков до обработчика ошибок.
  5. Задержки и буферизация (Backpressure): В реактивном программировании уделяется внимание задержкам и буферизации данных, чтобы справиться с асинхронностью и разницей в скорости потоков данных между компонентами системы.

RxJava 2.

RxJava 2 - это библиотека для реактивного программирования на платформе Java. Она предоставляет набор инструментов и операторов для работы с асинхронными потоками данных, такими как потоки событий, обратные вызовы и другие источники данных. Вот основные компоненты RxJava 2:

  1. Observable: Observable представляет собой источник данных, который генерирует и отправляет элементы в поток данных. Он может быть асинхронным и передавать элементы наблюдателям по мере их генерации.
  2. Observer: Observer подписывается на Observable и получает уведомления об элементах, отправленных Observable. Он определяет методы, такие как onNext(), onError() и onComplete(), которые вызываются соответствующим образом при получении элементов или ошибок.
  3. Subscription: Subscription представляет собой связь между Observable и Observer. Он позволяет Observer отказаться от подписки на Observable и остановить получение элементов.
  4. Operator: Операторы представляют собой функции, которые принимают Observable и преобразуют или комбинируют его элементы, создавая новый Observable.
  5. Scheduler: Scheduler определяет поток выполнения операций в RxJava. Он может управлять многопоточностью и позволяет выполнять операции на различных потоках, таких как пул потоков, основной поток пользовательского интерфейса и другие.
  6. Subject: Subject является одновременно и Observable, и Observer. Он может получать элементы от других источников и перенаправлять их своим наблюдателям.
  7. Disposable: Disposable представляет собой ресурс, который можно освободить. Он используется для отмены подписки на Observable и освобождения ресурсов, связанных с ней, например, для прерывания работы сетевого запроса.

Unit-тесты.

Unit-тесты — это тесты, которые проверяют функциональность отдельных компонентов программного обеспечения (например, функций, методов или классов) на уровне изоляции. Они являются частью практики разработки программного обеспечения, известной как тестирование единиц (Unit Testing).

Основные характеристики и преимущества unit-тестов:

  1. Изоляция: Unit-тесты выполняются в изолированной среде, в которой тестируется только конкретный компонент.
  2. Автоматизация: Unit-тесты можно автоматизировать, что позволяет выполнять их в любой момент и в любой среде.
  3. Документация: Набор хорошо написанных unit-тестов служит своего рода документацией для кода.
  4. Обнаружение ошибок: Unit-тесты позволяют обнаружить ошибки и дефекты в коде, даже если они кажутся незначительными.
  5. Улучшение проектирования: Написание unit-тестов заставляет разработчиков размышлять о дизайне компонентов и их зависимостях.

Практики и инструменты, связанные с unit-тестированием, включают:

  • Фреймворки для тестирования: На различных платформах доступны фреймворки, которые облегчают написание и выполнение unit-тестов.
  • Mock-объекты: Моки (mock-объекты) используются для замены реальных зависимостей во время unit-тестирования. Они позволяют симулировать поведение зависимостей и контролировать входные и выходные данные для более точного тестирования.
  • Code Coverage: Измерение покрытия кода unit-тестами позволяет определить, какой процент кода был выполнен во время тестирования.

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

UI-тесты.

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

Вот некоторые ключевые аспекты и инструменты, связанные с UI-тестированием в Android:

  • Espresso: Espresso - это фреймворк для UI-тестирования Android-приложений, предоставляемый Google. Он предлагает удобный и выразительный API для написания UI-тестов, которые эмулируют действия пользователя и проверяют состояние пользовательского интерфейса.
  • Действия и проверки: Espresso предоставляет набор действий, таких как нажатие кнопки, ввод текста, прокрутка и другие, которые можно выполнить на элементах пользовательского интерфейса. Он также предлагает проверки, которые позволяют проверить состояние элементов интерфейса или проверить результаты операций.
  • Идентификация элементов: Для взаимодействия с элементами пользовательского интерфейса в UI-тестах необходимо уметь идентифицировать эти элементы. Espresso предлагает различные методы для поиска элементов по их идентификатору, тексту, содержимому и другим атрибутам.
  • Автоматизация сценариев: С помощью Espresso можно автоматизировать сценарии пользовательского взаимодействия, такие как вход в приложение, переход по различным экранам, взаимодействие с различными элементами интерфейса и проверка ожидаемых результатов.
  • Инструменты для записи и воспроизведения: Android Studio предлагает инструменты для записи действий пользователя и их последующего воспроизведения в виде UI Automator и Espresso Test Recorder. Это упрощает начало работы с UI-тестированием и генерирует код теста на основе пользовательских действий.

Dependency Injection

Dependency Injection - это паттерн проектирования, который используется для управления зависимостями между объектами. Он предоставляет способ внедрения зависимостей в объекты вместо того, чтобы позволять им создавать зависимости самостоятельно.

В DI объекты не создают свои зависимости напрямую, а вместо этого получают их из внешнего источника, называемого контейнером DI. Контейнер DI отвечает за создание и управление зависимостями, предоставляя объектам те зависимости, которые им необходимы.

Преимущества использования DI включают:

  1. Упрощение тестирования.
  2. Увеличение гибкости.
  3. Улучшение переиспользования кода.
  4. Улучшение расширяемости.

DI может быть реализован вручную или с помощью фреймворков и библиотек, предоставляющих поддержку DI, например Dagger2, Koin.

Основные компоненты DI включают:

  1. Зависимости (Dependencies): Зависимости представляют объекты, которые нужны другим объектам для их функционирования.
  2. Контейнер DI (DI Container): Контейнер DI - это компонент, который управляет жизненным циклом объектов и их зависимостей.
  3. Инъекция зависимостей (Dependency Injection): Инъекция зависимостей - это процесс передачи зависимостей из контейнера DI в объекты.

В целом, DI способствует созданию более гибких, тестируемых и расширяемых систем путем управления зависимостями между объектами.

Dagger 2 / Hilt 2.

Hilt 2 (Dagger Hilt 2) - это фреймворк для внедрения зависимостей (Dependency Injection) для языка программирования Kotlin, разработанный на основе библиотеки Dagger. Hilt предоставляет упрощенный и более декларативный способ работы с инверсией управления и внедрением зависимостей в Android-приложениях.

Основные компоненты и концепции Hilt 2:

  1. Аннотации: Hilt использует набор аннотаций, которые добавляются к классам и методам для определения контейнеров зависимостей и способа их внедрения. Некоторые из ключевых аннотаций включают @HiltAndroidApp, @AndroidEntryPoint, @Inject, @Provides и другие.
  2. Контейнеры зависимостей: Hilt автоматически создает и управляет контейнерами зависимостей для вас. Он предоставляет компоненты, которые отвечают за создание и предоставление экземпляров зависимостей. Например, компонент @Singleton предоставляет синглтон-экземпляры, а компонент @ActivityScoped предоставляет экземпляры, жизненный цикл которых связан с активностью.
  3. Граф зависимостей: Hilt строит граф зависимостей автоматически на основе аннотаций и резолвит зависимости на основе их типа и области видимости. Это позволяет легко предоставлять зависимости внутри приложения и обеспечивает их правильный жизненный цикл.
  4. Тестирование: Hilt предоставляет поддержку для тестирования зависимостей и Android-компонентов. Он упрощает создание и внедрение фиктивных (mock) или поддельных (stub) зависимостей в юнит-тестах и инструментальных тестах.

Hilt является официальным рекомендуемым фреймворком от команды разработчиков Android. Он строится на основе мощного фреймворка Dagger, упрощая его использование и интеграцию с Android-платформой.

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

Вопросы работодателю.

Проект.

При данном вопросе необходимо спросить про технологический стэк проекта. Задать вопросы про используемые технологии, библиотеки, архитектуру.
Узнать про сроки ставящихся задач.

Идеальный кандидат.

При данном вопросе узнать, какого кандидата ищет команда: что безумно важно, что разработчик критично должен иметь, а что нет.

Испытательный срок.

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

Команда.

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