Анонимный класс внутри
inline-функции будет инстанцироваться при каждом вызове функции, что ломает цель inline — избавляться от накладных расходов на объекты. Анонимный класс — это класс без имени, который можно создать с помощью
object : Interface {}. interface ClickListener {
fun onClick()
}
fun main() {
val listener = object : ClickListener {
override fun onClick() {
println("Кнопка нажата!")
}
}
listener.onClick()
}Если мы создадим анонимный класс внутри
inline-функции, он будет создаваться каждый раз при вызове функции! inline fun setClickListener(action: () -> Unit) {
val listener = object : ClickListener {
override fun onClick() {
action()
}
}
listener.onClick()
}
fun main() {
setClickListener { println("Нажато!") }
}Ошибка компиляции
Inline function cannot contain object expressions
Если нужно использовать
inline, можно передавать лямбду вместо анонимного класса. Рабочий вариант
inline fun setClickListener(noinline action: () -> Unit): ClickListener {
return object : ClickListener {
override fun onClick() {
action()
}
}
}
fun main() {
val listener = setClickListener { println("Нажато!") }
listener.onClick()
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Рекомпозиция управляется Android Choreographer, который работает с VSYNC.
Процесс:
1. Изменение MutableState ставит флаг "UI требует перерисовки".
2. Choreographer ждет ближайший VSYNC и запускает рекомпозицию.
3. Перерисовываются только измененные элементы UI.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2🤯1
Android 5.0 Lollipop стал огромным обновлением по сравнению с Android 4.x, изменив интерфейс, архитектуру и производительность.
Android 4: использовал Holo UI (чёрно-серый стиль с минимализмом).
Android 5: представил Material Design — новый визуальный стиль с тенями, анимациями и цветными кнопками.
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_add"
app:backgroundTint="@color/colorAccent"/>
Android 4: использовал Dalvik (Just-in-Time, JIT) — код компилировался во время работы приложения.
Android 5: перешёл на ART (Ahead-of-Time, AOT) — код компилируется один раз при установке.
Android 4: уведомления были простыми и появлялись только в статус-баре.
Android 5: представил Heads-up Notifications (всплывающие уведомления).
val notification = NotificationCompat.Builder(context, "channel_id")
.setContentTitle("Новое сообщение")
.setContentText("Привет, как дела?")
.setPriority(NotificationCompat.PRIORITY_HIGH) // Heads-up уведомление
.setSmallIcon(R.drawable.ic_message)
.build()
Android 4: только 32-битная архитектура.
Android 5: появилась поддержка 64-битных процессоров, что улучшило производительность и работу с памятью.
Android 4: не имел централизованного контроля за энергопотреблением.
Android 5: представил Project Volta, который продлевает жизнь батареи.
val jobScheduler = getSystemService(JobScheduler::class.java)
val jobInfo = JobInfo.Builder(1, ComponentName(this, MyJobService::class.java))
.setRequiresCharging(true) // Выполнить только при зарядке
.build()
jobScheduler.schedule(jobInfo)
Android 4: старые приложения в "Недавних" выглядели как список окон.
Android 5: приложения теперь отображаются в виде стопки карт (Card Stack UI).
Шифрование устройства включено по умолчанию (Android 5).
SELinux (Security-Enhanced Linux) теперь работает в принудительном (
enforcing) режиме. Добавлен Smart Lock → автоматическая разблокировка устройства по Bluetooth / Wi-Fi / лицу.
Android 5 впервые добавил поддержку Android Wear (умные часы) и Android TV.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
😁3👍2
Suspend-функция не блокирует поток, а передает управление Continuation-объекту.
Функция "узнает" о приостановке через:
- Фрейм Continuation: если вызвана другая suspend-функция, текущая приостанавливает свое выполнение.
- Корутинный диспетчер: приостановка возможна, если выполнение ушло в другой поток (withContext).
- Возвращение специального маркера COROUTINE_SUSPENDED: сигнализирует Kotlin Runtime, что выполнение отложено.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5🤯3
В Kotlin можно добавлять свойства-расширения (
extension properties), но только с кастомным get (геттером). Расширяемые свойства могут быть только вычисляемыми (
val), потому что нельзя создать field внутри расширения. val String.firstChar: Char
get() = this[0]
fun main() {
println("Kotlin".firstChar) // K
}
Для
var нужно и get(), и set(), но всё равно нельзя использовать field. var StringBuilder.lastChar: Char
get() = this[length - 1]
set(value) {
this.setCharAt(length - 1, value)
}
fun main() {
val sb = StringBuilder("Hello")
println(sb.lastChar) // o
sb.lastChar = '!'
println(sb) // Hell!
}
Такой код НЕ скомпилируется!
var String.someProperty: String = "Default" // Ошибка!
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Основные характеристики snapshot:
- Снимки (snapshots) позволяют фиксировать состояние в определенный момент времени.
- Внесенные изменения откладываются до момента коммита (commit).
- Работают аналогично транзакциям – можно откатить изменения, если они конфликтуют.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Data Class и Sealed Class решают разные задачи и обеспечивают улучшения в организации кода, управлении состоянием и безопасности типов. Они вносят значительные упрощения и повышают читаемость кода в Kotlin-проектах.
Data Class предназначены для хранения данных и автоматически предоставляют ряд полезных методов, что упрощает разработку и уменьшает объем шаблонного кода. Основные причины использования Data Class:
Автоматически генерирует методы
equals(), hashCode(), и toString(), а также copy() и компонентные функции для объектов данных. Это избавляет от необходимости ручной реализации этих методов, что уменьшает количество кода и возможность ошибок.Идеально подходят для передачи данных между различными частями приложения, например, между слоями в архитектуре MVVM или при передаче данных между активностями и фрагментами.
С помощью них легко создавать неизменяемые объекты, что способствует безопасной работе с данными, особенно в многопоточной среде.
Sealed Class используются для определения закрытых иерархий классов, где все потомки известны и ограничены. Они полезны по следующим причинам:
Гарантируют, что все возможные подтипы обработаны в выражениях
when, что предотвращает ошибки во время выполнения из-за пропущенных случаев. Это упрощает управление состояниями и делает код более безопасным и предсказуемым.Ограничивают возможность создания подклассов за пределами файла, в котором они объявлены. Это предотвращает неожиданное наследование и сохраняет иерархию классов контролируемой и понятной.
Поскольку все расширения таких классов должны быть объявлены в том же файле, это способствует лучшей инкапсуляции и организации кода.
data class User(val name: String, val age: Int)
Пример Sealed Class
sealed class Result {
data class Success(val data: String) : Result()
data class Failure(val error: Throwable) : Result()
}
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println("Success with data: ${result.data}")
is Result.Failure -> println("Failure with error: ${result.error.message}")
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Это механизм, который синхронизирует отрисовку UI с частотой обновления экрана.
- В Android VSYNC происходит 60 раз в секунду (или больше на устройствах с высокой герцовкой).
- Compose привязывает рекомпозицию к VSYNC, чтобы отрисовка происходила плавно и эффективно.
- Изменения состояния приводят к запросу рекомпозиции, но реальная перерисовка выполняется только при следующем VSYNC-событии.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
В Kotlin Coroutines есть несколько диспетчеров (
Dispatchers), но Default и IO используются чаще всего. Dispatchers.Default — для тяжёлых вычислений (CPU-операции). Dispatchers.IO — для операций ввода-вывода (сеть, файлы, БД). Этот диспетчер используется, если код загружает процессор (например, сложные вычисления).
import kotlinx.coroutines.*
fun main() = runBlocking {
launch(Dispatchers.Default) {
val result = heavyComputation()
println("Результат: $result")
}
}
suspend fun heavyComputation(): Int {
delay(1000)
return (1..1_000_000).sum()
}
Этот диспетчер оптимизирован для ввода-вывода (I/O): работа с файлами, сетью, БД.
import kotlinx.coroutines.*
import java.io.File
fun main() = runBlocking {
launch(Dispatchers.IO) {
val text = File("data.txt").readText()
println("Файл прочитан: $text")
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Классы в Jetpack Compose автоматически считаются stable, если:
- Они data-классы.
- Они неизменяемые (immutable).
- Все их свойства состоят из stable типов (например, String, Int, Float).
- Они не используют ссылочные типы с изменяемым состоянием.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1🤔1
DiffUtil — это утилита для быстрого обновления списков в RecyclerView. Она сравнивает старый и новый список и находит различия, чтобы обновлять только изменённые элементы, а не весь список. Обычное обновление списка без
DiffUtil перерисовывает всё, даже если изменился один элемент. adapter.notifyDataSetChanged() // Полностью обновляет список ❌
DiffUtil находит различия между старым и новым списком и обновляет только изменённые элементы. class MyDiffUtilCallback(
private val oldList: List<User>,
private val newList: List<User>
) : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition] // Используем data class (авто `equals()`)
}
}
После создания
DiffUtil.Callback, вызываем DiffUtil.calculateDiff() и передаём результат в adapter. fun updateList(newList: List<User>) {
val diffCallback = MyDiffUtilCallback(userList, newList)
val diffResult = DiffUtil.calculateDiff(diffCallback)
userList = newList // Обновляем старый список
diffResult.dispatchUpdatesTo(this) // Обновляем только изменённые элементы
}Если используем
ListAdapter, DiffUtil уже встроен. class UserAdapter : ListAdapter<User, UserViewHolder>(DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(getItem(position)) // `getItem()` уже работает с `ListAdapter`
}
companion object {
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
oldItem == newItem
}
}
}Обновление списка в
ListAdapter adapter.submitList(newList) // DiffUtil работает внутри 🚀
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1🤔1
suspend-функции в Kotlin приостанавливают выполнение без блокировки потока.
Под капотом:
1. Suspend-функция разбивается на несколько частей (continuations).
2. Приостанавливается, если выполняется асинхронный код (например, delay(), withContext()).
3. Продолжается с места остановки, когда результат готов.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3🎉1
Forwarded from easyoffer
На easyoffer 2.0 появится:
🎯 Тренажер "Проработка вопросов"
✅ Метод интервальных повторений и флеш-карточки
✅ Персональный подход изучения на основе ваших ответов
✅ Упор на самые частые вопросы
📌 Интервальные повторения по карточкам это научно доказанный метод эффективного обучения. Каждая карточка – это вопрос, который задают на собеседовании, вы можете выбрать "Не знаю", "Знаю", "Не спрашивать". После ответа вам показывается правильный ответ и возможность изучить вопрос подробнее (примеры ответов других людей). От ваших ответов зависит то, как часто карточки будут показываться на следующей тренировке. Трудные вопросы показываются чаще, простые – реже. Это позволяет бить в слабые места. Кроме того, изначальный порядок карточек зависит от частотности (вероятности встретить вопрос).
🚀 Благодаря этому тренажеру вы сможете очень быстро подготовиться к собеседованию, т.к. фокусируетесь отвечать на самые частые вопросы. Именно так готовился я сам, когда искал первую работу программистом.
Уже в течение недели я объявлю о старте краудфандинговой кампании на сбор финансирования, чтобы ускорить разработку сайта. Все кто поддержит проект до официального релиза получат самые выгодные условия пользования сервисом. А именно 1 год доступа к сайту по цене месячной подписки.
‼️ Очень важно, чтобы как можно больше людей поддержали проект в первые дни, по-этому те кто окажет поддержку первыми получат еще более выгодную стоимость на годовую подписку и существенный💎 бонус о котором я позже расскажу в этом телеграм канале. Подписывайтесь, чтобы узнать о старте проекта раньше других и воспользоваться лимитированными вознаграждениями.
🎯 Тренажер "Проработка вопросов"
✅ Метод интервальных повторений и флеш-карточки
✅ Персональный подход изучения на основе ваших ответов
✅ Упор на самые частые вопросы
📌 Интервальные повторения по карточкам это научно доказанный метод эффективного обучения. Каждая карточка – это вопрос, который задают на собеседовании, вы можете выбрать "Не знаю", "Знаю", "Не спрашивать". После ответа вам показывается правильный ответ и возможность изучить вопрос подробнее (примеры ответов других людей). От ваших ответов зависит то, как часто карточки будут показываться на следующей тренировке. Трудные вопросы показываются чаще, простые – реже. Это позволяет бить в слабые места. Кроме того, изначальный порядок карточек зависит от частотности (вероятности встретить вопрос).
🚀 Благодаря этому тренажеру вы сможете очень быстро подготовиться к собеседованию, т.к. фокусируетесь отвечать на самые частые вопросы. Именно так готовился я сам, когда искал первую работу программистом.
Уже в течение недели я объявлю о старте краудфандинговой кампании на сбор финансирования, чтобы ускорить разработку сайта. Все кто поддержит проект до официального релиза получат самые выгодные условия пользования сервисом. А именно 1 год доступа к сайту по цене месячной подписки.
‼️ Очень важно, чтобы как можно больше людей поддержали проект в первые дни, по-этому те кто окажет поддержку первыми получат еще более выгодную стоимость на годовую подписку и существенный
Please open Telegram to view this post
VIEW IN TELEGRAM
Да! В Android есть специальные Map-коллекции, которые позволяют хранить примитивные типы (
int, long, boolean и т. д.) без автоупаковки (autoboxing). Обычные
HashMap<Int, Int> в Kotlin используют автоупаковку (Integer вместо int), что: Увеличивает потребление памяти (из-за объектов
Integer, Long и т. д.). Замедляет работу (из-за ненужного создания объектов).
Решение? Использовать специализированные мэпы из
android.util! Хранит пары
Int → Any, но без автоупаковки. import android.util.SparseArray
val sparseArray = SparseArray<String>()
sparseArray.put(1, "Привет")
sparseArray.put(2, "Мир")
println(sparseArray[1]) // Привет
println(sparseArray[2]) // Мир
Хранит пары
Int → Int без автоупаковки. import android.util.SparseIntArray
val sparseIntArray = SparseIntArray()
sparseIntArray.put(1, 100)
sparseIntArray.put(2, 200)
println(sparseIntArray[1]) // 100
println(sparseIntArray[2]) // 200
Оптимизирован для
Int → Boolean пар. import android.util.SparseBooleanArray
val sparseBooleanArray = SparseBooleanArray()
sparseBooleanArray.put(1, true)
sparseBooleanArray.put(2, false)
println(sparseBooleanArray[1]) // true
println(sparseBooleanArray[2]) // false
Оптимизирован для
Long → Any?, аналог SparseArray, но с Long ключами. import android.util.LongSparseArray
val longSparseArray = LongSparseArray<String>()
longSparseArray.put(10000000000L, "Длинный ключ")
println(longSparseArray[10000000000L]) // Длинный ключ
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Основные способы хранения данных:
- Локальные базы данных: SQLite, Room (Android), Realm, SharedPreferences.
- Файлы: JSON, XML, CSV, бинарные файлы.
- Кэширование: в памяти (LruCache), в файлах, в базах данных.
- Облачное хранение: Firebase Firestore, Google Drive API.
- Key-Value хранилища: Redis, SharedPreferences, MMKV.
Выбор зависит от объема данных, скорости доступа и необходимости в синхронизации.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥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
👍6
- SQLite – низкоуровневая реляционная база данных, требует SQL-запросов вручную.
- Room – надстройка над SQLite, предоставляет удобный API с аннотациями, поддерживает LiveData, Flow и автоматическую миграцию данных.
Room упрощает работу с базой и делает код читаемым, но внутри все равно использует SQLite.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥4😁2
В Java все классы неявно наследуются от класса
Object, если явно не указано другое наследование. class MyClass {
// Неявно наследуется от Object
}
class MyClass2 extends Object {
// То же самое, просто указано явно
}Класс
Object содержит основные методы, доступные во всех классах: class Person {
String name;
Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{name='" + name + "'}";
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("Alice");
System.out.println(p.toString()); // Person{name='Alice'}
System.out.println(p.hashCode()); // Хеш-код объекта
System.out.println(p.getClass()); // class Person
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Да, через Intent можно передавать фото, но:
- Для небольших изображений можно использовать Intent.putExtra("data", bitmap).
- Для больших файлов используют URI (Intent.putExtra(Intent.EXTRA_STREAM, uri)) или FileProvider, чтобы избежать TransactionTooLargeException.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍1
Forwarded from easyoffer
На easyoffer 2.0 появится новый раздел:
Задачи с собеседований
🟠 Задачи на Алгоритмические, Live-coding и System Design из реальных собеседований
🟠 Вероятность встретить ту или иную задачу
🟠 Возможность подготовиться к задачам конкретной компании
Есть много сайтов, на которых можно тренироваться решать задачи, но у них у всех одна проблема – сами задачи люди просто выдумывают. На easyoffer 2.0 вы сможете готовиться к live-coding и system design секциям на основе задач из реальных собеседований. Вы можете найдете самые частые задачи и сделаете упор на их решение.
Считаные дни остались до старта краудфандинговой кампании, чтобы ускорить разработку easyoffer 2.0. Все кто, поддержал проект на этом этапе смогу получить 1 год доступа к сайту по цене месячной подписки, а те кто поддержат проект раньше других ито дешевле + получат существенный бонус. Следите за стартом 👉 в этом телеграм канале.
Задачи с собеседований
Есть много сайтов, на которых можно тренироваться решать задачи, но у них у всех одна проблема – сами задачи люди просто выдумывают. На easyoffer 2.0 вы сможете готовиться к live-coding и system design секциям на основе задач из реальных собеседований. Вы можете найдете самые частые задачи и сделаете упор на их решение.
Считаные дни остались до старта краудфандинговой кампании, чтобы ускорить разработку easyoffer 2.0. Все кто, поддержал проект на этом этапе смогу получить 1 год доступа к сайту по цене месячной подписки, а те кто поддержат проект раньше других ито дешевле + получат существенный бонус. Следите за стартом 👉 в этом телеграм канале.
Please open Telegram to view this post
VIEW IN TELEGRAM