Kotlin | Вопросы собесов
2.56K subscribers
30 photos
970 links
Download Telegram
🤔 Что такое UI tred и Worker tred?

В Android есть два основных типа потоков:
1. UI Thread (главный поток) → отвечает за интерфейс и обработку событий.
2. Worker Thread (фоновый поток) → используется для долгих операций (запросы в сеть, работа с БД).

🚩`UI Thread` – главный поток

- Отвечает за отрисовку интерфейса (Activity, Fragment, View).
- Обрабатывает нажатия, свайпы, анимации.
- Все setText(), setImageResource() и другие UI-методы работают только здесь. Если делать тяжёлые операции в UI Thread, приложение зависнет (ANR - Application Not Responding)!
val url = URL("https://example.com")
val connection = url.openConnection() as HttpURLConnection // Ошибка!


🚩`Worker Thread` – фоновый поток

- Выполняет долгие операции, не блокируя UI:
Запросы в сеть (Retrofit, OkHttp)
Чтение/запись в БД (Room, SQLite)
Обработку файлов, JSON, XML
Любые тяжёлые вычисления
CoroutineScope(Dispatchers.IO).launch {
val url = URL("https://example.com")
val connection = url.openConnection() as HttpURLConnection // Работает!
}


🚩Как переключаться между `UI Thread` и `Worker Thread`?
Способ 1: Coroutines (лучший вариант)
CoroutineScope(Dispatchers.IO).launch {
val data = fetchData() // Работает в `Worker Thread`

withContext(Dispatchers.Main) {
textView.text = data // Назад в `UI Thread`
}
}


Способ 2: Handler + Thread (старый вариант)
val handler = Handler(Looper.getMainLooper())

Thread {
val data = fetchData() // Работает в `Worker Thread`

handler.post {
textView.text = data // Назад в `UI Thread`
}
}.start()


Способ 3: AsyncTask (УСТАРЕЛ, НЕ ИСПОЛЬЗОВАТЬ!)
class MyTask : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg params: Void?): String {
return fetchData() // `Worker Thread`
}

override fun onPostExecute(result: String) {
textView.text = result // `UI Thread`
}
}


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

В зависимости от типа сервиса:
- Для обычного (started) сервиса:
- Внутри самого сервиса:
Для bound service — вызывается unbindService().


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊2🔥1🤔1
Forwarded from easyoffer
Ура, друзья! Изиоффер переходит в публичное бета-тестирование!

🎉 Что нового:
🟢Анализ IT собеседований на основе 4500+ реальных интервью
🟢Вопросы из собеседований с вероятностью встречи
🟢Видео-примеры ответов на вопросы от Senior, Middle, Junior грейдов
🟢Пример лучшего ответа
🟢Задачи из собеседований
🟢Тестовые задания
🟢Примеры собеседований
🟢Фильтрация всего контента по грейдам, компаниям
🟢Тренажер подготовки к собеседованию на основе интервальных повторений и флеш карточек
🟡Тренажер "Реальное собеседование" с сценарием вопросов из реальных собеседований (скоро)
🟢Автоотклики на HeadHunter
🟢Закрытое сообщество easyoffer


💎 Акция в честь открытия для первых 500 покупателей:
🚀 Скидка 50% на PRO тариф на 1 год (15000₽ → 7500₽)

🔥 Акция уже стартовала! 👉 https://easyoffer.ru/pro
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое софткод?

Софткод (Softcode, Soft Coding) — это подход к программированию, при котором логика программы хранится в конфигурационных файлах, базе данных или других внешних источниках, а не жёстко (hardcoded) прописана в коде.

🚩Разница между Hardcode и Softcode

Пример Hardcode (жёстко зашито в коде)
val apiUrl = "https://api.example.com" //  Если URL изменится, надо менять код


Пример Softcode (гибко через конфигурацию)
val apiUrl = Config.get("api_url") //  Загружается из настроек


🚩Где используется софткод?

Конфигурационные файлы (config.json, .properties, .xml).
База данных (логика, настройки, права пользователей).
API и сервер (получение UI-элементов, бизнес-логики с сервера).
Скриптовые языки (скрипты загружаются динамически).

🚩Примеры использования Softcode в Android

Softcode через SharedPreferences (конфигурация в памяти)
val sharedPrefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val theme = sharedPrefs.getString("app_theme", "light") // Загружаем тему из настроек


Softcode через remoteConfig (Firebase Remote Config)
val minVersion = Firebase.remoteConfig.getInt("min_supported_version")


Softcode через JSON-файл (читаем конфиг из assets)
fun getConfigValue(context: Context, key: String): String {
val json = context.assets.open("config.json").bufferedReader().use { it.readText() }
val jsonObject = JSONObject(json)
return jsonObject.getString(key) // Получаем значение из JSON
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Разница между git pull и git fetch?

- git fetch — загружает изменения из удалённого репозитория, но не сливает их. Используется для предварительного обзора.
- git pull = fetch + merge — загружает и сразу применяет изменения к текущей ветке.


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

XML — это eXtensible Markup Language (Расширяемый язык разметки).

🚩Что означает расшифровка?

eXtensible → Расширяемый (можно создавать свои теги).
Markup → Разметка (использует теги <tag> для структурирования данных).
Language → Язык (формат хранения и передачи данных).

🚩Где используется XML?

В Android:
Разметка UI (activity_main.xml).
Конфигурация (AndroidManifest.xml).
Ресурсы (strings.xml, colors.xml).

В веб-разработке:
Конфигурации (.xml файлы в серверных приложениях).
Форматы обмена данными (SOAP, RSS, SVG).

Пример XML-кода
<user>
<name>Алиса</name>
<age>25</age>
</user>


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

Hilt позволяет внедрять контекст с помощью аннотаций
@ApplicationContext или @ActivityContext, чтобы точно указать, какой именно контекст требуется для зависимости.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Можно ли создать анонимный класс внутри inline функции?

Анонимный класс внутри inline-функции будет инстанцироваться при каждом вызове функции, что ломает цель inline — избавляться от накладных расходов на объекты.

🚩Что такое анонимный класс в Kotlin?

Анонимный класс — это класс без имени, который можно создать с помощью object : Interface {}.
interface ClickListener {
fun onClick()
}

fun main() {
val listener = object : ClickListener {
override fun onClick() {
println("Кнопка нажата!")
}
}

listener.onClick()
}


🚩Анонимный класс в `inline`-функции

Если мы создадим анонимный класс внутри 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
👍2
🤔 Что известно про heap pollution (загрязнение heap-а)?

Это ситуация, когда в куче (heap) оказывается объект неправильного типа из-за неправильного использования дженериков. Это может привести к ClassCastException во время выполнения.
Причины загрязнения:
- Использование необработанных типов (raw types), например List вместо List<String>.
- Нарушение type safety при кастах ((List<Integer>) obj).
- Использование varargs с дженериками (List<T>... args).


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

🟠Null Safety (Безопасность null)
Переменные по умолчанию не могут быть null, что предотвращает NullPointerException.
var a: String = "abc"
var b: String? = "abc"
b = null // Допустимо


🟠Коллекции (Collections)
Разделение на изменяемые и неизменяемые коллекции.
val list: List<String> = listOf("a", "b", "c")  // Неизменяемый список
val mutableList: MutableList<String> = mutableListOf("a", "b", "c") // Изменяемый список


🟠Data Classes (Классы данных)
Автоматическое создание методов equals(), hashCode(), и toString().
data class User(val name: String, val age: Int)   


🟠Smart Casts (Умные приведения типов)
Автоматическое приведение типа после проверки с помощью is.
fun demo(x: Any) {
if (x is String) {
println(x.length)
}
}


🟠Sealed Classes (Запечатанные классы)
Упрощают обработку ограниченных иерархий классов.
sealed class Expr
data class Const(val number: Double) : Expr()


🟠Выведение типов (Type Inference)
Kotlin автоматически определяет тип переменной.
val x = 10  // Int


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Зачем нужен scope в Dagger?

Scope управляет временем жизни объектов, создаваемых Dagger. Он определяет, сколько времени объект будет существовать в зависимости от компонента, к которому он привязан. Это позволяет:
1. Контролировать память: избегать лишнего создания объектов.
2. Гарантировать единственный экземпляр: например,
@Singleton обеспечивает создание объекта один раз на компонент.
3. Разделять области ответственности: создавать зависимости, привязанные к жизненному циклу Activity, Fragment, ViewModel и т.д.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥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
👍3
🤔 Для чего нужны фрагменты, если есть Activity?

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

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

Самый обычный сервис, который наследуется от класса Service, по умолчанию запускается в главном потоке приложения, который также называется UI-потоком (User Interface Thread). Это означает, что все операции, выполняемые в сервисе, включая методы onStartCommand(), onCreate(), и onBind(), выполняются в главном потоке. Если в сервисе будут выполняться длительные или ресурсоемкие операции, такие как сетевые запросы, обработка больших данных или выполнение сложных вычислений, это может привести к "зависанию" пользовательского интерфейса и появлению сообщений о том, что приложение не отвечает (ANR — Application Not Responding).

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

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
🤔 Что такое 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"


🚩Плюсы

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

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

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

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 AAB (Android App Bundle) — в чём разница?

- APK — готовый исполняемый архив для конкретного устройства.
- AAB — содержит все ресурсы и DEX-коды для всех конфигураций, но не устанавливается напрямую.
Google Play генерирует APK под конкретное устройство, уменьшая размер загрузки.
Формат обязателен для публикации в Google Play с 2021 года.


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

Передача больших данных (например, изображений, видео, JSON) между Activity требует оптимального подхода, потому что:
Intent.putExtra() имеет ограничение по размеру (~1MB).
Передача Bitmap в Intent может вызвать TransactionTooLargeException.
Большие данные лучше передавать через Uri, БД или FileProvider.

Неправильный способ (НЕ ДЕЛАТЬ!) – Bitmap через Intent
val bitmap: Bitmap = getBitmap()
val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image", bitmap) // ОПАСНО! Может вызвать Exception
startActivity(intent)


🚩Лучший способ – передача через `Uri` (`FileProvider`)

Сохраняем изображение во File и получаем Uri
fun saveBitmapToFile(context: Context, bitmap: Bitmap): Uri {
val file = File(context.cacheDir, "image.png")
file.outputStream().use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
}
return FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
}


Передаём Uri через Intent
val uri = saveBitmapToFile(this, bitmap)
val intent = Intent(this, ImageActivity::class.java).apply {
putExtra("image_uri", uri.toString())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Даем доступ другому Activity
}
startActivity(intent)


Получаем Uri в ImageActivity и загружаем изображение
val uriString = intent.getStringExtra("image_uri")
val uri = Uri.parse(uriString)

val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
imageView.setImageBitmap(bitmap)


🚩Альтернативный способ – через Базу Данных (`Room`)

Если изображение уже хранится в базе данных (Room), передаём ID записи, а не сам файл.
val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image_id", imageId) // Передаём только ID
startActivity(intent)


🚩Альтернативный способ – через `SharedPreferences` (только путь к файлу!)
Сохраняем путь
val filePath = saveBitmapToFile(this, bitmap).toString()
getSharedPreferences("app_prefs", MODE_PRIVATE).edit()
.putString("last_image", filePath)
.apply()


Читаем путь в Activity
val filePath = getSharedPreferences("app_prefs", MODE_PRIVATE)
.getString("last_image", null)
val bitmap = BitmapFactory.decodeFile(filePath)
imageView.setImageBitmap(bitmap)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Какой самый экономичный тип графического файла?

Формат WebP является наиболее экономичным с точки зрения сжатия без потери качества. Он лучше по сжатию, чем PNG или JPEG, поддерживает прозрачность и используется во многих Android-приложениях.


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

🟠Profile GPU Rendering
Этот инструмент показывает время, затраченное на отрисовку каждого кадра. Использование этого инструмента позволяет выявить "тяжелые" кадры и измерить улучшения после оптимизации.

1⃣Включите режим разработчика на устройстве.
2⃣Перейдите в "Настройки" -> "Для разработчиков".
3⃣Включите опцию "Profile GPU Rendering" и выберите "On screen as bars".
4⃣Запустите ваше приложение и наблюдайте за графиками. Зеленые линии указывают на время отрисовки, и ваша цель - оставаться ниже 16 мс (для 60 кадров в секунду).

🟠Android Profiler
Предоставляет набор инструментов для анализа производительности приложения.

1⃣ Откройте Android Studio и запустите ваше приложение.
2⃣Перейдите в "View" -> "Tool Windows" -> "Profiler".
3⃣Выберите ваше устройство и запущенное приложение.
4⃣Используйте вкладки CPU, Memory, Network и Energy для анализа различных аспектов производительности.
5⃣Сравните данные до и после оптимизации.

🟠Benchmarking
Создание и использование тестов производительности помогает количественно оценить улучшения. Вы можете использовать библиотеку Jetpack Benchmark для создания и выполнения тестов производительности.

🚩Пример использования Jetpack Benchmark:

1⃣Добавьте зависимости в build.gradle:
dependencies {
androidTestImplementation "androidx.benchmark:benchmark-junit4:1.1.0"
androidTestImplementation "androidx.test:runner:1.3.0"
androidTestImplementation "androidx.test:rules:1.3.0"
}


2⃣Создайте класс теста:
import androidx.benchmark.junit4.BenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ExampleBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()

@Test
fun myFunctionBenchmark() {
benchmarkRule.measureRepeated {
// Вызов вашей функции или кода для тестирования производительности
myFunction()
}
}
}


🟠Logcat
Используйте журналирование для измерения времени выполнения определенных операций.
val startTime = System.currentTimeMillis()
// Ваш код
val endTime = System.currentTimeMillis()
Log.d("Performance", "Время выполнения: ${endTime - startTime} мс")


🟠StrictMode
StrictMode помогает обнаружить операции, которые могут замедлить работу приложения, такие как работа с сетью или базой данных в главном потоке.
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
}


🟠Systrace
Systrace позволяет собирать и анализировать трассировки производительности системы, предоставляя детализированные данные о времени выполнения различных операций.

1⃣Включите режим разработчика на устройстве.
2⃣В "Настройки" -> "Для разработчиков" включите "Enable GPU Debug Layers".
3⃣Запустите команду adb shell am broadcast -a com.android.systemui.screenshot.ScreenshotService.ACTION_SYSTRACE.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Чем background service отличается от foreground service?

– Background service работает в фоне, может быть остановлен системой при нехватке ресурсов.
– Foreground service требует уведомление и имеет приоритет, не убивается системой просто так.


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