Kotlin | Вопросы собесов
2.56K subscribers
29 photos
968 links
Download Telegram
Forwarded from easyoffer
Я боялся, что провалю собеседование. Так появился easyoffer

Когда я только начинал искать первую работу программистом, меня пугала мысль, что я просто не смогу ответить на вопросы на собеседовании.

Типа… ты потратил месяцы на то, чтобы учиться, писал pet-проекты, собирал резюме, рассылаешь отклики — и всё может закончиться на одном-единственном вопросе, на который ты не знаешь ответ.

Я реально боялся.
Я смотрел видео mock-собеседований на YouTube, останавливал каждое, выписывал вопросы в Notion. Потом вручную писал к ним ответы. И потом ещё по нескольку раз перечитывал. Такой вот "тренажёр" на коленке.

📎 (там на картинке — один из моих реальных списков в Notion, ставь 🔥 если тоже так делал)

В какой-то момент я посчитал — у меня уже было выписано больше 500 вопросов. Я почувствовал ужас.
Потому что невозможно всё это зазубрить. А что, если спросят как раз тот, к которому я не успел подготовиться?..

Тогда и пришла идея

А что если понять, какие из вопросов встречаются чаще всего? Чтобы не учить всё подряд, а сфокусироваться на главном.

Так родился easyoffer.

Сначала — просто как пет-проект, чтобы показать в резюме и подготовиться к собесам. А потом оказалось, что он реально помогает людям. За первые месяцы его посетили сотни тысяч человек. И я понял: это больше, чем просто пет-проект.

Сейчас я делаю EasyOffer 2.0
И уже не один, а вместе с вами.

В новой версии будут:
– вопросы из реальных собесов, с фильтрацией по грейду, компании, типу интервью
– тренажёр с карточками (по принципу интервальных повторений — как в Anki)
– база задач с интервью
– тренажёр «реальное собеседование», чтобы отрепетировать как в жизни

Каждая фича упрощает и сокращает время на подготовку. Все эти штуки я бы мечтал иметь, когда сам готовился к собеседованиям.

Я делаю всё на свои деньги. Никаких инвесторов. Только вы и я.

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

Все, кто поддержат проект до релиза, получат:

🚀 1 год PRO-доступа по цене месячной подписки. Его можно активировать в любое время, например когда начнете готовится к собесам.
Доступ к закрытому бета-тесту

Поддержать 👉 https://planeta.ru/campaigns/easyoffer

Спасибо, что верите в этот проект 🙌
👍2🔥1
🤔 Как можно измерить размер проекта?

Размер проекта можно измерить в строках кода, количестве модулей или размере APK. Средний крупный проект имеет около 100K строк кода, разделённых на 5-10 модулей.

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

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

🚩Создание BroadcastReceiver

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

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


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

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

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

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

</application>

</manifest>


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

public class MainActivity extends AppCompatActivity {
private MyBroadcastReceiver myBroadcastReceiver;

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

myBroadcastReceiver = new MyBroadcastReceiver();
}

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

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


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

- ViewModel — проверка логики отображения, работы с LiveData/StateFlow.
- UseCase / Interactor — основная бизнес-логика.
- Repository (если изолирован от сети) — для проверки логики агрегации данных.
- Вспомогательные утилиты и мапперы — чтобы гарантировать корректность трансформации данных.


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

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

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

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

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

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


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

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

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


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

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


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

Android 9 (Pie) добавил:
- Жестовое управление.
- Поддержка ML Kit и нейросетевых API.
- Adaptive Battery и Adaptive Brightness.
- App Actions и предиктивные предложения.
- Поддержка notch-экранов (Display Cutout).


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

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

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

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

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


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

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


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

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

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

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


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

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


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

- Синхронно:
- Асинхронно (с отложенным исполнением):
commitNow() — выполняется немедленно в текущем потоке. Используется редко (например, в setup-методах).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
😁5👍1🔥1👀1
🤔 Есть ли отличия между launch и async в обработке ошибок?

Да, ошибки (Exceptions) обрабатываются по-разному в launch и async!

🚩Ошибки в `launch` – падают сразу

launch сразу выбрасывает исключение, и если нет try-catch, корутина завершает родительский CoroutineScope.
fun main() = runBlocking {
launch {
println("Начало работы")
throw RuntimeException("Ошибка в launch!")
}
delay(100) // Код не выполнится, так как `launch` упадёт
println("Этот код не выполнится")
}


Вывод в консоль
Начало работы
Exception in thread "main" java.lang.RuntimeException: Ошибка в launch!


Решение
Использовать try-catch внутри launch.
launch {
try {
throw RuntimeException("Ошибка в launch!")
} catch (e: Exception) {
println("Ошибка поймана: ${e.message}")
}
}


🚩Ошибки в `async` – остаются в `Deferred`, падают при `await()`

В async ошибка не выбрасывается сразу, а сохраняется в Deferred<T>. Она появится только при вызове await().
fun main() = runBlocking {
val deferred = async {
println("Начало работы async")
throw RuntimeException("Ошибка в async!")
}
delay(100) // Код выполнится, так как ошибка пока не выброшена
println("Этот код выполнится")

deferred.await() // 💥 Ошибка падает только здесь!
}


Вывод в консоль
Начало работы async
Этот код выполнится
Exception in thread "main" java.lang.RuntimeException: Ошибка в async!


Решение
Использовать try-catch при await().
try {
deferred.await()
} catch (e: Exception) {
println("Ошибка поймана: ${e.message}")
}


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

Гарантируется только в том случае, если структура данных поддерживает порядок (например, List, LinkedList).
Если коллекция неупорядоченная (например, HashSet, HashMap.keySet()), порядок может быть произвольным и не повторяться.


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

Передача фото в редактор зависит от типа редактора:
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)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Какой класс нужно использовать, чтобы отрисовывать View в background thread?

Нельзя напрямую рисовать UI из background потока. Но:
- Можно использовать SurfaceView — он предоставляет отдельный буфер, который можно обновлять вне основного потока.
- Также используется Canvas через SurfaceHolder.lockCanvas().
Подходит для игр, видео или сложной анимации.


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

Nothing — это специальный bottom type (нижний тип), который означает:
Функция никогда не возвращает результат (throw, error()).
Код после Nothing недостижим.
fun fail(): Nothing {
throw IllegalStateException("Ошибка!") // Никогда не возвращает значение
}


🚩Почему `Nothing` не имеет инстансов?

Все классы в Kotlin могут иметь инстансы (объекты), кроме Nothing.
Nothing нельзя создать (instantiate), потому что он не имеет конструктора.
Любая переменная типа Nothing просто не существует.
val x: Nothing = Nothing() //  Ошибка: у Nothing нет конструктора


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

Используется в throw
fun fail(): Nothing = throw IllegalArgumentException("Ошибка")


Используется в TODO()
fun getData(): String {
TODO("Функция ещё не реализована")
}


Используется в if-else, где один вариант throw
fun getValue(x: Int): String {
return if (x > 0) "Позитивное число" else throw IllegalArgumentException("Отрицательное!")
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
Forwarded from easyoffer
Осталось всего 14 дней до завершения краудфандинга

Сейчас самое подходящее время подключиться, если вы ждали или откладывали:

Все, кто поддержат проект сейчас, до релиза, получат:
🚀 PRO-доступ на 1 год по цене месячной подписки
Бета-доступ к EasyOffer 2.0 (конец мая)

👉 Поддержать: https://planeta.ru/campaigns/easyoffer
🤔 Перечислите маркерные интерфейс?

Маркерные интерфейсы не содержат методов, но обозначают поведение объекта.
Примеры в Java:
- Serializable — объект можно сериализовать.
- Cloneable — объект можно клонировать.
- Remote — используется для удалённых вызовов.
- RandomAccess — для коллекций с быстрым доступом по индексу.


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

Сборка 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


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍1
🤔 Все ли виды ссылок защищают объект от удаления?

Нет, не все ссылки защищают объект от удаления:
1. Strong Reference (сильная ссылка): защищает объект от удаления. Пока существует сильная ссылка, объект остаётся в памяти.
2. Weak Reference (слабая ссылка): объект может быть удалён сборщиком мусора, даже если на него есть слабая ссылка.
3. Soft Reference (мягкая ссылка): объект удаляется только при нехватке памяти.
4. Phantom Reference (фантомная ссылка): используется для выполнения действий после удаления объекта, объект недоступен через эту ссылку.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
🤔 В чём отличие АПК с подписью и без подписи?

APK (Android Package) — это архив с кодом приложения, ресурсами и манифестом.
Приложение должно быть подписано, чтобы его можно было установить на устройство.

🚩Неподписанный APK (`unsigned APK`)

- Это черновая версия APK, которая не имеет цифровой подписи.
- Такой APK можно запустить только в эмуляторе или при отладке (debug build).
- Google Play не принимает неподписанные APK.
При сборке debug-версии в Android Studio:
gradlew assembleDebug


Попытка установить неподписанный APK
adb install app-unsigned.apk


Ошибка
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]


🚩Подписанный APK (`signed APK`)

- Подписанный APK содержит цифровую подпись, которая гарантирует, что код не был изменён.
- Android проверяет ключ подписи перед установкой.
Google Play требует подписанный APK или AAB.
Как подписать APK вручную?
apksigner sign --ks my-release-key.jks --out app-signed.apk app-unsigned.apk


🚩Зачем нужна подпись?

Подпись APK гарантирует*
Целостность → код не был изменён после сборки.
Подлинность → приложение подписано разработчиком, а не злоумышленником.
Обновления → только приложения с тем же ключом могут обновлять старую версию.

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