Kotlin | Вопросы собесов
2.56K subscribers
30 photos
971 links
Download 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
👍1
🤔 Что такое итератор?

Итератор — это объект, позволяющий поэлементно перебирать коллекцию (список, массив и т.п.).
Он обычно предоставляет методы hasNext() и next() и позволяет абстрагироваться от конкретной структуры.


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

Являются двумя различными типами коллекций, которые используются для хранения наборов данных. Основные различия между этими двумя структурами данных касаются их внутренней реализации, что влияет на производительность операций добавления, удаления и доступа к элементам.

🚩Внутренняя реализация

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

🟠LinkedList
основан на двусвязном списке элементов, где каждый элемент (узел) содержит данные и ссылки на предыдущий и следующий элементы в списке. Это обеспечивает высокую производительность операций вставки и удаления, поскольку требуется лишь изменить ссылки у соседних элементов, но доступ к элементам по индексу занимает больше времени, так как для этого нужно последовательно пройти от начала или конца списка до нужного элемента.

🚩Производительность:

🟠ArrayList
Быстрый доступ к элементам по индексу. Медленные операции добавления и удаления элементов (особенно в начале и середине списка), так как может потребоваться сдвиг оставшейся части массива.

🟠LinkedList
Быстрые операции вставки и удаления элементов, поскольку они требуют только изменения ссылок. Медленный доступ к элементам по индексу, так как для доступа к элементу необходимо пройти по списку.

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

– Через Navigation Component,
– через интенты и аргументы,
– с помощью Router или Navigator при использовании архитектур (MVP, MVI),
– в многомодульности — через интерфейсы и зависимости через DI.


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

Методы equals() и hashCode() используются для сравнения объектов и их корректной работы в коллекциях (Set, Map).

🚩Контракт `equals()`

Метод equals() должен:
Рефлексивность: a.equals(a)true (объект равен самому себе).
Симметричность: a.equals(b) == b.equals(a).
Транзитивность: если a == b и b == c, то a == c.
Согласованность: если a == b, то a.equals(b) всегда возвращает одно и то же, пока объект не изменится.
Сравнение с null всегда даёт false: a.equals(null) == false.
class User(val name: String, val age: Int) {
override fun equals(other: Any?): Boolean {
if (this === other) return true // Проверка на ссылочное равенство
if (other !is User) return false // Проверка типа

return name == other.name && age == other.age // Сравнение полей
}
}


val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
println(user1 == user2) // true (потому что переопределён equals)


🚩Контракт `hashCode()`

Метод hashCode() должен:
Согласованность с equals(): если a == b, то a.hashCode() == b.hashCode().
Но обратное не обязательно: два разных объекта могут иметь одинаковый hashCode().
Хеш-код не должен меняться, если объект не изменился.
class User(val name: String, val age: Int) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is User) return false
return name == other.name && age == other.age
}

override fun hashCode(): Int {
return name.hashCode() * 31 + age // 31 - стандартный коэффициент
}
}


val userSet = HashSet<User>()
userSet.add(User("Alice", 25))
println(userSet.contains(User("Alice", 25))) // true


🚩Автоматическая генерация в Kotlin

Чтобы не писать equals() и hashCode() вручную, можно использовать data class:
data class User(val name: String, val age: Int)


data class автоматически создаёт equals(), hashCode(), а также copy() и toString().
val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
println(user1 == user2) // true (equals)
println(user1.hashCode() == user2.hashCode()) // true


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

- Статический вложенный класс (static nested) не имеет доступа к членам внешнего класса без явной ссылки.
- Нестатический вложенный класс (inner class) имеет доступ к членам внешнего класса, включая приватные.


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

В Android существуют:
- Normal permissions — автоматически предоставляются (например, доступ к интернету).
- Dangerous permissions — требуют запроса у пользователя (камера, геолокация, контакты).
- Signature permissions — предоставляются только приложениям с той же подписью.
- Special permissions — требуют перехода в системные настройки (например, SYSTEM_ALERT_WINDOW, BATTERY_OPTIMIZATIONS).


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

Модификатор final можно применить:
- к переменной — нельзя изменить значение после инициализации;
- к методу — нельзя переопределить в подклассе;
- к классу — нельзя наследовать от этого класса;
- к параметрам метода — параметр нельзя переназначить внутри метода.


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

В Kotlin все классы неявно наследуются от Any (аналог Object в Java).
Это значит, что любой класс в Kotlin имеет 3 базовых метода:

🚩`equals()` – сравнение объектов

По умолчанию сравнивает ссылки (как ===)
class Person(val name: String)

fun main() {
val p1 = Person("Alice")
val p2 = Person("Alice")

println(p1 == p2) // false (разные объекты)
}


Как переопределить equals() для сравнения по значениям?
class Person(val name: String) {
override fun equals(other: Any?): Boolean {
return other is Person && this.name == other.name
}
}

fun main() {
val p1 = Person("Alice")
val p2 = Person("Alice")

println(p1 == p2) // true (теперь сравниваются значения)
}


🚩`hashCode()` – хеш-код объекта

По умолчанию генерируется на основе ссылки
val p = Person("Alice")
println(p.hashCode()) // Разный для каждого объекта


Как переопределить hashCode()?
class Person(val name: String) {
override fun hashCode(): Int {
return name.hashCode() // Генерируем хеш-код на основе имени
}
}


🚩`toString()` – строковое представление объекта

По умолчанию печатает ClassName@hashCode
val p = Person("Alice")
println(p.toString()) // Person@4e25154f (неудобный вывод)


Как сделать красивый вывод?
class Person(val name: String) {
override fun toString(): String {
return "Person(name=$name)"
}
}

val p = Person("Alice")
println(p.toString()) // Person(name=Alice)


🚩Data-классы автоматически переопределяют `equals()`, `hashCode()`, `toString()`

data class User(val name: String)

fun main() {
val u1 = User("Alice")
val u2 = User("Alice")

println(u1 == u2) // true (по значениям)
println(u1.hashCode()) // Одинаковый для объектов с одинаковыми данными
println(u1.toString()) // User(name=Alice)
}


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

- Dagger 1 — основан на рефлексии, работал во время выполнения (runtime).
- Dagger 2 — использует annotation processing и генерирует код на этапе компиляции, что делает его быстрее, безопаснее и лучше поддерживает compile-time проверки.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊3🔥1
Forwarded from easyoffer
Официальный релиз easyoffer 2.0 состоится уже в течение нескольких дней.

Напоминаю, что в честь релиза запускаем акцию.

Первые 500 покупателей получат:

🚀 Скидку 50% на PRO тариф на 1 год
🎁 Подарок ценностью 5000₽ для тех, кто подписан на этот канал

🔔 Подпишитесь на этот канал: https://t.iss.one/+b2fZN17A9OQ3ZmJi
В нем мы опубликуем сообщение о релизе в первую очередь
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
👍7💊2
🤔 У нас нет устройства, чтобы профилировать приложение. Что будешь делать, чтобы проверить код?

– Использовать эмулятор и встроенные инструменты Android Studio (Logcat, Profiler),
– Добавить ручные замеры времени,
– Использовать Unit и Instrumented тесты,
– Использовать StrictMode и LeakCanary.


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

В Kotlin Flow есть два специальных вида потоков для управления состоянием и передачей данных:
StateFlow — используется для хранения и отслеживания состояния.
SharedFlow — используется для многократной отправки данных нескольким подписчикам.
Теперь разберёмся в деталях.

🚩Что такое SharedFlow?

SharedFlow — это горячий (hot) поток, который можно использовать для передачи данных нескольким подписчикам. Он не хранит состояние и просто раздаёт значения подписчикам в реальном времени.

🚩Как работает SharedFlow?

- Позволяет многим подписчикам получать одни и те же данные.
- Может буферизировать значения (хранить их для новых подписчиков).
- Может повторять последние значения (replay) для новых подписчиков.
- Может накапливать данные и работать как очередь событий.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
val sharedFlow = MutableSharedFlow<Int>(replay = 2) // Будет повторять последние 2 значения для новых подписчиков

launch {
for (i in 1..5) {
sharedFlow.emit(i)
delay(100)
}
}

launch {
delay(150) // Подписываемся чуть позже
sharedFlow.collect { println("Первый подписчик получил: $it") }
}

launch {
delay(300) // Подписываемся ещё позже
sharedFlow.collect { println("Второй подписчик получил: $it") }
}
}


🚩Что такое StateFlow?

StateFlow — это поток, который всегда хранит одно последнее значениЕ. Он идеально подходит для представления состояния (например, UI-состояния в MVVM).
- Всегда содержит одно актуальное значение.
- Если новое значение не отличается от текущего, оно не отправляется подписчикам.
- Новый подписчик сразу получает текущее значение.
- Можно думать о StateFlow как о LiveData, но для корутин.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
val stateFlow = MutableStateFlow(0) // Начальное состояние

launch {
delay(200) // Подписываемся позже
stateFlow.collect { println("Подписчик получил: $it") }
}

delay(100)
stateFlow.value = 1 // Меняем состояние
stateFlow.value = 2 // Меняем состояние
}


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

Bundle передаётся через Intent или setArguments, и доступен при запуске Activity/Fragment или в методе onSaveInstanceState. Он используется как для передачи данных, так и для восстановления состояния.


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

В Android используются разные единицы измерения для работы с экраном, чтобы адаптировать UI под разные устройства.

🚩`px` (пиксели) – физические точки экрана

px (pixel) – это самая маленькая единица изображения на экране.
Не зависит от плотности экрана → на разных устройствах элементы могут выглядеть слишком маленькими или большими.
<TextView
android:layout_width="100px"
android:layout_height="50px"/>


🚩`dp` (density-independent pixels) – независимые от плотности пиксели

dp (density-independent pixel) – это абстрактная единица измерения, которая адаптируется под плотность экрана (dpi).
Рекомендуется для всех размеров UI, чтобы интерфейс выглядел одинаково на разных устройствах.
<TextView
android:layout_width="100dp"
android:layout_height="50dp"/>


Пример в Kotlin
val sizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 100f, resources.displayMetrics
)


🚩`sp` (scale-independent pixels) – масштабируемые пиксели (для текста)

sp (scale-independent pixel) – как dp, но ещё учитывает настройки шрифта пользователя. Используется только для размеров шрифтов.
<TextView
android:text="Привет, мир!"
android:textSize="16sp"/>


Пример в Kotlin
val textSizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16f, resources.displayMetrics
)


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

1. Настроить инфраструктуру для юнит-тестирования (JUnit, Mockito) и интеграционного тестирования (Espresso).
2. Начать с написания тестов для ключевых частей функциональности, например, бизнес-логики.
3. Использовать инструментальные тесты для проверки работы интерфейса.
4. Постепенно увеличивать покрытие тестами, анализируя результаты через CI/CD.


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

В Retrofit можно изменять все запросы глобально с помощью Interceptor (перехватчика) в OkHttp. Это позволяет добавлять или изменять заголовки, параметры запроса, авторизацию, логирование и многое другое.

🟠Изменение всех запросов с помощью `Interceptor`
Перехватчик (Interceptor) позволяет модифицировать запрос перед его отправкой.
class AuthInterceptor(private val tokenProvider: TokenProvider) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer ${tokenProvider.getToken()}")
.addHeader("Accept", "application/json")
.build()
return chain.proceed(request)
}
}


Добавляем перехватчик в OkHttpClient:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(AuthInterceptor(tokenProvider))
.build()

val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()


🟠Добавление общих Query параметров
Иногда нужно добавлять общие GET-параметры (например, API-ключ).
class QueryInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val originalUrl = originalRequest.url

val newUrl = originalUrl.newBuilder()
.addQueryParameter("api_key", "YOUR_API_KEY")
.build()

val newRequest = originalRequest.newBuilder()
.url(newUrl)
.build()

return chain.proceed(newRequest)
}
}


Добавляем в OkHttpClient:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(QueryInterceptor())
.build()


🟠Автоматическая повторная авторизация (Refresh Token)
Если сервер возвращает 401 Unauthorized, можно обновить токен и повторить запрос.
class AuthenticatorInterceptor(private val tokenProvider: TokenProvider) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
var response = chain.proceed(request)

if (response.code == 401) {
// Получаем новый токен
val newToken = tokenProvider.refreshToken()

// Делаем новый запрос с обновлённым токеном
val newRequest = request.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()

response.close() // Закрываем старый ответ
response = chain.proceed(newRequest) // Повторяем запрос
}

return response
}
}


Добавляем в OkHttpClient:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(AuthenticatorInterceptor(tokenProvider))
.build()


🟠Логирование всех запросов
Для отладки удобно логировать все запросы и ответы.
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

val okHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()


🟠Полностью менять `baseUrl` (динамический URL)
Если в приложении нужно менять baseUrl, можно изменять его перед каждым запросом.
class BaseUrlInterceptor(private val urlProvider: UrlProvider) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val newUrl = urlProvider.getBaseUrl() + originalRequest.url.encodedPath

val newRequest = originalRequest.newBuilder()
.url(newUrl)
.build()

return chain.proceed(newRequest)
}
}


Используем в OkHttpClient:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(BaseUrlInterceptor(urlProvider))
.build()


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

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

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