Kotlin | Вопросы собесов – Telegram
Kotlin | Вопросы собесов
2.52K subscribers
28 photos
1.04K links
Download Telegram
🤔 Что такое Dagger?

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1💊1
🤔 Какие стратегии бранчинга знаешь, расскажи про их плюсы и минусы?

Бранчинг (ветвление) — это способ управления кодом в Git, когда разработчики работают в отдельных ветках (branches).
Основные стратегии бранчинга
Git Flow
GitHub Flow
GitLab Flow
Trunk-Based Development

🚩Git Flow – классическая модель с `develop` и `release`

Основные ветки:
main (стабильная версия, релизы).
develop (основная ветка разработки).
Временные ветки:
feature/* (новые фичи, мерджатся в develop).
release/* (готовится релиз, тестирование, фикс багов).
hotfix/* (критические фиксы в main).
Схема Git Flow:
main ──── hotfix ─▶️ merge ────▶️ main

├── develop ─▶️ release ─▶️ merge ─▶️ main
│ │
├── feature/1
├── feature/2


🚩GitHub Flow – упрощённый процесс для CI/CD

Только две основные ветки:
main (всегда стабильная версия).
Фичи разрабатываются в feature/* и сразу мерджатся в main.
Деплой возможен сразу после мерджа в main.
Схема GitHub Flow
main ────▶️ feature/1 ─▶️ merge ─▶️ main ─▶️ deploy
└── feature/2 ─▶️ merge ─▶️ main ─▶️ deploy


🚩GitLab Flow – баланс между Git Flow и GitHub Flow

main – стабильная ветка (готовая к продакшену).
develop (опционально) – если нужно тестирование перед main.
feature/* – для разработки новых фич.
production, staging – если нужно разделение сред.
hotfix/* – фиксы продакшена.
main ────▶️ production

├── staging ───▶️ merge ─▶️ main

├── feature/1 ─▶️ merge ─▶️ staging
├── feature/2 ─▶️ merge ─▶️ staging


🚩Trunk-Based Development – одна ветка (`main`)

Разработчики работают прямо в main, без feature/* веток.
- Коммиты в main маленькие и частые.
- Используются Feature Flags (фичи включаются/выключаются динамически).
Схема Trunk-Based
main ────▶️ commit ─▶️ commit ─▶️ commit ─▶️ deploy


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2💊1
🤔 Какие есть аналоги библиотеки Hilt?

Основные альтернативы — Dagger (ручная конфигурация), Koin (написан на Kotlin, декларативный), Kodein (устаревающий), Service Locator. Выбор зависит от предпочтений и архитектуры проекта.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1💊1
🤔 Расскажи про ключевое слово Object в Kotlin?

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

🟠Объявление объекта-одиночки (Singleton)
Kotlin предоставляет встроенную поддержку для создания singleton-объектов. Это объект, который имеет единственный экземпляр в приложении.
object Database {
val name = "MainDatabase"
fun connect() {
println("Подключение к базе данных $name")
}
}

fun main() {
Database.connect() // Подключение к базе данных MainDatabase
}


🟠Анонимные объекты
Ключевое слово object может использоваться для создания анонимных объектов (объектов без имени). Это полезно, если нужно создать временный объект или реализовать интерфейс/абстрактный класс.
fun main() {
val listener = object : ClickListener {
override fun onClick() {
println("Кнопка нажата")
}
}
listener.onClick()
}

interface ClickListener {
fun onClick()
}


🟠Компаньон-объекты (Companion Object)
Ключевое слово object можно использовать внутри класса для объявления компаньон-объекта. Это позволяет создавать статические методы и переменные в классе.
class User(val name: String) {
companion object {
fun createGuest() = User("Guest")
}
}

fun main() {
val guest = User.createGuest()
println(guest.name) // Guest
}


🟠Объект-выражение
Ключевое слово object может использоваться для объявления объектов в коде прямо "на месте".
val myObject = object {
val x = 10
fun printX() {
println(x)
}
}

fun main() {
myObject.printX() // 10
}


🚩Почему `object` так полезен?

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

🟠Потокобезопасность
В случае object, компилятор Kotlin автоматически гарантирует, что объект создаётся в потокобезопасном режиме.

🟠Гибкость
object можно использовать практически везде: глобально, локально, внутри классов и функций.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Почему происходят гонки потоков и как с этим бороться?

Гонки происходят, когда два потока одновременно обращаются к общим данным без синхронизации. Решения:
- synchronized,
- ReentrantLock,
- Atomic-типы,
- volatile — только если нужна гарантия видимости, но не атомарности.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Расскажи про коллекцию Map

Map – это коллекция пар ключ-значение. Каждый ключ уникален, а значения могут повторяться. В Kotlin Map не наследуется от Collection, но является частью стандартных коллекций.

🟠Виды `Map` в Kotlin
В Kotlin есть два основных типа Map:
Map (неизменяемая) – нельзя добавлять/удалять элементы после создания.
MutableMap (изменяемая) – можно добавлять, удалять и изменять элементы.

Создаётся с помощью mapOf()
val users = mapOf(1 to "Alice", 2 to "Bob", 3 to "Charlie")
println(users[1]) // Выведет: Alice


Создаётся с помощью mutableMapOf()
val users = mutableMapOf(1 to "Alice", 2 to "Bob")
users[3] = "Charlie" // Добавляем элемент
users[1] = "Alex" // Изменяем значение по ключу
users.remove(2) // Удаляем элемент
println(users) // {1=Alex, 3=Charlie}


🟠Основные операции с `Map`
map[key] – получить значение (или null, если ключа нет).
map.getValue(key) – получить значение (или исключение, если ключа нет).
map.getOrDefault(key, defaultValue) – вернуть значение или defaultValue, если ключа нет.
map.getOrElse(key) { default } – если ключа нет, выполнить лямбда-выражение.
val users = mapOf(1 to "Alice", 2 to "Bob")
println(users[1]) // Alice
println(users.getOrDefault(3, "Unknown")) // Unknown


🟠Проверка наличия ключей и значений
map.containsKey(key) – есть ли ключ?
map.containsValue(value) – есть ли значение?
println(users.containsKey(2))  // true
println(users.containsValue("Charlie")) // false


Перебор элементов
for ((key, value) in users) {
println("ID: $key, Name: $value")
}


Фильтрация Map
val filtered = users.filter { (key, value) -> key % 2 == 0 }
println(filtered) // {2=Bob}


🚩Особенности `Map` в Kotlin

🟠Порядок элементов
mapOf() – не гарантирует порядок. linkedMapOf() – сохраняет порядок добавления.
🟠Производительность
Обычный HashMap (mutableMapOf()) – O(1) для поиска по ключу. TreeMapO(log n), но поддерживает сортировку по ключам.

val sortedUsers = sortedMapOf(3 to "Charlie", 1 to "Alice", 2 to "Bob")
println(sortedUsers) // {1=Alice, 2=Bob, 3=Charlie}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Фреймворк Dagger

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Расскажи про инициализаторы в классах в Kotlin

В Kotlin инициализаторы используются для выполнения кода при создании экземпляра класса.
Первичный (primary) конструктор
Вторичные (secondary) конструкторы
Инициализационный блок (init)

🟠Первичный конструктор
Это основной способ инициализации класса в Kotlin. Он объявляется в заголовке класса.
class User(val name: String, val age: Int)


Если нужно выполнить дополнительную логику во время создания объекта, используют блок init:
class User(val name: String, val age: Int) {
init {
println("Создан пользователь: $name, возраст: $age")
}
}


🟠Вторичные конструкторы
Они объявляются с помощью constructor и нужны, если:
Нужно несколько способов создания объекта.
Надо вызвать другой конструктор (this(...)).
class User {
var name: String
var age: Int

constructor(name: String) {
this.name = name
this.age = 18 // Значение по умолчанию
}

constructor(name: String, age: Int) {
this.name = name
this.age = age
}
}


Можно создать объект так
val user1 = User("Алекс")   // возраст будет 18  
val user2 = User("Иван", 25)


Если есть первичный конструктор, вторичный должен его вызывать через this(...)
class User(val name: String, val age: Int) {
constructor(name: String) : this(name, 18)
}


🟠`init` vs вторичный конструктор
init выполняется всегда при вызове первичного конструктора.
Вторичный конструктор создаёт альтернативный способ создания объекта.
class User(val name: String, val age: Int) {
init {
println("Создан пользователь: $name, возраст: $age")
}

constructor(name: String) : this(name, 18) {
println("Вызван вторичный конструктор")
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 В Android существуют мэпы, в которые можно положить примитивные типы?

Да, существуют специализированные реализации мэп, оптимизированные под примитивы, например:
- SparseIntArray — аналог Map<Int, Int>, но без обёртки Integer.
- SparseBooleanArray, SparseArray<T> и другие.
Они используются для экономии памяти и производительности, избегая автоупаковки (autoboxing).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
🤔 Чем отличаются data-классы и sealed-классы?

data class — класс для хранения данных с автогенерацией equals(), hashCode(), copy().
sealed class — ограниченная иерархия классов, используется для when.

🚩`data class` – для хранения данных

data class автоматически создаёт:
equals() и hashCode() → сравнение объектов по значениям.
copy() → удобное копирование с изменением параметров.
toString() → красивый вывод.
data class User(val id: Int, val name: String)

fun main() {
val user1 = User(1, "Alice")
val user2 = user1.copy(name = "Bob") // Создаём копию с новым именем

println(user1) // User(id=1, name=Alice)
println(user2) // User(id=1, name=Bob)
}


🚩`sealed class` – для ограниченных иерархий классов

sealed class используется, когда есть фиксированное число подклассов.
sealed class NetworkState {
object Loading : NetworkState()
data class Success(val data: String) : NetworkState()
data class Error(val message: String) : NetworkState()
}

fun handleState(state: NetworkState) {
when (state) {
is NetworkState.Loading -> println("Загрузка...")
is NetworkState.Success -> println("Данные: ${state.data}")
is NetworkState.Error -> println("Ошибка: ${state.message}")
}
}


🚩Можно ли использовать `data class` внутри `sealed class`?

Да! Это лучший вариант для управления состояниями:
sealed class Result {
object Loading : Result()
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 В чем разница между LinkedList и ArrayList?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 В каких случаях наследование полезно, а в каких нет?

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

🚩Когда наследование полезно

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

   open class Animal(val name: String) {
fun eat() {
println("$name ест.")
}
}

class Dog(name: String) : Animal(name) {
fun bark() {
println("$name лает.")
}
}

class Cat(name: String) : Animal(name) {
fun meow() {
println("$name мяукает.")
}
}

// Использование:
val dog = Dog("Бобик")
dog.eat() // "Бобик ест."
dog.bark() // "Бобик лает."


🟠Для создания иерархий классов (например, типологии)
Наследование отлично подходит для задач, связанных с созданием деревьев типов или категорий. Например, у вас есть базовый класс Shape (Форма), и от него наследуются конкретные фигуры: Circle, Rectangle, Triangle.

   open class Shape {
open fun draw() {
println("Рисуется форма.")
}
}

class Circle : Shape() {
override fun draw() {
println("Рисуется круг.")
}
}

class Rectangle : Shape() {
override fun draw() {
println("Рисуется прямоугольник.")
}
}

// Использование:
val shapes: List<Shape> = listOf(Circle(), Rectangle())
for (shape in shapes) {
shape.draw()
}


🟠Для использования полиморфизма
Полиморфизм позволяет использовать родительские классы для работы с объектами наследников. Это полезно, если у вас есть методы, которые работают с разными типами объектов, но с единым интерфейсом. Вызов методов draw() у всех объектов, не задумываясь об их конкретном типе, благодаря полиморфизму.

   fun render(shape: Shape) {
shape.draw()
}

render(Circle()) // "Рисуется круг."
render(Rectangle()) // "Рисуется прямоугольник."


🟠Когда концептуально существует отношение "is-a" (является)
Если наследник действительно является вариантом (подтипом) родительского класса, наследование логично. Например: Собака является животным (Dog is an Animal). Прямоугольник является фигурой (Rectangle is a Shape).

🚩Когда наследование не полезно

🟠Когда отношение "is-a" отсутствует
Если объекты не имеют строгого отношения "является", наследование использовать нельзя. Например: Класс Car (Машина) и Boat (Лодка) лучше объединить через общие свойства (интерфейсы или композицию), чем делать лодку наследником машины.

   open class Bird {
open fun fly() {
println("Птица летит.")
}
}

class Penguin : Bird() {
override fun fly() {
throw UnsupportedOperationException("Пингвин не умеет летать!")
}
}


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

   class Car : Engine, Wheels

Используйте композицию:

   class Car {
private val engine = Engine()
private val wheels = Wheels()
}


🟠Сложные иерархии классов
Если вы создаете слишком глубокие или разветвленные иерархии, код становится сложным для понимания, тестирования и изменения. Например, изменение в базовом классе может неожиданно затронуть всех наследников.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что такое Garbage Collector Roots?

Garbage Collector Roots в контексте JVM (Java Virtual Machine) — это набор объектов, которые служат отправными точками для сборщика мусора при анализе достижимых объектов. К ним относятся активные потоки, статические поля классов, локальные переменные и активные блоки Java-кода.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Что при создании классов в Kotlin изменились по сравнению с Java?

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

🚩Объявление классов и конструкторов

В Kotlin объявление классов и их конструкторов значительно упрощено.
В Java
public class Person {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}


В Kotlin
class Person(val name: String, val age: Int)


🟠Статические члены
В Java для объявления статических членов используется ключевое слово static. В Kotlin вместо этого используются companion object.
В Java
public class MyClass {
public static final String CONSTANT = "constant";

public static void staticMethod() {
// Some code
}
}


В Kotlin
class MyClass {
companion object {
const val CONSTANT = "constant"

@JvmStatic
fun staticMethod() {
// Some code
}
}
}


🟠Data классы
Kotlin предоставляет специальный тип классов — data классы, которые автоматически генерируют методы equals(), hashCode(), toString(), copy(), и componentN().
В Java
public class User {
private String name;
private int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public boolean equals(Object o) {
// Implementation
}

@Override
public int hashCode() {
// Implementation
}

@Override
public String toString() {
// Implementation
}
}


В Kotlin
data class User(val name: String, val age: Int)


🟠Свойства и методы доступа
В Kotlin свойства объявляются напрямую, и методы доступа (геттеры и сеттеры) генерируются автоматически.
В Java
public class Rectangle {
private int width;
private int height;

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}

public int getHeight() {
return height;
}

public void setHeight(int height) {
this.height = height;
}
}


В Kotlin
class Rectangle(var width: Int, var height: Int)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Отличие исключений Kotlin / Java?

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Как с MVI обрабатывать события, которые не нужно хранить?

В MVI (Model-View-Intent) есть проблема: события, которые не нужно хранить в State, могут повторно отобразиться при пересоздании экрана (например, при повороте экрана или навигации назад).
Показ Toast / Snackbar
Навигация (openScreen())
Ошибки, которые нужно показать один раз

🟠Используем `SingleLiveEvent` (для ViewModel)
Это LiveData, которая отправляет событие только один раз и не пересылает его новым подписчикам.
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)

override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner) { value ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(value)
}
}
}

override fun setValue(value: T?) {
pending.set(true)
super.setValue(value)
}
}


ViewModel с SingleLiveEvent
class MyViewModel : ViewModel() {
val eventShowToast = SingleLiveEvent<String>()

fun onButtonClicked() {
eventShowToast.value = "Привет, это Toast!"
}
}


Activity/Fragment подписывается
viewModel.eventShowToast.observe(viewLifecycleOwner) { message ->
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}


🟠Используем `SharedFlow` (современный подход)
В StateFlow все события хранятся в State (что не подходит для одноразовых событий).
Вместо этого используем SharedFlow с replay = 0, чтобы событие не повторялось.

ViewModel с SharedFlow
class MyViewModel : ViewModel() {
private val _events = MutableSharedFlow<UiEvent>()
val events = _events.asSharedFlow()

fun onButtonClicked() {
viewModelScope.launch {
_events.emit(UiEvent.ShowToast("Привет, это Toast!"))
}
}
}

sealed class UiEvent {
data class ShowToast(val message: String) : UiEvent()
object NavigateToNextScreen : UiEvent()
}


Подписка в UI:
lifecycleScope.launchWhenStarted {
viewModel.events.collect { event ->
when (event) {
is UiEvent.ShowToast -> Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
is UiEvent.NavigateToNextScreen -> findNavController().navigate(R.id.nextFragment)
}
}
}


🟠Используем `EventWrapper` (обходной путь для StateFlow)
Если в State всё-таки нужно хранить событие, но не показывать его повторно, можно использовать EventWrapper.
class EventWrapper<out T>(private val content: T) {
private var hasBeenHandled = false

fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) null else {
hasBeenHandled = true
content
}
}
}


ViewModel с StateFlow:
data class UiState(val message: EventWrapper<String>? = null)

class MyViewModel : ViewModel() {
private val _state = MutableStateFlow(UiState())
val state = _state.asStateFlow()

fun onButtonClicked() {
_state.value = UiState(message = EventWrapper("Привет, это Toast!"))
}
}


UI подписывается и проверяет EventWrapper:
viewModel.state.collect { state ->
state.message?.getContentIfNotHandled()?.let { message ->
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Можно ли что-то из компонентов не описывать в Manifest?

Да, все компоненты, которые не должны быть доступны извне, могут не указываться, особенно:
- Activity, используемая только внутри;
- BroadcastReceiver или Service, регистрируемые динамически;
- ContentProvider — если не требуется внешнего доступа.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1🤔1
🤔 Как подключить BroadcastReceiver, чтобы он смог получать сообщения?

Подключение BroadcastReceiver в Android состоит из двух основных шагов: создание самого ресивера и его регистрация. Ресивер можно зарегистрировать как статически в манифесте, так и динамически в коде.

🚩Создание BroadcastReceiver

Создадим простой BroadcastReceiver, который будет реагировать на определённое событие, например, на получение SMS.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Сообщение получено!", Toast.LENGTH_SHORT).show();
}
}


🚩Регистрация ресивера

🟠Статическая регистрация в манифесте
Можно зарегистрировать ресивер в файле AndroidManifest.xml. Это удобно, когда вы хотите, чтобы ресивер всегда был активен и слушал определённые системные события, например, перезагрузку устройства или получение SMS.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">

<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>

</application>

</manifest>


🟠Динамическая регистрация в коде
Иногда нужно регистрировать ресивер только на время выполнения определённой активности или службы. В этом случае лучше использовать динамическую регистрацию.
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
private MyBroadcastReceiver myBroadcastReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myBroadcastReceiver = new MyBroadcastReceiver();
}

@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(myBroadcastReceiver, filter);
}

@Override
protected void onStop() {
super.onStop();
unregisterReceiver(myBroadcastReceiver);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 В чём основной плюс MVVM?

Главный плюс MVVM — разделение обязанностей:
- UI (View) отделён от логики (ViewModel);
- бизнес-логика тестируется отдельно;
- упрощает масштабирование и поддержку;
- особенно эффективно в Android с LiveData, StateFlow, DataBinding.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1🤔1
🤔 Расскажи подробно, что из себя представляет каждый компонент Аndroid-приложения?

Android-приложение состоит из четырёх основных компонентов:
Activity – UI-экран приложения.
Service – фоновая работа без UI.
BroadcastReceiver – слушает системные и пользовательские события.
ContentProvider – делится данными между приложениями.

🚩`Activity` – экран приложения (UI)

Отображает интерфейс пользователя.
Обрабатывает взаимодействие (нажатия, свайпы, ввод текста).
Управляется системой через жизненный цикл (Lifecycle).
Пример Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // Загружаем XML-разметку
}
}


🚩`Service` – фоновая работа без UI

Выполняет длительные фоновые задачи (музыка, загрузка файлов).
Может работать даже если приложение закрыто.
НЕ имеет UI (не рисует View).
Пример Service
class MyService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Thread {
// Долгая фоновая задача
}.start()
return START_STICKY
}

override fun onBind(intent: Intent?): IBinder? = null
}


Запуск сервиса
startService(Intent(this, MyService::class.java))


Остановка сервиса
stopService(Intent(this, MyService::class.java))


🚩`BroadcastReceiver` – обработка событий (системных и пользовательских)

- Получает события системы (BOOT_COMPLETED, BATTERY_LOW).
- Слушает события других приложений.
- Может быть динамическим (через код) или статическим (AndroidManifest.xml).
Пример BroadcastReceiver (отключение Wi-Fi)
class NetworkReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
println("Сеть изменилась!")
}
}
}


Регистрируем BroadcastReceiver в AndroidManifest.xml
<receiver android:name=".NetworkReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>


Можно зарегистрировать динамически в коде
val receiver = NetworkReceiver()
registerReceiver(receiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))


🚩`ContentProvider` – обмен данными между приложениями

- Дает доступ к данным *другим приложениям.
- Позволяет безопасно работать с БД (Room, SQLite).
- Используется для Contacts, MediaStore, Calendar.

Пример ContentProvider (чтение контактов)
val cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)
cursor?.use {
while (it.moveToNext()) {
val name = it.getString(it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
println("Контакт: $name")
}
}


Запрос данных через Uri
val uri = Uri.parse("content://com.example.provider/table")
contentResolver.query(uri, null, null, null, null)


🚩Как компоненты взаимодействуют между собой?

Пример: Activity запускает Service через Intent
startService(Intent(this, MyService::class.java))

Пример: Service отправляет Broadcast, а Receiver его ловит
sendBroadcast(Intent("com.example.CUSTOM_EVENT"))


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Зачем при повороте экрана пересоздаётся Activity?

Поворот экрана вызывает изменение конфигурации (orientation, screen size). Android по умолчанию пересоздаёт Activity, чтобы загрузить соответствующий ресурсный файл и адаптировать интерфейс.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4