fa
Feedback
Kotlin | Вопросы собесов

Kotlin | Вопросы собесов

رفتن به کانال در Telegram

Сайт: https://easyoffer.ru/ Все каналы: t.me/+xGeAw6ckJ4liYzQy Контакт для рекламы: @sendme_ads

نمایش بیشتر
2 455
مشترکین
اطلاعاتی وجود ندارد24 ساعت
+47 روز
+730 روز

در حال بارگیری داده...

جذب مشترکین
اکتبر '26
اکتبر '26
+8
در 0 کانال‌ها
سپتامبر '26
+24
در 0 کانال‌ها
Get PRO
اوت '26
+25
در 0 کانال‌ها
Get PRO
ژوئیه '26
+27
در 0 کانال‌ها
Get PRO
ژوئن '26
+23
در 1 کانال‌ها
Get PRO
مه '26
+20
در 0 کانال‌ها
Get PRO
آوریل '26
+30
در 0 کانال‌ها
Get PRO
مارس '26
+32
در 0 کانال‌ها
Get PRO
فوریه '26
+46
در 0 کانال‌ها
Get PRO
ژانویه '26
+54
در 0 کانال‌ها
Get PRO
دسامبر '25
+23
در 0 کانال‌ها
Get PRO
نوامبر '25
+42
در 0 کانال‌ها
Get PRO
اکتبر '25
+38
در 0 کانال‌ها
Get PRO
سپتامبر '25
+44
در 0 کانال‌ها
Get PRO
اوت '25
+47
در 0 کانال‌ها
Get PRO
ژوئیه '25
+35
در 0 کانال‌ها
Get PRO
ژوئن '25
+46
در 0 کانال‌ها
Get PRO
مه '25
+55
در 0 کانال‌ها
Get PRO
آوریل '25
+57
در 0 کانال‌ها
Get PRO
مارس '25
+110
در 1 کانال‌ها
Get PRO
فوریه '25
+105
در 2 کانال‌ها
Get PRO
ژانویه '25
+159
در 53 کانال‌ها
Get PRO
دسامبر '24
+59
در 0 کانال‌ها
Get PRO
نوامبر '24
+70
در 0 کانال‌ها
Get PRO
اکتبر '24
+210
در 42 کانال‌ها
Get PRO
سپتامبر '24
+608
در 251 کانال‌ها
Get PRO
اوت '24
+147
در 0 کانال‌ها
Get PRO
ژوئیه '24
+203
در 54 کانال‌ها
Get PRO
ژوئن '24
+492
در 244 کانال‌ها
Get PRO
مه '24
+563
در 189 کانال‌ها
Get PRO
آوریل '24
+461
در 23 کانال‌ها
تاریخ
رشد مشترکین
اشارات
کانال‌ها
07 اکتبر0
06 اکتبر+1
05 اکتبر+3
04 اکتبر+1
03 اکتبر+1
02 اکتبر+1
01 اکتبر+1
پست‌های کانال
🔥 Скрытые вакансии с удаленной работой для Android разработчика, которые нигде больше не публикуются. Напрямую от компаний,
🔥 Скрытые вакансии с удаленной работой для Android разработчика, которые нигде больше не публикуются. Напрямую от компаний, с контактами рекрутеров. 🔹 Android Jobs | Вакансии 🔹 Flutter Dart Jobs | Вакансии 🔹 iOS Jobs | Вакансии 🔸 Все каналы с вакансиями

2
🤔 Что известно про генерации Young, Old в GC? - Young Generation: - Содержит новые объекты. - Быстрое создание и удаление. - Делится на Eden и два Survivor-пула. - GC здесь называется Minor GC и работает быстро. - Old Generation (Tenured): - Хранит объекты, пережившие несколько циклов GC. - Удаление объектов тут требует более тяжёлой и полной сборки — Major GC. Такой подход (генерационная модель) помогает оптимизировать производительность, так как большинство объектов "умирает молодыми". Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний
169
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() } Ставь 👍 и забирай 📚 Базу знаний
217
4
🤔 Разница Iteration и Enumeration? - Enumeration — устаревший интерфейс, используется в старых коллекциях (Vector). - Iterator — современный подход с возможностью безопасного удаления (remove()) элементов во время итерации. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний
251
5
🤔 Что такое SharedFlow и чем он отличается от StateFlow? В Kotlin Flow есть два специальных вида потоков для управления состоянием и передачей данных: StateFlow — используется для хранения и отслеживания состояния. SharedFlow — используется для многократной отправки данных нескольким подписчикам. Теперь разберёмся в деталях. 🚩Что такое SharedFlow? SharedFlow — это горячий (hot) поток, который можно использовать для передачи данных нескольким подписчикам. Он не хранит состояние и просто раздаёт значения подписчикам в реальном времени. 🚩Как работает SharedFlow? - Позволяет многим подписчикам получать одни и те же данные. - Может буферизировать значения (хранить их для новых подписчиков). - Может повторять последние значения (replay) для новых подписчиков. - Может накапливать данные и работать как очередь событий. import kotlinx.coroutines.* import kotlinx.coroutines.flow.* fun main() = runBlocking { val sharedFlow = MutableSharedFlow<Int>(replay = 2) // Будет повторять последние 2 значения для новых подписчиков launch { for (i in 1..5) { sharedFlow.emit(i) delay(100) } } launch { delay(150) // Подписываемся чуть позже sharedFlow.collect { println("Первый подписчик получил: $it") } } launch { delay(300) // Подписываемся ещё позже sharedFlow.collect { println("Второй подписчик получил: $it") } } } 🚩Что такое StateFlow? StateFlow — это поток, который всегда хранит одно последнее значениЕ. Он идеально подходит для представления состояния (например, UI-состояния в MVVM). - Всегда содержит одно актуальное значение. - Если новое значение не отличается от текущего, оно не отправляется подписчикам. - Новый подписчик сразу получает текущее значение. - Можно думать о StateFlow как о LiveData, но для корутин. import kotlinx.coroutines.* import kotlinx.coroutines.flow.* fun main() = runBlocking { val stateFlow = MutableStateFlow(0) // Начальное состояние launch { delay(200) // Подписываемся позже stateFlow.collect { println("Подписчик получил: $it") } } delay(100) stateFlow.value = 1 // Меняем состояние stateFlow.value = 2 // Меняем состояние } Ставь 👍 и забирай 📚 Базу знаний
257
6
🤔 Что такое утечки памяти? Утечки памяти возникают, когда объекты в памяти больше не используются, но продолжают удерживаться из-за активных ссылок, которые на них ссылаются. 1. В языках с автоматическим управлением памятью, таких как Java или Python, утечки обычно вызваны циклическими ссылками или неправильным использованием коллекций. 2. В языках со статическим управлением памятью, таких как C++, утечки происходят из-за отсутствия освобождения памяти после выделения (например, забыли вызвать delete после new). 3. Основная проблема утечек — рост потребления памяти, что может привести к замедлению программы или её краху. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний
260
7
🤔 Как запустить асинхронные операции в чистом Android? В Android существует несколько способов запуска асинхронных операций. Наиболее распространённые из них включают использование AsyncTask (хотя он уже устарел), HandlerThread, AsyncTaskLoader, ExecutorService и современные подходы с использованием Kotlin Coroutines и библиотеки WorkManager. 🟠AsyncTask (устаревший) AsyncTask был одним из первых инструментов для выполнения асинхронных задач, однако его использование сейчас не рекомендуется из-за проблем с управлением жизненным циклом и утечками памяти. private class MyAsyncTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... voids) { // Выполнение длительной операции return "Result"; } @Override protected void onPostExecute(String result) { // Обновление UI после завершения операции } } // Запуск new MyAsyncTask().execute(); 🟠HandlerThread HandlerThread — это удобный способ создания фонового потока с циклом обработки сообщений. HandlerThread handlerThread = new HandlerThread("MyHandlerThread"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()); handler.post(new Runnable() { @Override public void run() { // Выполнение длительной операции } }); 🟠ExecutorService ExecutorService из стандартной библиотеки Java предоставляет более гибкий способ управления потоками. ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(new Runnable() { @Override public void run() { // Выполнение длительной операции } }); 🟠Kotlin Coroutines Корутины в Kotlin — это современный и мощный инструмент для асинхронного программирования. Они упрощают работу с асинхронными задачами и обеспечивают безопасность работы с UI. import kotlinx.coroutines.* fun performAsyncTask() { GlobalScope.launch(Dispatchers.IO) { // Выполнение длительной операции val result = longRunningTask() withContext(Dispatchers.Main) { // Обновление UI после завершения операции } } } suspend fun longRunningTask(): String { delay(1000) // Симуляция длительной операции return "Result" } 🟠WorkManager WorkManager — это библиотека для выполнения фоновых задач, которая обеспечивает выполнение задач даже после закрытия приложения или перезагрузки устройства. public class MyWorker extends Worker { public MyWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @NonNull @Override public Result doWork() { // Выполнение длительной операции return Result.success(); } } // Запуск Worker WorkManager workManager = WorkManager.getInstance(context); OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class).build(); workManager.enqueue(request); 🟠RxJava RxJava — это библиотека для реактивного программирования, которая также может быть использована для выполнения асинхронных задач. import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.schedulers.Schedulers; Observable.fromCallable(() -> { // Выполнение длительной операции return "Result"; }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { // Обновление UI после завершения операции }); Ставь 👍 и забирай 📚 Базу знаний
226
8
🤔 Что известно про аннотации Dagger Provide и Binds? - @Provide: - Используется, когда ты сам создаёшь объект вручную. - Возвращает конкретную реализацию зависимости. - Гибко и подходит для конструкций с логикой. - @Binds: - Используется, если реализация уже доступна через конструктор. - Просто указывает, какой интерфейс соответствует какому классу. - Требует абстрактной функции и не допускает тела метода. Обычно @Provide применяют при сложной логике создания, а @Binds — при простом сопоставлении интерфейса и реализации. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний
247
9
🤔 Как собираем код? Сборка Android-приложения проходит несколько этапов: Компиляция (.kt/.java → .class → .dex) Объединение ресурсов (XML, PNG → R.java) Сборка APK/AAB Подписывание и оптимизация Установка и запуск на устройстве 🚩Компиляция кода (`.kt/.java → .dex`) Компиляция Kotlin/Java в байткод JVM (.class) Kotlin-код (.kt) → Java-байткод (.class) Java-код (.java) → .class - Используются компиляторы: - kotlinc (Kotlin Compiler) - javac (Java Compiler) // Kotlin-код (MainActivity.kt) fun main() { println("Привет, Android!") } kotlinc MainActivity.kt -d MainActivity.class Преобразование .class в `.dex* Android не использует байткод JVM, а преобразует .class в Dalvik Executable (.dex). d8 MainActivity.class --output classes.dex 🚩Компиляция ресурсов (`XML, PNG → R.java → .dex`) Android Gradle Plugin (AGP) обрабатывает ресурсы - res/layout/*.xml → UI файлы - res/drawable/*.png → Иконки - AndroidManifest.xml → Метаинформация public final class R { public static final class layout { public static final int activity_main = 0x7f0a0000; } } 🚩Сборка APK/AAB APK (старый формат) и AAB (новый формат Google Play) classes.dex + res/ + AndroidManifest.xml объединяются Проходит обфускация (ProGuard / R8) APK/AAB упаковывается в ZIP-архив zipalign -v 4 app.apk signed_app.apk 🚩Подписывание и оптимизация APK/AAB должен быть подписан перед установкой apksigner sign --ks my-release-key.jks --out signed_app.apk app-release.apk Установка и запуск adb install signed_app.apk adb shell am start -n com.example.app/.MainActivity Ставь 👍 и забирай 📚 Базу знаний
251
10
🤔 Что такое Extensions? Термин "Extensions" (расширения) используется для обозначения функциональности, которая позволяет добавлять новые возможности к существующим классам без изменения их исходного кода. Этот концепт особенно популярен к примеру в Swift и Kotlin, где расширения используются для улучшения читаемости кода, упрощения его структуры и добавления удобных методов для работы с уже существующими типами данных. 🚩Расширения в Kotlin Расширения позволяют добавить новую функциональность к классам, даже если исходный код этих классов недоступен для изменений (например, классы из стандартной библиотеки или сторонних библиотек). Это делается путём объявления функций или свойств, которые могут быть вызваны на экземплярах этих классов, как если бы они были частью оригинальных классов. // Расширение класса String для проверки, пуста ли строка или состоит из пробелов fun String.isNullOrBlank(): Boolean { return this == null || this.trim().isEmpty() } // Использование расширения val myString = " " println(myString.isNullOrBlank()) // Выведет: true 🚩Расширения в Swift Использует очень похожий подход, позволяя добавлять новые методы и вычисляемые свойства к существующим типам. Расширения могут даже добавлять новые протоколы к типам, что делает их чрезвычайно мощным инструментом в разработке под iOS и macOS. // Расширение стандартного типа String для добавления метода инвертирования строки extension String { func reversedString() -> String { return String(self.reversed()) } } let myString = "Hello" print(myString.reversedString()) // Выведет: "olleH" 🚩Плюсы ➕Улучшение читаемости и организации кода Расширения позволяют держать методы, связанные с определёнными типами данных, ближе к использованию этих типов. ➕Избегание наследования Расширения предоставляют альтернативный способ добавления функциональности к классам без использования наследования, что помогает избежать избыточной иерархии и увеличения сложности системы. ➕Реиспользование кода Методы, добавленные через расширения, могут быть легко переиспользованы в различных частях приложения. Ставь 👍 и забирай 📚 Базу знаний
282
11
🤔 Что нужно сделать в Android-проекте чтобы начать рисовать UI на экране? Чтобы начать рисовать пользовательский интерфейс (UI) на экране в Android-проекте, необходимо выполнить несколько шагов, которые включают настройку проекта, создание макета и взаимодействие с основными компонентами Android. 1⃣Создание Android-проекта Первый шаг — создание Android-проекта в Android Studio. Это можно сделать, выбрав шаблон «Empty Activity», который предоставляет минимальный набор для разработки приложения. Укажите имя проекта. Выберите язык программирования (Java или Kotlin). Убедитесь, что минимальная версия SDK подходит для вашей целевой аудитории. 2⃣Настройка макета (XML-файл) Android UI в основном создаётся с использованием XML-файлов, которые определяют структуру интерфейса. По умолчанию файл макета находится в каталоге: res/layout/activity_main.xml #### Пример простого макета: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Привет, мир!" android:textSize="24sp" android:padding="16dp"/> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Нажми меня"/> </LinearLayout> 🟠Подключение макета к активности Макет нужно связать с логикой приложения в Java или Kotlin. Это делается с помощью метода setContentView() в Activity. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Указываем, какой XML-файл использовать для интерфейса setContentView(R.layout.activity_main) // Найдём элементы интерфейса и добавим логику val textView = findViewById<TextView>(R.id.textView) val button = findViewById<Button>(R.id.button) button.setOnClickListener { textView.text = "Кнопка нажата!" } } } 🟠Добавление кастомного рисования (по необходимости) Если нужно нарисовать что-то вручную (например, графику, линии, или кастомные фигуры), можно создать свой собственный класс, унаследованный от View, и переопределить метод onDraw(). class CustomView(context: Context) : View(context) { private val paint = Paint().apply { color = Color.RED strokeWidth = 10f } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) // Рисуем линию canvas?.drawLine(100f, 100f, 400f, 400f, paint) } } Чтобы использовать этот класс, можно добавить его в макет или программно class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Устанавливаем кастомный View вместо макета setContentView(CustomView(this)) } } 🟠Запуск приложения Теперь можно запустить приложение на эмуляторе или реальном устройстве: Нажмите Run в Android Studio. Убедитесь, что устройство подключено или выбран эмулятор. После запуска вы увидите созданный интерфейс. Ставь 👍 и забирай 📚 Базу знаний
279
12
🤔 Как бы передавал фотографию в редактор? Передача фото в редактор зависит от типа редактора: 1. Внешний редактор (например, Google Photos, Snapseed). 2. Встроенный редактор внутри приложения. 🚩Передача фото во внешний редактор (Intent) Если редактор — другое приложение, используем Intent.ACTION_EDIT. Как передать фото в редактор? fun openPhotoEditor(context: Context, uri: Uri) { val intent = Intent(Intent.ACTION_EDIT).apply { setDataAndType(uri, "image/*") addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Разрешение на чтение } context.startActivity(Intent.createChooser(intent, "Выберите редактор")) } 🚩Передача в `Activity` своего приложения (FileProvider) Если редактор — внутри приложения, можно передавать фото через Intent с Uri. Отправка фото в редактор fun openEditor(context: Context, photoFile: File) { val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", photoFile) val intent = Intent(context, PhotoEditorActivity::class.java).apply { putExtra("PHOTO_URI", uri.toString()) } context.startActivity(intent) } Получение фото в редакторе (PhotoEditorActivity) class PhotoEditorActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val uriString = intent.getStringExtra("PHOTO_URI") val uri = uriString?.let { Uri.parse(it) } uri?.let { imageView.setImageURI(it) // Показываем фото } } } 🚩Если фото приходит в `byte[]` (с сервера) Если фото уже в памяти (byte[]), можно передать его как Parcelable. Отправка val bitmap = ... // Получили Bitmap val byteArray = ByteArrayOutputStream().apply { bitmap.compress(Bitmap.CompressFormat.PNG, 100, this) }.toByteArray() val intent = Intent(context, PhotoEditorActivity::class.java).apply { putExtra("PHOTO_BYTES", byteArray) } context.startActivity(intent) Получение val byteArray = intent.getByteArrayExtra("PHOTO_BYTES") byteArray?.let { val bitmap = BitmapFactory.decodeByteArray(it, 0, it.size) imageView.setImageBitmap(bitmap) } Ставь 👍 и забирай 📚 Базу знаний
304
13
🤔 Какие есть особенности использования Nothing в дженериках Nothing в Kotlin используется как нижняя граница для дженериков, что означает, что он совместим с любым типом, но не может иметь значений. Это позволяет указывать, что функция или выражение не возвращают никакого значения, например, в случае исключений или бесконечного цикла. Nothing удобно использовать в дженериках для создания функций, которые гарантированно завершат выполнение программы или выбросят исключение. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний
306
14
🤔 Как LeakCanary находит ссылку на уничтоженную Activity? Использует комбинацию слабых ссылок (WeakReference), анализ кучи и цепочек удержания, чтобы найти ссылки на уничтоженные Activity или другие объекты, которые должны были быть освобождены сборщиком мусора. Вот более подробное объяснение того, как это работает: 🚩Шаги 1⃣Создание слабой ссылки (WeakReference) Когда Activity (или другой компонент) уничтожается, LeakCanary создает слабую ссылку на этот объект. val weakReference = WeakReference(activity) 2⃣Ожидание сборки мусора (Garbage Collection) LeakCanary инициирует сборку мусора, чтобы попытаться освободить уничтоженный объект. System.gc() System.runFinalization() 3⃣Проверка слабой ссылки LeakCanary проверяет, освобождена ли слабая ссылка. Если объект не был освобожден, это указывает на возможную утечку памяти. if (weakReference.get() != null) { // Объект все еще удерживается в памяти, вероятна утечка } 4⃣Снимок кучи (Heap Dump) Если слабая ссылка не была освобождена, LeakCanary создает снимок кучи (heap dump) для дальнейшего анализа. val heapDumpFile = createHeapDump() 5⃣Анализ кучи с помощью Shark LeakCanary использует библиотеку Shark для анализа снимка кучи. Shark проверяет все объекты в куче и их удерживающие ссылки. val heapAnalyzer = HeapAnalyzer() val analysis = heapAnalyzer.analyze(heapDumpFile) Ставь 👍 и забирай 📚 Базу знаний
362
15
🤔 Что такое px, dp, sp? В Android используются разные единицы измерения для работы с экраном, чтобы адаптировать UI под разные устройства. 🚩`px` (пиксели) – физические точки экрана px (pixel) – это самая маленькая единица изображения на экране. Не зависит от плотности экрана → на разных устройствах элементы могут выглядеть слишком маленькими или большими. <TextView android:layout_width="100px" android:layout_height="50px"/> 🚩`dp` (density-independent pixels) – независимые от плотности пиксели dp (density-independent pixel) – это абстрактная единица измерения, которая адаптируется под плотность экрана (dpi). Рекомендуется для всех размеров UI, чтобы интерфейс выглядел одинаково на разных устройствах. <TextView android:layout_width="100dp" android:layout_height="50dp"/> Пример в Kotlin val sizeInPx = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 100f, resources.displayMetrics ) 🚩`sp` (scale-independent pixels) – масштабируемые пиксели (для текста) sp (scale-independent pixel) – как dp, но ещё учитывает настройки шрифта пользователя. Используется только для размеров шрифтов. <TextView android:text="Привет, мир!" android:textSize="16sp"/> Пример в Kotlin val textSizeInPx = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, 16f, resources.displayMetrics ) Ставь 👍 и забирай 📚 Базу знаний
389
16
🤔 Что такое утечки памяти? Это ситуация, когда приложение выделяет память для каких-либо объектов, но затем не освобождает её, даже когда эти объекты больше не нужны. Это приводит к тому, что используемая память накапливается, что может со временем замедлить работу приложения и привести к его аварийному завершению (крашу) из-за нехватки доступной памяти. 🚩Почему происходят утечки памяти в Android? В Android утечки памяти могут возникнуть из-за особенностей работы виртуальной машины (ART или Dalvik), а также из-за того, что сборщик мусора (Garbage Collector, GC) не может освободить память для объектов, на которые по-прежнему существуют ссылки. Это происходит в следующих случаях: 🟠Долгоживущие ссылки Если объект ссылается на другой объект, который больше не нужен, последний не может быть освобождён GC. Например: Статические ссылки, которые продолжают "удерживать" объект. Замыкания (closures), которые хранят ссылки на контекст активности. 🟠Неявные ссылки Некоторые объекты системы Android, такие как Context, View, Handler, хранят ссылки на компоненты приложения (например, Activity), из-за чего их нельзя освободить. 🟠Неправильное использование API Ошибки, такие как регистрация слушателей (listeners) без последующей отписки, использование таймеров, которые продолжают работать даже после уничтожения активности, и так далее. 🚩Пример утечки памяти public class MyActivity extends AppCompatActivity { private static TextView myTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myTextView = findViewById(R.id.my_text_view); myTextView.setText("Hello, Memory Leak!"); } } Исправленный код public class MyActivity extends AppCompatActivity { private TextView myTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myTextView = findViewById(R.id.my_text_view); myTextView.setText("Hello, World!"); } } 🚩Как избежать утечек памяти? 🟠Избегайте долгоживущих ссылок на объекты активности или контекста Не используйте static для объектов, ссылающихся на Context или Activity. Используйте WeakReference, если нужно сохранить ссылку, которая не должна блокировать сборщик мусора. 🟠Отключайте слушателей и callback-методы Если вы регистрируете слушателей (например, через setOnClickListener), обязательно удаляйте их в методах жизненного цикла, например, в onDestroy(). 🟠Осторожно используйте анонимные классы и лямбда-выражения Анонимные классы (например, Runnable, Handler) могут неявно хранить ссылки на внешние классы, что может привести к утечке памяти. 🟠Используйте инструменты для выявления утечек памяти Android Profiler: встроенный инструмент Android Studio для мониторинга использования памяти. LeakCanary: библиотека, которая автоматически обнаруживает утечки памяти в вашем приложении. Ставь 👍 и забирай 📚 Базу знаний
355
17
🤔 Как лучше всего использовать модификаторы доступа в Java? - private — для инкапсуляции данных внутри класса; - protected — если нужно разрешить доступ наследникам и классам в пакете; - public — для API, доступного извне; - package-private (без модификатора) — для доступа внутри одного пакета. Лучше всего ограничивать доступ настолько, насколько это возможно. Ставь 👍 если знал ответ, 🔥 если нет Забирай 📚 Базу знаний
355
18
🤔 Вставка значения быстрее в Linked list или в Array list? 🚩Вставка в `ArrayList` ArrayList реализован на основе массива. 🟠Вставка в конец Амортизированное O(1). Если массив не заполнен, элемент добавляется в конец списка. В случае, если массив заполнен, требуется выделение нового массива и копирование элементов, что занимает O(n) времени. val arrayList = ArrayList<Int>() arrayList.add(1) // Быстрая вставка в конец 🟠Вставка в начало или середину O(n). Необходимо сдвинуть все элементы, начиная с позиции вставки, чтобы освободить место для нового элемента. Это требует времени, пропорционального количеству элементов после позиции вставки. arrayList.add(0, 2) // Медленная вставка в начало arrayList.add(1, 3) // Медленная вставка в середину 🚩Вставка в LinkedList LinkedList реализован на основе узлов, где каждый узел содержит ссылку на следующий и/или предыдущий узел (в случае двусвязного списка). 🟠Вставка в начало: O(1). Для добавления элемента в начало достаточно изменить ссылки первого узла и нового узла. val linkedList = LinkedList<Int>() linkedList.addFirst(1) // Быстрая вставка в начало 🟠Вставка в конец O(1) (если есть ссылка на последний узел) или O(n) (если необходимо пройти весь список). Если список двусвязный и хранится ссылка на последний элемент, вставка в конец осуществляется за O(1). В противном случае, требуется пройти весь список до конца. linkedList.addLast(2) // Быстрая вставка в конец, если есть ссылка на последний узел 🟠Вставка в середину O(n). Необходимо пройти список до нужной позиции и изменить ссылки узлов. linkedList.add(1, 3) // Медленная вставка в середину Ставь 👍 и забирай 📚 Базу знаний
379
19
🤔 Из каких основных элементов состоит Dagger? Dagger – это фреймворк для внедрения зависимостей (DI), который помогает управлять созданием и передачей объектов в приложении. В основе Dagger лежат граф зависимостей, который состоит из следующих ключевых элементов: 🚩`@Module` и `@Provides` – создание зависимостей @Module – это класс, который предоставляет зависимости. @Provides – аннотация, указывающая, что метод создаёт объект для графа Dagger. @Module class NetworkModule { @Provides fun provideRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl("https://api.example.com") .build() } } 🟠`@Inject` – внедрение зависимостей @Inject в конструкторе – позволяет Dagger автоматически создавать объект без @Module. class ApiService @Inject constructor(private val retrofit: Retrofit) { fun fetchData() { /*...*/ } } 🟠`@Component` – связывание модулей и мест внедрения @Component – это интерфейс, который соединяет модули и классы, где нужны зависимости. @Component(modules = [NetworkModule::class]) interface AppComponent { fun inject(activity: MainActivity) // Указываем, куда внедрять зависимости } 🟠`@Singleton` – область жизни объекта Используется для создания одного экземпляра объекта на всё приложение. @Singleton @Component(modules = [NetworkModule::class]) interface AppComponent 🟠`@Binds` – привязка интерфейса к реализации Если у нас есть интерфейс и его реализация, лучше использовать @Binds вместо @Provides. interface Repository { fun getData(): String } class RepositoryImpl @Inject constructor() : Repository { override fun getData() = "Data from repository" } @Module abstract class RepositoryModule { @Binds abstract fun bindRepository(repo: RepositoryImpl): Repository } 🟠`Subcomponent` – вложенные компоненты Если нужно создать зависимости с разными областями жизни (например, Activity, Fragment), используют @Subcomponent. @Subcomponent interface ActivityComponent { fun inject(activity: MainActivity) } Ставь 👍 и забирай 📚 Базу знаний
392
20
🤔 В андроиде существуют мэпы, в которые можно положить примитивные типы? Да! В Android есть специальные Map-коллекции, которые позволяют хранить примитивные типы (int, long, boolean и т. д.) без автоупаковки (autoboxing). 🚩Почему это важно? Обычные HashMap<Int, Int> в Kotlin используют автоупаковку (Integer вместо int), что: Увеличивает потребление памяти (из-за объектов Integer, Long и т. д.). Замедляет работу (из-за ненужного создания объектов). Решение? Использовать специализированные мэпы из android.util! 🚩`SparseArray` (замена `HashMap<Int, Any?>`) Хранит пары Int → Any, но без автоупаковки. import android.util.SparseArray val sparseArray = SparseArray<String>() sparseArray.put(1, "Привет") sparseArray.put(2, "Мир") println(sparseArray[1]) // Привет println(sparseArray[2]) // Мир 🚩`SparseIntArray` (замена `HashMap<Int, Int>`) Хранит пары 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 🚩`SparseBooleanArray` (замена `HashMap<Int, Boolean>`) Оптимизирован для 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 🚩`LongSparseArray<T>` (замена `HashMap<Long, T>`) Оптимизирован для Long → Any?, аналог SparseArray, но с Long ключами. import android.util.LongSparseArray val longSparseArray = LongSparseArray<String>() longSparseArray.put(10000000000L, "Длинный ключ") println(longSparseArray[10000000000L]) // Длинный ключ Ставь 👍 и забирай 📚 Базу знаний
402