- FragmentManager управляет фрагментами: добавление, поиск, стек возврата.
- FragmentTransaction используется для выполнения операций с фрагментами (добавление, замена, удаление). Это как "транзакция базы данных", применяемая к UI.
Transaction создаётся через FragmentManager.beginTransaction() и применяется через commit().
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2💊2
В Java есть 4 модификатора доступа, которые контролируют видимость полей, методов и классов:
Поля почти всегда должны быть
private, чтобы инкапсулировать данные. Методы тоже лучше делать private, если они не должны использоваться извне. public class User {
private String name; // ✅ Доступен только внутри класса
public User(String name) {
this.name = name;
}
private void logUserAction() { // ✅ Только внутри класса
System.out.println(name + " совершил действие");
}
}Используется, если класс не должен быть доступен за пределами пакета. Полезно для вспомогательных классов.
class FileHelper { // ✅ Доступен только в этом пакете
static void readFile(String path) {
System.out.println("Читаем файл: " + path);
}
}protected лучше использовать только в abstract классах и для методов, которые должны быть переопределены. Не используй protected для полей → нарушает инкапсуляцию! abstract class Animal {
protected void makeSound() { // ✅ Наследники могут переопределять
System.out.println("Какой-то звук");
}
}
class Dog extends Animal {
@Override
protected void makeSound() {
System.out.println("Гав-гав!");
}
}Класс можно делать
public, только если он должен быть доступен во всех пакетах. Не делай поля public → нарушает инкапсуляцию! public class UserManager { // ✅ Доступен везде
public void createUser() { // ✅ Можно вызывать в любом месте
System.out.println("Создание пользователя...");
}
}final class → класс нельзя наследовать. final method → метод нельзя переопределить. final field → переменная неизменяема (константа). public final class MathUtils {
public static final double PI = 3.14159;
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
- Компоненты — это точки входа в граф зависимостей. Через них получаем зависимости и связываем граф с приложением.
- Модули — классы, предоставляющие зависимости через
Они позволяют:
- Разделять зависимости по слоям (data, domain, UI).
- Управлять временем жизни объектов.
- Повторно использовать код в разных частях приложения.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Сборщик мусора (Garbage Collector, GC) в Android (и в JVM) использует анализ ссылок для определения, можно ли уничтожить объект.
GC работает по принципу "сборки мусора с поиском корней" (Tracing Garbage Collection).
GC ищет "корневые" объекты (Root Objects) – это объекты, к которым точно есть ссылка (например, статические переменные, локальные переменные текущего потока, объекты в стеке).
GC обходит все объекты, к которым есть ссылки (прямые или косвенные).
Если объект не связан с корневыми объектами, он считается "мусором" и удаляется.
fun main() {
var user: User? = User("Alice") // Создаём объект
user = null // Теперь на объект нет ссылок, GC его удалит
}Метод Mark & Sweep – основной алгоритм работы GC.
Mark (Пометка) – GC помечает все достижимые объекты (к которым есть ссылки).
Sweep (Очистка) – GC удаляет непомеченные объекты (на которые нет ссылок).
Root → A → B
→ C
Недостижимые объекты (GC их удаляет)
Root → A → B (C больше недоступен)
C (GC удалит!)
Объекты делятся на молодые (Young) и старые (Old)
Young Generation – новые объекты (большинство умирает быстро).
Old Generation – "долго живущие" объекты (Activity, Singleton).
Утечки памяти (Memory Leaks) происходят, если на объект осталась ссылка, но он больше не нужен.
class Activity {
var button: Button? = null
}
var activity: Activity? = Activity() // Создаём объект
activity?.button = Button() // `Button` ссылается на `Activity`
activity = null // Activity нельзя удалить из-за ссылки на кнопку!Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
В Kotlin switch заменён на when. Это более мощный инструмент с поддержкой:
- Значений.
- Диапазонов.
- Условий.
- Множественных веток.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1💊1
Google постоянно ограничивает работу сервисов в Android, чтобы:
Уменьшить расход батареи
Оптимизировать использование памяти
Защитить пользователя от фоновых процессов, "убивающих" производительность
Сервис нельзя запустить из фона, если приложение не активно.
startService(Intent) выдаст ошибку, если приложение не на переднем плане. 1. Использовать Foreground Service (с уведомлением).
2. Использовать JobIntentService / WorkManager.
class MyForegroundService : Service() {
override fun onCreate() {
super.onCreate()
val notification = NotificationCompat.Builder(this, "channelId")
.setContentTitle("Сервис работает")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build()
startForeground(1, notification) // Запускаем сервис в Foreground
}
override fun onBind(intent: Intent?): IBinder? = null
}Если сервис запущен из фона (например, через
BroadcastReceiver), он не запустится. Исключение – если сервис работает в Foreground или с AlarmManager.
1. Использовать WorkManager (лучший вариант).
2. Использовать Foreground Service с уведомлением.
3. Использовать AlarmManager для периодических задач.
В спящем режиме (
Doze Mode) система отключает сервисы. Приложения не могут выполнять фоновые задачи.
Использовать Foreground Service.
Использовать JobScheduler / WorkManager.
Использовать Firebase Cloud Messaging (FCM) для пробуждения приложения.
Если пользователь смахнул приложение из списка недавних, фоновые сервисы будут убиты.
Решение → Foreground Service +
startForeground() или JobScheduler.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Android-приложение состоит из четырёх основных компонентов:
Activity – UI-экран приложения.
Service – фоновая работа без UI.
BroadcastReceiver – слушает системные и пользовательские события.
ContentProvider – делится данными между приложениями.
Отображает интерфейс пользователя.
Обрабатывает взаимодействие (нажатия, свайпы, ввод текста).
Управляется системой через жизненный цикл (
Lifecycle). Пример
Activity class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // Загружаем XML-разметку
}
}Выполняет длительные фоновые задачи (музыка, загрузка файлов).
Может работать даже если приложение закрыто.
НЕ имеет UI (не рисует
View). Пример
Serviceclass 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))
- Получает события системы (
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))
- Дает доступ к данным *другим приложениям.
- Позволяет безопасно работать с БД (
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
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
RxJava может вызывать утечки памяти, если подписки (
Disposable) не очищаются правильно. Утечки памяти происходят, когда RxJava-события продолжают выполняться после уничтожения
Activity/Fragment, удерживая ссылки на View или Context. CompositeDisposable собирает все подписки и очищает их при onDestroy(). class MainActivity : AppCompatActivity() {
private val disposables = CompositeDisposable() // Собираем подписки
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val disposable = Observable.interval(1, TimeUnit.SECONDS)
.subscribe { println("Tick: $it") }
disposables.add(disposable) // Добавляем подписку
}
override fun onDestroy() {
super.onDestroy()
disposables.clear() // ✅ Очищаем подписки, предотвращая утечку памяти
}
}Если используем Jetpack ViewModel или
LifecycleOwner, можно автоматически отвязывать подписки. class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Observable.interval(1, TimeUnit.SECONDS)
.autoDispose(viewLifecycleOwner) // ✅ Автоочистка при `onDestroyView()`
.subscribe { println("Tick: $it") }
}
}Если подписка не в
CompositeDisposable, её можно очистить вручную. private var disposable: Disposable? = null
fun startObserving() {
disposable = Observable.interval(1, TimeUnit.SECONDS)
.subscribe { println("Tick: $it") }
}
fun stopObserving() {
disposable?.dispose() // ✅ Очищаем подписку
}
RxJava может удерживать
Activity/Fragment, вызывая утечки памяти. Лучше использовать
ViewModel + LiveData! class MyViewModel : ViewModel() {
private val _timer = MutableLiveData<Long>()
val timer: LiveData<Long> = _timer
init {
Observable.interval(1, TimeUnit.SECONDS)
.subscribe { _timer.postValue(it) }
}
}Подписка в
Activity (без утечек!) viewModel.timer.observe(this) { println("Tick: $it") }Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Порядок:
1. Статический блок суперкласса;
2. Статический блок текущего класса;
3. Конструктор суперкласса;
4. Конструктор текущего класса.
(Статические блоки вызываются один раз при загрузке класса.)
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
Модификации могут означать разные вещи в зависимости от контекста. В Android-разработке чаще всего речь идёт о следующих типах модификаций:
Модификация данных (изменение состояния)
Модификация UI (Compose Modifiers)
Модификация кода (рефакторинг, оптимизация)
Модификация системы (кастомные прошивки, рут-изменения)
Это изменение переменных, объектов и структур данных во время работы приложения.
class ViewModel : ViewModel() {
private val _state = MutableStateFlow("Привет, мир!")
val state: StateFlow<String> = _state
fun updateText(newText: String) {
_state.value = newText // Модификация состояния
}
}В Jetpack Compose модификации (
Modifier) используются для изменения внешнего вида и поведения UI-элементов. Text(
text = "Привет!",
modifier = Modifier
.padding(16.dp) // Добавляем отступ
.background(Color.Blue) // Меняем фон
.clickable { println("Нажали!") } // Добавляем клик
)
В программировании модификация кода означает его улучшение без изменения основной логики.
fun sum(a: Int, b: Int): Int {
return a + b
}После рефакторинга
fun sum(a: Int, b: Int) = a + b
Это изменения в самой операционной системе Android, например:
- Рут-доступ (
root), позволяющий изменять системные файлы. - Кастомные прошивки (например, LineageOS).
- Изменение build.prop для скрытия рут-доступа.
ro.build.version.sdk=33
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊7👍1
Аннотация
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍2
Методы
equals() и hashCode() используются для сравнения объектов и их корректной работы в коллекциях (Set, Map). Метод
equals() должен:Рефлексивность:
a.equals(a) → true (объект равен самому себе). Симметричность:
a.equals(b) == b.equals(a). Транзитивность: если
a == b и b == c, то a == c. Согласованность: если
a == b, то a.equals(b) всегда возвращает одно и то же, пока объект не изменится. Сравнение с
null всегда даёт false: a.equals(null) == false. class User(val name: String, val age: Int) {
override fun equals(other: Any?): Boolean {
if (this === other) return true // Проверка на ссылочное равенство
if (other !is User) return false // Проверка типа
return name == other.name && age == other.age // Сравнение полей
}
}val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
println(user1 == user2) // true (потому что переопределён equals)Метод
hashCode() должен:Согласованность с
equals(): если a == b, то a.hashCode() == b.hashCode(). Но обратное не обязательно: два разных объекта могут иметь одинаковый
hashCode(). Хеш-код не должен меняться, если объект не изменился.
class User(val name: String, val age: Int) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is User) return false
return name == other.name && age == other.age
}
override fun hashCode(): Int {
return name.hashCode() * 31 + age // 31 - стандартный коэффициент
}
}val userSet = HashSet<User>()
userSet.add(User("Alice", 25))
println(userSet.contains(User("Alice", 25))) // true
Чтобы не писать
equals() и hashCode() вручную, можно использовать data class: data class User(val name: String, val age: Int)
data class автоматически создаёт equals(), hashCode(), а также copy() и toString(). val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
println(user1 == user2) // true (equals)
println(user1.hashCode() == user2.hashCode()) // trueСтавь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Наследование — это когда один класс расширяет другой, наследуя его поведение.
Композиция — это включение одного объекта в другой, при этом зависимость явно передаётся. Композиция считается более гибкой и менее связанной альтернативой наследованию.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
В Kotlin оператор
== используется для структурного сравнения объектов. Оператор
== вызывает метод equals(), чтобы проверить содержимое объектов, а не их ссылки. data class User(val name: String)
fun main() {
val user1 = User("Alice")
val user2 = User("Alice")
println(user1 == user2) // true (структурное сравнение)
println(user1 === user2) // false (сравнение ссылок)
}
== проверяет, равны ли данные объектов (equals()). === проверяет, ссылаются ли объекты на один и тот же участок памяти. val str1 = "Hello"
val str2 = "Hello"
println(str1 == str2) // true (содержимое одинаковое)
println(str1 === str2) // true (Kotlin кеширует строки)
val obj1 = String("Hello".toCharArray())
val obj2 = String("Hello".toCharArray())
println(obj1 == obj2) // true (содержимое одинаковое)
println(obj1 === obj2) // false (разные объекты в памяти)
Если
equals() не переопределён, то сравниваются ссылки (как ===). class Person(val name: String)
fun main() {
val p1 = Person("Bob")
val p2 = Person("Bob")
println(p1 == p2) // false (equals() не переопределён, сравниваются ссылки)
}
Чтобы
== работал по содержимому, нужно переопределить equals()class Person(val name: String) {
override fun equals(other: Any?): Boolean {
return other is Person && other.name == this.name
}
}
fun main() {
val p1 = Person("Bob")
val p2 = Person("Bob")
println(p1 == p2) // true (equals() сравнивает содержимое)
}Kotlin предотвращает
NullPointerException при сравнении с null:val a: String? = null
val b: String? = "Hello"
println(a == b) // false (без NPE)
println(a == null) // true
Когда
a == b, на самом деле выполняется:a?.equals(b) ?: (b === null)
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Нет, LayoutInflater — не синглтон, но может быть получен как скоуп-зависимый объект (getSystemService) в Context. Однако его можно переиспользовать, так как он не хранит состояния между вызовами.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊4🔥2
Разделение UI и бизнес-логики делает код понятнее, тестируемее и проще в поддержке.
Это ключевой принцип чистой архитектуры (Clean Architecture) и паттернов MVP, MVVM, MVI.
Плохой код (UI + логика в
Activity) class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
val result = fetchData() // ❌ Логика в UI
button.text = result
}
}
fun fetchData(): String {
return "Данные с сервера" // ❌ Тут должна быть ViewModel
}
}Разделим код на
Activity + ViewModel MainActivity (только UI) class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button)
viewModel.text.observe(this) { text ->
button.text = text // ✅ UI обновляется из ViewModel
}
button.setOnClickListener {
viewModel.loadData() // ✅ Вызываем бизнес-логику
}
}
}MainViewModel (бизнес-логика) class MainViewModel : ViewModel() {
private val _text = MutableLiveData<String>()
val text: LiveData<String> = _text
fun loadData() {
_text.value = "Данные с сервера" // ✅ UI не знает, откуда данные
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
- В manifest:
- В коде:
Также можно использовать taskAffinity и фильтрацию в onNewIntent().
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1💊1
Ключевое слово
object в Kotlin имеет несколько важных применений, и оно является одной из наиболее мощных и уникальных возможностей языка. 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()
}Ключевое слово
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
}Вместо написания множества шаблонного кода для Singleton, анонимных объектов или статических методов, вы получаете готовую реализацию из коробки.
В случае
object, компилятор Kotlin автоматически гарантирует, что объект создаётся в потокобезопасном режиме.object можно использовать практически везде: глобально, локально, внутри классов и функций.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1