Kotlin | Вопросы собесов
2.56K subscribers
30 photos
972 links
Download Telegram
🤔 В чём различия наследования, композиции, агрегации?

Наследование предполагает создание нового класса на основе существующего. Композиция включает один класс в качестве части другого для использования его функциональности. Агрегация — слабая форма композиции, где включённый объект может существовать независимо от владельца.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
🤔 Вставка значения быстрее в 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) // Медленная вставка в середину


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Что такое BroadcastReceiver?

`BroadcastReceiver` — это компонент Android, который позволяет приложениям принимать и обрабатывать широковещательные сообщения (broadcasts) от системы или других приложений. Примеры системных сообщений включают изменения состояния сети, получение SMS или завершение загрузки устройства. Приложения могут регистрировать BroadcastReceiver статически в манифесте или динамически в коде. BroadcastReceiver помогает реагировать на важные события, происходящие в системе.

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

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

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

В Java класс Object является корневым классом для всех остальных классов. Это значит, что все классы в Java неявно наследуются от Object, если явно не указано другое.

🚩Основные методы класса `Object`

🟠`equals(Object obj)`
Определяет, равны ли два объекта. По умолчанию использует сравнение по ссылке (==), но может быть переопределён для логического сравнения.
     class Person {
String name;

Person(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}
}

public class Main {
public static void main(String[] args) {
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // true
}
}


🟠`hashCode()`
Возвращает числовой хеш-код объекта, используемый, например, в HashMap. Если переопределяем equals(), нужно переопределить и hashCode().
     @Override
public int hashCode() {
return Objects.hash(name);
}


🟠`toString()`
Возвращает строковое представление объекта. По умолчанию – имя класса + хеш-код, но лучше переопределять.
     @Override
public String toString() {
return "Person{name='" + name + "'}";
}


🟠`getClass()`
Возвращает объект Class, описывающий класс объекта.
     System.out.println(p1.getClass().getName()); // Person


🟠`clone()`
Создаёт копию объекта (поверхностное клонирование). Объект должен реализовать Cloneable, иначе будет CloneNotSupportedException.
     class Person implements Cloneable {
String name;

Person(String name) {
this.name = name;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}


🟠`finalize()` *(устарел, не рекомендуется использовать)*
Вызывается перед удалением объекта сборщиком мусора. Лучше использовать try-with-resources и close().
     @Override
protected void finalize() throws Throwable {
System.out.println("Object is being garbage collected");
}


🟠`wait()`, `notify()`, `notifyAll()`
Методы для работы с многопоточностью. wait() – приостанавливает поток до вызова notify(). notify() – пробуждает один поток. notifyAll() – пробуждает все потоки.
     class SharedResource {
synchronized void doWait() throws InterruptedException {
wait();
}

synchronized void doNotify() {
notify();
}
}


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

1. Класс Object:
- В equals по умолчанию сравниваются ссылки на объекты (ссылочное равенство).
2. Переопределение:
- Для пользовательских классов метод equals переопределяют, чтобы сравнивать содержимое объектов.


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

Пропадание пользовательских данных при повороте экрана в 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💊1
🤔 Какие ещё виды map знаешь в RxJava?

Кроме map и flatMap, в RxJava есть switchMap — отменяет предыдущий поток при каждом новом вызове, concatMap — сохраняет порядок выполнения, и exhaustMap — игнорирует новые вызовы до завершения текущего.


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

Запрос данных из двух таблиц обычно выполняется с помощью операции объединения (JOIN) в SQL. Алгоритм и его сложность зависят от типа объединения, структуры данных и используемой базы данных.

🚩Пример запроса

Предположим, у нас есть две таблицы: employees и departments. Мы хотим получить список сотрудников вместе с их отделами.
SELECT employees.name, departments.name
FROM employees
INNER JOIN departments ON employees.department_id = departments.id;


🚩Алгоритмы выполнения JOIN

1⃣Nested Loop Join (Вложенные циклы)
Для каждой строки из первой таблицы выполняется поиск соответствующих строк во второй таблице.
for each row in employees:
for each row in departments:
if row.employees.department_id == row.departments.id:
yield (row.employees.name, row.departments.name)


2⃣Hash Join (Хеш-объединение)
Создается хеш-таблица для одной из таблиц на основе ключа соединения. Для каждой строки из другой таблицы проверяется наличие соответствующего ключа в хеш-таблице.
hash_table = {}
for each row in departments:
hash_table[row.id] = row.name
for each row in employees:
if row.department_id in hash_table:
yield (row.name, hash_table[row.department_id])


3⃣Sort-Merge Join (Сортировка и слияние)
Обе таблицы сортируются по ключу соединения, затем выполняется слияние отсортированных списков.
sorted_employees = sort(employees, key=lambda x: x.department_id)
sorted_departments = sort(departments, key=lambda x: x.id)
i, j = 0, 0
while i < len(sorted_employees) and j < len(sorted_departments):
if sorted_employees[i].department_id == sorted_departments[j].id:
yield (sorted_employees[i].name, sorted_departments[j].name)
i += 1
j += 1
elif sorted_employees[i].department_id < sorted_departments[j].id:
i += 1
else:
j += 1


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 В чем преимущество Kotlin для разработки под android?

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

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

TransactionTooLargeException — это исключение, которое возникает, если передаваемые данные превышают лимит 1MB в Binder.
Чаще всего ошибка появляется при передаче больших объектов через Intent, Bundle, `onSaveInstanceState().

🚩Как воспроизвести `TransactionTooLargeException`?

Передача большого Bitmap через Intent (ОШИБКА!)
val bitmap: Bitmap = getLargeBitmap() //  Очень большой объект

val intent = Intent(this, ImageActivity::class.java)
intent.putExtra("image", bitmap) // Ошибка: TransactionTooLargeException

startActivity(intent)


Ошибка
android.os.TransactionTooLargeException: data parcel size 2MB is larger than 1MB


🚩Как избежать `TransactionTooLargeException`?

Использовать FileProvider и Uri вместо Bitmap
val file = File(context.cacheDir, "image.png")
file.outputStream().use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
}

val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
val intent = Intent(this, ImageActivity::class.java).apply {
putExtra("image_uri", uri.toString())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(intent)


🚩Использовать `ViewModel` вместо `onSaveInstanceState()`

Ошибка: сохранение больших данных в onSaveInstanceState()
override fun onSaveInstanceState(outState: Bundle) {
outState.putSerializable("largeData", myLargeObject) // ОШИБКА!
super.onSaveInstanceState(outState)
}


Решение → Используем ViewModel, чтобы хранить данные в памяти:
class MyViewModel : ViewModel() {
var largeData: MyLargeObject? = null
}


🚩Хранить данные в `SharedPreferences` или `Room`

Если данные не нужны постоянно → используем SharedPreferences
getSharedPreferences("app_prefs", MODE_PRIVATE).edit()
.putString("last_data", jsonData)
.apply()


Если данные большие и важные → используем Room
@Dao
interface MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveData(data: MyData)
}


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

Можно описать набор состояний, каждое из которых — отдельный подкласс sealed-класса. Это удобно для моделирования состояний UI, например: Loading, Success, Error. Все возможные состояния известны компилятору, что делает обработку безопасной.


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

Jetpack Navigation Component — современный способ навигации, основанный на графе навигации и безопасных аргументах. Позволяет легко переходить между фрагментами и активностями, а также управлять состоянием навигации.
FragmentTransaction — ручной способ добавления, замены и удаления фрагментов.
Navigation через Intent — используется для переключения между активностями или фрагментами внутри приложения или между приложениями.
Explicit и Implicit Intents — явные и неявные намерения для навигации между компонентами.
NavHostFragment и NavController — связаны с использованием графа навигации для управления фрагментами в приложениях Android.


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

Да, потеря состояния (state) может быть связана с фрагментами (Fragments) в Android. Это довольно распространённая проблема, особенно при работе с динамическими интерфейсами. Давайте разберёмся, почему она возникает, как её предотвратить и какие решения существуют.

🚩Почему происходит потеря состояния во фрагментах?

🟠Жизненный цикл фрагмента
Фрагменты имеют сложный жизненный цикл, который тесно связан с активностью. Основные этапы:
onCreate() — создаётся фрагмент, инициализируются объекты.
onViewCreated() — создаётся View (UI компоненты).
onStart() / onResume() — фрагмент становится видимым и активным.
onPause() / onStop() — фрагмент приостанавливается.
onDestroyView() — уничтожается только View (UI), но сам фрагмент всё ещё существует.
onDestroy() — полностью уничтожается фрагмент.
Фрагменты могут пересоздаваться системой, например, при смене ориентации экрана или нехватке памяти. Если разработчик неправильно сохраняет состояние фрагмента, оно может быть утеряно.

🟠Удаление View фрагмента системой
Когда фрагмент переходит в состояние onDestroyView(), его View уничтожается, но сам объект фрагмента сохраняется. Если пользователь вернётся к этому фрагменту, View будет пересоздана, и вы потеряете все изменения, сделанные ранее, если они не сохранены явно.

🟠Проблемы с менеджером фрагментов
Использование FragmentManager или FragmentTransaction с неправильными методами, такими как replace() или add(), без должного управления стэком (back stack), может привести к пересозданию или дублированию фрагментов, что вызывает потерю состояния.

🟠Проблемы с `savedInstanceState`
Фрагменты, как и активности, используют механизм сохранения состояния через BundleonSaveInstanceState). Если состояние не сохранено правильно, данные могут быть потеряны.

🚩Как предотвратить потерю состояния во фрагментах?

🟠Сохранение данных в `onSaveInstanceState`
При уничтожении фрагмента система вызывает метод onSaveInstanceState(). Здесь вы можете сохранить важные данные в Bundle.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("key_text", editText.text.toString())
}


При пересоздании фрагмента данные можно восстановить в onViewCreated()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val restoredText = savedInstanceState?.getString("key_text")
editText.setText(restoredText)
}


🟠Использование `ViewModel`
Для хранения состояния, которое переживает уничтожение и пересоздание фрагмента, лучше использовать архитектурный компонент ViewModel.
1⃣Создайте ViewModel
class MyViewModel : ViewModel() {
val textData = MutableLiveData<String>()
}


2⃣Используйте его во фрагменте
class MyFragment : Fragment() {
private lateinit var viewModel: MyViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

// Восстановление данных
viewModel.textData.observe(viewLifecycleOwner) { text ->
editText.setText(text)
}

// Сохранение данных при изменении
editText.addTextChangedListener {
viewModel.textData.value = it.toString()
}
}
}


🟠Использование `FragmentManager` правильно
При работе с фрагментами всегда добавляйте их в back stack, если вы хотите сохранить состояние.
val fragment = MyFragment()
parentFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit()


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

Если используется делегирование интерфейса, то необходимо переопределить функции этого интерфейса. Это может быть getValue, setValue или любые кастомные функции, если используется пользовательский делегат.


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

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


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

В Kotlin есть три основных типа коллекций:
List — упорядоченный список элементов.
Set — множество уникальных элементов.
Map — коллекция пар "ключ-значение".

🚩List — упорядоченный список

List — это коллекция, в которой можно хранить дубликаты, а элементы доступны по индексу.
val numbers = listOf(1, 2, 3, 4, 5) // Immutable (нельзя изменять)
val mutableNumbers = mutableListOf(1, 2, 3) // Можно изменять

mutableNumbers.add(4) // Добавляем элемент
mutableNumbers.removeAt(1) // Удаляем элемент по индексу

println(mutableNumbers) // [1, 3, 4]


🚩Set — множество уникальных элементов

Set — это коллекция, в которой нет дубликатов.
val numbers = setOf(1, 2, 3, 3, 4, 5) // Дубликаты удаляются автоматически
println(numbers) // [1, 2, 3, 4, 5]

val mutableNumbers = mutableSetOf(1, 2, 3)
mutableNumbers.add(3) // Не добавится, потому что уже есть
mutableNumbers.add(4) // Добавится

println(mutableNumbers) // [1, 2, 3, 4]


🚩Map — коллекция пар "ключ-значение"

Map — это структура данных, в которой каждому ключу соответствует одно значение.
val userMap = mapOf(
1 to "Alice",
2 to "Bob",
3 to "Charlie"
) // Immutable

val mutableUserMap = mutableMapOf(1 to "Alice", 2 to "Bob")
mutableUserMap[3] = "Charlie" // Добавляем новый ключ-значение
mutableUserMap.remove(1) // Удаляем элемент по ключу

println(mutableUserMap) // {2=Bob, 3=Charlie}


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