Kotlin | Вопросы собесов
2.57K subscribers
28 photos
966 links
Download Telegram
🤔 Какие механизмы в Android позволяют сделать так, чтобы приложение не умерло?

- Foreground Service — работает даже в фоне, пока отображается уведомление.
- JobScheduler / WorkManager — выполняют задачи при оптимальных условиях, даже после перезапуска.
- AlarmManager — может запускать код даже после завершения приложения.
- BroadcastReceiver — перехватывает системные события (например, BOOT_COMPLETED).
- Persistence через SharedPreferences, Room — для восстановления состояния.
- Также важно не держать тяжёлую логику в UI и правильно обрабатывать onStop() и onDestroy().


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

Чтобы обрабатывать жесты в Android, используйте класс GestureDetector. Он помогает отслеживать стандартные жесты: одиночные нажатия, свайпы, долгие нажатия, двойные касания и т.д.

🚩Как использовать GestureDetector?

Создайте экземпляр GestureDetector, передав Context и слушателя (GestureDetector.OnGestureListener).
Передавайте события касания в gestureDetector.onTouchEvent(event) из метода onTouchEvent().
class GestureActivity : AppCompatActivity(), GestureDetector.OnGestureListener {
private lateinit var gestureDetector: GestureDetector

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gestureDetector = GestureDetector(this, this)
}

override fun onTouchEvent(event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
}

override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
val deltaX = e2!!.x - e1!!.x
if (deltaX > 0) Log.d("Gesture", "Swipe Right") else Log.d("Gesture", "Swipe Left")
return true
}

override fun onDown(e: MotionEvent?) = true
override fun onShowPress(e: MotionEvent?) {}
override fun onSingleTapUp(e: MotionEvent?) = false
override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float) = false
override fun onLongPress(e: MotionEvent?) {}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Разница PriorityQueue, Deque?

- PriorityQueue — структура, где элементы обрабатываются по приоритету, а не по порядку добавления.
- Deque (двусторонняя очередь) — позволяет добавлять/удалять элементы с обеих сторон. Поддерживает поведение как стека, так и очереди.


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

В Kotlin нет switch, но его заменяет более мощный оператор when.

Простой пример (аналог switch-case)
fun getDayName(day: Int): String {
return when (day) {
1 -> "Понедельник"
2 -> "Вторник"
3 -> "Среда"
4 -> "Четверг"
5 -> "Пятница"
6 -> "Суббота"
7 -> "Воскресенье"
else -> "Некорректный день"
}
}

fun main() {
println(getDayName(3)) // Среда
}


when без else (если учтены все случаи)
fun getStatus(code: Int): String = when (code) {
200 -> "OK"
404 -> "Not Found"
500 -> "Server Error"
}


Несколько значений в одном case
fun isWeekend(day: Int): Boolean {
return when (day) {
6, 7 -> true // Оба значения работают как один case
else -> false
}
}


🚩`when` как альтернатива `if-else`

Можно проверять логические выражения, а не просто числа.
fun checkNumber(x: Int): String {
return when {
x < 0 -> "Отрицательное число"
x == 0 -> "Ноль"
x > 0 -> "Положительное число"
else -> "Ошибка"
}
}


Использование when без аргумента
fun describe(obj: Any): String {
return when {
obj is String -> "Это строка"
obj is Int -> "Это число"
obj is Boolean -> "Это логический тип"
else -> "Неизвестный тип"
}
}


🚩`when` в качестве выражения

when можно использовать как выражение, возвращая результат:
val message = when (val code = 404) {
200 -> "OK"
404 -> "Not Found"
else -> "Unknown"
}
println(message) // Not Found


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

Рефлексия — это механизм, позволяющий программе исследовать и изменять свою структуру (классы, методы, поля) во время выполнения. Она позволяет вызывать приватные методы, создавать экземпляры классов по имени и т.д. Это мощный, но небезопасный и затратный по производительности инструмент.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 В чем разница между работой с list и работой с sequence?

🚩Основные структуры работы с коллекциями

🟠List
Является жадной коллекцией, что означает, что все операции над элементами выполняются немедленно и целиком. Когда вы применяете функции к списку, такие как map, filter и т.д., все элементы проходят через каждую функцию сразу же. В данном примере все элементы списка numbers сначала умножаются на 2, затем фильтруются. Вся работа выполняется сразу для каждого элемента.
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers
.map { it * 2 }
.filter { it > 5 }
println(result) // Output: [6, 8, 10]


🟠Sequence
Является ленивой коллекцией. Это означает, что элементы обрабатываются по мере необходимости. Функции, применяемые к последовательности, создают цепочку операций, которая выполняется только при обращении к элементам. В этом примере numbers сначала преобразуется в Sequence. Операции map и filter создают цепочку, которая выполняется только при преобразовании обратно в List с помощью toList(). Это позволяет избежать лишних операций и обрабатывать элементы по мере необходимости.
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers.asSequence()
.map { it * 2 }
.filter { it > 5 }
.toList()
println(result) // Output: [6, 8, 10]


🚩Различия

🟠Жадность против ленивости
List: Все элементы обрабатываются сразу при вызове функций.
Sequence: Элементы обрабатываются по мере необходимости, что может повысить эффективность.

🟠Производительность
List: Подходит для небольших коллекций, где выполнение всех операций сразу не критично.
Sequence: Эффективен для больших коллекций или длинных цепочек операций, так как уменьшает количество промежуточных коллекций.

🟠Использование памяти
List: Может использовать больше памяти из-за создания промежуточных коллекций.
Sequence: Уменьшает использование памяти, обрабатывая элементы по одному.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Откуда происходит повторный вызов composable функции?

Повторный вызов происходит из механизма "recomposition". Compose автоматически вызывает функцию снова, если состояние, связанное с этой функцией, изменилось.

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

Сильная ссылка - это ссылка, которая напрямую указывает на объект и предотвращает его сборку сборщиком мусора.

🚩Основные подходы

🟠WeakReference
Использование слабых ссылок (WeakReference) позволяет определить, был ли объект освобожден сборщиком мусора.
🟠ObjectWatcher (в LeakCanary)
LeakCanary использует ObjectWatcher для отслеживания объектов. Если объект не освобожден, ObjectWatcher уведомляет об утечке.

import java.lang.ref.WeakReference;

public class MemoryLeakExample {

public static void main(String[] args) {
// Создание объекта
MyObject myObject = new MyObject();

// Создание слабой ссылки на объект
WeakReference<MyObject> weakRef = new WeakReference<>(myObject);

// Удаление сильной ссылки
myObject = null;

// Вызов сборщика мусора
System.gc();

// Проверка, была ли слабая ссылка освобождена
if (weakRef.get() == null) {
System.out.println("Object has been garbage collected");
} else {
System.out.println("Object is still alive");
}
}

static class MyObject {
// Некоторая логика класса
}
}


🚩Понимание, что объект не используется

🟠Нет сильных ссылок
Убедитесь, что на объект нет сильных ссылок. Только слабые, мягкие или фантомные ссылки не предотвращают сборку объекта.
🟠Сборка мусора
Если объект становится недоступным через сильные ссылки, сборщик мусора может его освободить.
🟠Проверка с помощью WeakReference
Слабые ссылки могут быть использованы для проверки того, был ли объект освобожден.

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

Ты можешь использовать GridLayoutManager и задать количество колонок динамически

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

В Android есть несколько способов навигации между экранами. Давай разберём основные.

🟠Activity-based навигация
Каждый экран – это отдельная Activity, переход осуществляется с помощью Intent.
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)


🟠Fragment-based навигация
Один Activity содержит несколько Fragment, навигация через FragmentManager.
supportFragmentManager.beginTransaction()
.replace(R.id.container, SecondFragment())
.addToBackStack(null)
.commit()


🟠Navigation Component (Jetpack)
Google рекомендует Navigation Component для удобной работы с Fragment.
findNavController().navigate(R.id.action_firstFragment_to_secondFragment)


🟠Bottom Navigation / Tab Navigation
Используется BottomNavigationView или TabLayout для переключения между экранами.
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.home -> replaceFragment(HomeFragment())
R.id.profile -> replaceFragment(ProfileFragment())
}
true
}


🟠Drawer Navigation (Navigation Drawer)
Боковое меню (DrawerLayout) с пунктами навигации.
drawerLayout.openDrawer(GravityCompat.START)


🟠Deep Links и App Links
Навигация через ссылки (например, myapp://profile/123).
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="myapp" android:host="profile"/>
</intent-filter>


🟠Navigation в Jetpack Compose
Используется NavHost и NavController.
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("profile") { ProfileScreen(navController) }
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4💊1
🤔 Будет ли zip гарантировать параллельность выполнения 2 запросов в сеть, запущенных для Coroutine?

Нет, zip сам по себе не гарантирует параллельность.
Если ты просто напишешь zip(firstCall(), secondCall()), оба вызова начнутся последовательно, и zip просто объединит их результаты.
Чтобы получить параллельность:
- Запусти оба вызова через async.
- Затем передай их await() в zip.


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

Библиотека Lifecycle в Android Jetpack помогает управлять и контролировать жизненный цикл компонентов Android, таких как Activities и Fragments. Она упрощает создание компонентов, которые осведомлены о своем жизненном цикле и могут корректно реагировать на изменения в нем. Это позволяет избегать утечек памяти и некорректной работы компонентов при изменении конфигураций или переходах между состояниями.

🚩Основные компоненты библиотеки Lifecycle

🟠LifecycleOwner
Это интерфейс, который определяет класс как владеющий жизненным циклом. Activity и Fragment уже реализуют этот интерфейс.
class MyActivity : AppCompatActivity(), LifecycleOwner {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}


🟠LifecycleObserver
Это интерфейс, который позволяет классу наблюдать за изменениями в жизненном цикле компонента. Методы, аннотированные @OnLifecycleEvent, будут вызываться при соответствующих событиях жизненного цикла.
class MyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
// Код, который выполняется при старте жизненного цикла
Log.d("MyObserver", "onStart called")
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
// Код, который выполняется при остановке жизненного цикла
Log.d("MyObserver", "onStop called")
}
}


🟠Lifecycle
Это класс, который содержит информацию о текущем состоянии и позволяет другим объектам наблюдать за изменениями в жизненном цикле.
class MyActivity : AppCompatActivity() {
private lateinit var myObserver: MyObserver

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

myObserver = MyObserver()
lifecycle.addObserver(myObserver)
}
}


🚩Применение библиотеки Lifecycle

1⃣ Добавьте зависимости в ваш build.gradle
dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
}


2⃣Создайте класс, реализующий LifecycleObserver
class MyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
Log.d("MyObserver", "Activity resumed")
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
Log.d("MyObserver", "Activity paused")
}
}


3⃣Добавьте LifecycleObserver в LifecycleOwner (например, Activity)
class MyActivity : AppCompatActivity() {
private lateinit var myObserver: MyObserver

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

myObserver = MyObserver()
lifecycle.addObserver(myObserver)
}
}


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

- Стек (Stack) — хранит локальные переменные, параметры, вызовы функций. Объём ограничен (обычно от 512 КБ до 2 МБ на поток).
- Куча (Heap) — используется для динамического размещения объектов (new). Размер зависит от платформы и может быть от нескольких МБ до нескольких ГБ.
Стек быстрее, но меньше. Куча медленнее (и управляется GC), но объёмнее.


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

Пропадание пользовательских данных при повороте экрана в Android-приложениях является распространенной проблемой, связанной с тем, как Android управляет жизненным циклом активности. Когда устройство поворачивается, система уничтожает текущую активность и создает её заново, что приводит к потере данных, если они не были сохранены должным образом.

🚩Причины пропадания данных при повороте экрана

🟠Пересоздание активности
🟠Неправильное управление состоянием

🚩Способы предотвращения потери данных

Сохранение состояния с помощью onSaveInstanceState и onRestoreInstanceState
class MainActivity : AppCompatActivity() {

private var userData: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

if (savedInstanceState != null) {
userData = savedInstanceState.getString("USER_DATA_KEY")
// Восстановите данные в пользовательском интерфейсе
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("USER_DATA_KEY", userData)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
userData = savedInstanceState.getString("USER_DATA_KEY")
// Восстановите данные в пользовательском интерфейсе
}
}


Использование Retain Fragment. Этот метод сохраняет данные, используя фрагмент, который сохраняет своё состояние при пересоздании активности.
class RetainFragment : Fragment() {
var userData: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
}

class MainActivity : AppCompatActivity() {

private lateinit var retainFragment: RetainFragment

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val fragmentManager = supportFragmentManager
retainFragment = fragmentManager.findFragmentByTag("RETAIN_FRAGMENT") as RetainFragment?
?: RetainFragment().also {
fragmentManager.beginTransaction().add(it, "RETAIN_FRAGMENT").commit()
}

// Используйте данные из RetainFragment
val userData = retainFragment.userData
}
}


Использование SavedStateHandle в ViewModel
class UserViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
var userData: String?
get() = savedStateHandle.get("USER_DATA_KEY")
set(value) = savedStateHandle.set("USER_DATA_KEY", value)
}

class MainActivity : AppCompatActivity() {

private lateinit var viewModel: UserViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(UserViewModel::class.java)

// Используйте данные из ViewModel
val userData = viewModel.userData
}
}


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

Основной поток приложения — это главный поток (UI-поток), который отвечает за обработку пользовательского интерфейса и взаимодействие с пользователем.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Из каких основных элементов состоит 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)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Работает ли switch() с double/float?

Нет, в Java switch не работает с float и
ли double, так как они подвержены проблемам сравнения с плавающей точкой. switch работает с int, byte, short, char, enum, String, а также с их обёрточными типами.


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

В Android при нажатии пользователя на экран вызывается событие ACTION_DOWN.

🚩Разбор работы событий касания

Android использует систему обработки касаний через MotionEvent. Когда пользователь касается экрана, система генерирует разные типы событий:
ACTION_DOWN – вызывается в момент первого касания.
ACTION_MOVE – вызывается, когда пользователь двигает палец по экрану.
ACTION_UP – вызывается, когда пользователь убирает палец с экрана.
ACTION_CANCEL – вызывается, если система прерывает касание (например, из-за входящего звонка).

🚩Как обрабатывать нажатие

Для обработки событий касания нужно переопределить метод onTouchEvent() в View или использовать setOnTouchListener().
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
Log.d("TouchEvent", "Палец коснулся экрана")
return true
}
MotionEvent.ACTION_UP -> {
Log.d("TouchEvent", "Палец отпущен")
}
}
return super.onTouchEvent(event)
}


Можно также назначить слушатель
view.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
Log.d("TouchEvent", "Нажатие зафиксировано")
true
} else {
false
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Конструкторы нужны для инициализации свойств. В data class основной конструктор обязателен, так как он используется для equals, copy, toString и hashCode. Обойтись совсем без конструктора нельзя — хотя можно использовать значения по умолчанию.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍2🔥2💊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
👍1
🤔 Какие модификаторы доступа могут быть в типах в Kotlin?

- public — доступен везде (по умолчанию).
- internal — доступен в пределах модуля.
- protected — доступен внутри класса и подклассов.
- private — доступен внутри файла или класса.


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