Forwarded from easyoffer
Я боялся, что провалю собеседование. Так появился easyoffer
Когда я только начинал искать первую работу программистом, меня пугала мысль, что я просто не смогу ответить на вопросы на собеседовании.
Типа… ты потратил месяцы на то, чтобы учиться, писал pet-проекты, собирал резюме, рассылаешь отклики — и всё может закончиться на одном-единственном вопросе, на который ты не знаешь ответ.
Я реально боялся.
Я смотрел видео mock-собеседований на YouTube, останавливал каждое, выписывал вопросы в Notion. Потом вручную писал к ним ответы. И потом ещё по нескольку раз перечитывал. Такой вот "тренажёр" на коленке.
📎 (там на картинке — один из моих реальных списков в Notion, ставь 🔥 если тоже так делал)
В какой-то момент я посчитал — у меня уже было выписано больше 500 вопросов. Я почувствовал ужас.
Потому что невозможно всё это зазубрить. А что, если спросят как раз тот, к которому я не успел подготовиться?..
Тогда и пришла идея
А что если понять, какие из вопросов встречаются чаще всего? Чтобы не учить всё подряд, а сфокусироваться на главном.
Так родился easyoffer.
Сначала — просто как пет-проект, чтобы показать в резюме и подготовиться к собесам. А потом оказалось, что он реально помогает людям. За первые месяцы его посетили сотни тысяч человек. И я понял: это больше, чем просто пет-проект.
Сейчас я делаю EasyOffer 2.0
И уже не один, а вместе с вами.
В новой версии будут:
– вопросы из реальных собесов, с фильтрацией по грейду, компании, типу интервью
– тренажёр с карточками (по принципу интервальных повторений — как в Anki)
– база задач с интервью
– тренажёр «реальное собеседование», чтобы отрепетировать как в жизни
Каждая фича упрощает и сокращает время на подготовку. Все эти штуки я бы мечтал иметь, когда сам готовился к собеседованиям.
Я делаю всё на свои деньги. Никаких инвесторов. Только вы и я.
Если вы хотите помочь — сейчас самое важное время.
Краудфандинг уже стартовал. Благодаря нему я смогу привлечь больше людей для разработки, сбору и обработки собеседований.
Все, кто поддержат проект до релиза, получат:
🚀 1 год PRO-доступа по цене месячной подписки. Его можно активировать в любое время, например когда начнете готовится к собесам.
➕ Доступ к закрытому бета-тесту
Поддержать 👉 https://planeta.ru/campaigns/easyoffer
Спасибо, что верите в этот проект 🙌
Когда я только начинал искать первую работу программистом, меня пугала мысль, что я просто не смогу ответить на вопросы на собеседовании.
Типа… ты потратил месяцы на то, чтобы учиться, писал pet-проекты, собирал резюме, рассылаешь отклики — и всё может закончиться на одном-единственном вопросе, на который ты не знаешь ответ.
Я реально боялся.
Я смотрел видео mock-собеседований на YouTube, останавливал каждое, выписывал вопросы в Notion. Потом вручную писал к ним ответы. И потом ещё по нескольку раз перечитывал. Такой вот "тренажёр" на коленке.
📎 (там на картинке — один из моих реальных списков в Notion, ставь 🔥 если тоже так делал)
В какой-то момент я посчитал — у меня уже было выписано больше 500 вопросов. Я почувствовал ужас.
Потому что невозможно всё это зазубрить. А что, если спросят как раз тот, к которому я не успел подготовиться?..
Тогда и пришла идея
А что если понять, какие из вопросов встречаются чаще всего? Чтобы не учить всё подряд, а сфокусироваться на главном.
Так родился easyoffer.
Сначала — просто как пет-проект, чтобы показать в резюме и подготовиться к собесам. А потом оказалось, что он реально помогает людям. За первые месяцы его посетили сотни тысяч человек. И я понял: это больше, чем просто пет-проект.
Сейчас я делаю EasyOffer 2.0
И уже не один, а вместе с вами.
В новой версии будут:
– вопросы из реальных собесов, с фильтрацией по грейду, компании, типу интервью
– тренажёр с карточками (по принципу интервальных повторений — как в Anki)
– база задач с интервью
– тренажёр «реальное собеседование», чтобы отрепетировать как в жизни
Каждая фича упрощает и сокращает время на подготовку. Все эти штуки я бы мечтал иметь, когда сам готовился к собеседованиям.
Я делаю всё на свои деньги. Никаких инвесторов. Только вы и я.
Если вы хотите помочь — сейчас самое важное время.
Краудфандинг уже стартовал. Благодаря нему я смогу привлечь больше людей для разработки, сбору и обработки собеседований.
Все, кто поддержат проект до релиза, получат:
🚀 1 год PRO-доступа по цене месячной подписки. Его можно активировать в любое время, например когда начнете готовится к собесам.
➕ Доступ к закрытому бета-тесту
Поддержать 👉 https://planeta.ru/campaigns/easyoffer
Спасибо, что верите в этот проект 🙌
👍2🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3😁3👀1
Подключение
BroadcastReceiver в Android состоит из двух основных шагов: создание самого ресивера и его регистрация. Ресивер можно зарегистрировать как статически в манифесте, так и динамически в коде.Создадим простой
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 class — класс для хранения данных с автогенерацией equals(), hashCode(), copy(). sealed class — ограниченная иерархия классов, используется для when. 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 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}")
}
}Да! Это лучший вариант для управления состояниями:
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
- Жестовое управление.
- Поддержка 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
Основные ветки:
main (стабильная версия, релизы). develop (основная ветка разработки). Временные ветки:
feature/* (новые фичи, мерджатся в develop). release/* (готовится релиз, тестирование, фикс багов). hotfix/* (критические фиксы в main). Схема Git Flow:
main ──── hotfix ─▶️ merge ────▶️ main
│
├── develop ─▶️ release ─▶️ merge ─▶️ main
│ │
├── feature/1
├── feature/2
Только две основные ветки:
main (всегда стабильная версия). Фичи разрабатываются в
feature/* и сразу мерджатся в main. Деплой возможен сразу после мерджа в
main. Схема GitHub Flow
main ────▶️ feature/1 ─▶️ merge ─▶️ main ─▶️ deploy
└── feature/2 ─▶️ merge ─▶️ main ─▶️ deploy
main – стабильная ветка (готовая к продакшену). develop (опционально) – если нужно тестирование перед main. feature/* – для разработки новых фич. production, staging – если нужно разделение сред. hotfix/* – фиксы продакшена. main ────▶️ production
│
├── staging ───▶️ merge ─▶️ main
│
├── feature/1 ─▶️ merge ─▶️ staging
├── feature/2 ─▶️ merge ─▶️ staging
Разработчики работают прямо в
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
Да, ошибки (
Exceptions) обрабатываются по-разному в launch и async! 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<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👍3❤1
Гарантируется только в том случае, если структура данных поддерживает порядок (например, List, LinkedList).
Если коллекция неупорядоченная (например, HashSet, HashMap.keySet()), порядок может быть произвольным и не повторяться.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Передача фото в редактор зависит от типа редактора:
1. Внешний редактор (например, Google Photos, Snapseed).
2. Встроенный редактор внутри приложения.
Если редактор — другое приложение, используем
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, "Выберите редактор"))
}Если редактор — внутри приложения, можно передавать фото через
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[]), можно передать его как 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
- Можно использовать SurfaceView — он предоставляет отдельный буфер, который можно обновлять вне основного потока.
- Также используется Canvas через SurfaceHolder.lockCanvas().
Подходит для игр, видео или сложной анимации.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
Nothing — это специальный bottom type (нижний тип), который означает: Функция никогда не возвращает результат (
throw, error()). Код после
Nothing недостижим. fun fail(): Nothing {
throw IllegalStateException("Ошибка!") // Никогда не возвращает значение
}Все классы в Kotlin могут иметь инстансы (объекты), кроме
Nothing. Nothing нельзя создать (instantiate), потому что он не имеет конструктора. Любая переменная типа
Nothing просто не существует. val x: Nothing = Nothing() // ❌ Ошибка: у Nothing нет конструктора
Используется в
throwfun fail(): Nothing = throw IllegalArgumentException("Ошибка")Используется в
TODO()fun getData(): String {
TODO("Функция ещё не реализована")
}Используется в
if-else, где один вариант throwfun getValue(x: Int): String {
return if (x > 0) "Позитивное число" else throw IllegalArgumentException("Отрицательное!")
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
Forwarded from easyoffer
⏳ Осталось всего 14 дней до завершения краудфандинга
Сейчас самое подходящее время подключиться, если вы ждали или откладывали:
Все, кто поддержат проект сейчас, до релиза, получат:
🚀 PRO-доступ на 1 год по цене месячной подписки
➕ Бета-доступ к EasyOffer 2.0 (конец мая)
👉 Поддержать: https://planeta.ru/campaigns/easyoffer
Сейчас самое подходящее время подключиться, если вы ждали или откладывали:
Все, кто поддержат проект сейчас, до релиза, получат:
🚀 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
Подписывание и оптимизация
Установка и запуск на устройстве
Компиляция 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
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 (новый формат 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, которая не имеет цифровой подписи.
- Такой APK можно запустить только в эмуляторе или при отладке (
debug build). - Google Play не принимает неподписанные APK.
При сборке
debug-версии в Android Studio: gradlew assembleDebug
Попытка установить неподписанный APK
adb install app-unsigned.apk
Ошибка
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]
- Подписанный 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