Kotlin | Вопросы собесов
2.57K subscribers
28 photos
960 links
Download Telegram
📌 Как делать списки чатов с точки зрения UI?

💬 Спрашивают в 7% собеседований

Создание списков чатов с точки зрения UI требует учёта множества аспектов, чтобы обеспечить удобство использования, хорошую производительность и красивый внешний вид. В этой задаче важно учесть, как будет отображаться каждая чат-комната, отдельные сообщения и различные состояния (например, новые сообщения, непрочитанные сообщения, онлайн-статус пользователя и т.д.).

🤔 Основные шаги

1️⃣ Определение данных
2️⃣ Создание макетов для элементов списка
3️⃣Создание адаптера для RecyclerView
4️⃣Настройка RecyclerView и управление данными
5️⃣Оптимизация производительности

Пример реализации

1️⃣ Определение данных

Для чата мы можем определить два типа элементов: чат-комнаты и сообщения.

data class ChatRoom(
val id: String,
val name: String,
val lastMessage: String,
val timestamp: Long,
val unreadCount: Int,
val imageUrl: String
)

data class Message(
val id: String,
val chatRoomId: String,
val senderId: String,
val text: String,
val timestamp: Long,
val isRead: Boolean
)


2️⃣ Создание макетов для элементов списка

Макет для элемента чат-комнаты (item_chat_room.xml)


3️⃣ Создание адаптера для RecyclerView

Адаптер для списка чат-комнат


4️⃣ Настройка RecyclerView и управление данными

Настройка RecyclerView в Activity или Fragment


5️⃣ Оптимизация производительности

ViewHolder Pattern: Уже используется, так как RecyclerView автоматически использует паттерн ViewHolder.
DiffUtil: Для оптимального обновления данных в RecyclerView, используйте DiffUtil для вычисления различий между старым и новым списком.
Пагинация: Если у вас много данных, рассмотрите возможность использования Paging Library для подгрузки данных по мере прокрутки.

// Пример использования DiffUtil
class ChatRoomsAdapter : RecyclerView.Adapter<ChatRoomsAdapter.ChatRoomViewHolder>() {

private val chatRooms = mutableListOf<ChatRoom>()

fun setChatRooms(newChatRooms: List<ChatRoom>) {
val diffCallback = ChatRoomDiffCallback(chatRooms, newChatRooms)
val diffResult = DiffUtil.calculateDiff(diffCallback)

chatRooms.clear()
chatRooms.addAll(newChatRooms)
diffResult.dispatchUpdatesTo(this)
}

// Остальной код адаптера...

class ChatRoomDiffCallback(
private val oldList: List<ChatRoom>,
private val newList: List<ChatRoom>
) : DiffUtil.Callback() {

override fun getOldListSize() = oldList.size

override fun getNewListSize() = newList.size

override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
}


🤔 Краткий вывод

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

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41
🤔 Какое значение примет переменная val y = "kotlin".capitalize()?
Anonymous Quiz
5%
kotlin
41%
Kotlin
52%
KOTLIN
1%
kOTLIN
🤔 В каких случаях расчет diff в background потоке работает плохо?

Расчет DiffUtil в фоновом потоке является полезной практикой для повышения производительности и обеспечения плавного пользовательского интерфейса. Однако в некоторых случаях он может работать плохо или даже вызывать проблемы. Вот несколько таких случаев:

🟠Модификация данных во время расчета DiffUtil: Если данные изменяются в процессе вычисления DiffUtil, это может привести к некорректным результатам. Например, если данные в списке обновляются из другого потока, пока выполняется расчет диффа, результаты могут быть несогласованными или некорректными.

🟠Сложные и длительные вычисления в DiffUtil.Callback: Если методы areItemsTheSame или areContentsTheSame выполняют сложные или длительные вычисления, это может негативно сказаться на производительности, даже если расчет выполняется в фоновом потоке. Это может также вызвать блокировки или задержки в главном потоке, если результат используется для обновления UI.

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

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

🟠Изменение данных во время обновления интерфейса: Если данные изменяются в процессе обновления интерфейса на основе результатов DiffUtil, это может привести к несогласованности интерфейса. Например, если пользовательский интерфейс обновляется в главном потоке, а данные изменяются в фоновом потоке, результаты могут быть непредсказуемыми.

Пример проблемы с многопоточностью
// Определение diffCallback
val diffCallback = object : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size

override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
// Сложные вычисления или доступ к данным, которые могут измениться
return oldList[oldItemPosition].id == newList[newItemPosition].id
}

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
// Сложные вычисления или доступ к данным, которые могут измениться
return oldList[oldItemPosition] == newList[newItemPosition]
}
}

// Выполнение diff в фоновом потоке
Thread {
val diffResult = DiffUtil.calculateDiff(diffCallback)

// Обновление UI в главном потоке
runOnUiThread {
adapter.submitList(newList)
diffResult.dispatchUpdatesTo(adapter)
}
}.start()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
🤔 Какой результат выполнения выражения val x = "Hello".drop(3)?
Anonymous Quiz
23%
He
28%
Hel
44%
lo
5%
llo
🤔 Когда лучше использовать png и webp, а когда svg?

Использование различных форматов изображений в Android приложениях зависит от контекста и требований к качеству изображения, его размеру и поддержке различных экранов. Вот рекомендации по выбору между PNG, WebP и SVG:

🚩Когда использовать PNG:

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

Преимущества:
🟠Поддержка прозрачности (альфа-канала).
🟠Без потерь качества (lossless compression).

Недостатки: Большой размер файлов по сравнению с другими форматами, такими как WebP.

🚩WebP:

🟠Фотографии и сложные изображения: WebP хорошо подходит для фотографий и изображений с множеством цветов и градиентов, так как он поддерживает как сжатие без потерь (lossless), так и сжатие с потерями (lossy).
🟠Сжатие и экономия пространства: Если необходимо уменьшить размер файлов без значительной потери качества, WebP может сжать изображения до 34% меньше, чем аналогичные изображения в формате PNG.

Преимущества:
🟠Поддержка сжатия как с потерями, так и без потерь.
🟠Поддержка прозрачности.
🟠Меньший размер файлов по сравнению с PNG и JPEG.

Недостатки: Поддержка WebP появилась в Android 4.0 (API Level 14). В старых версиях Android этот формат не поддерживается.

🚩SVG

🟠Векторные изображения: SVG идеально подходит для векторных изображений, таких как иконки, логотипы и другие графические элементы, которые могут масштабироваться без потери качества.
🟠Изображения, требующие масштабирования: Когда изображение должно корректно отображаться на экранах с различным разрешением и плотностью пикселей (DPI), SVG гарантирует четкость и качество на любом экране.

Преимущества:
🟠Масштабируемость без потери качества.
🟠Малый размер файла для векторных изображений.
🟠Возможность изменения и стилизации с помощью CSS и JavaScript (для веб-приложений).

Недостатки:
🟠Ограниченная поддержка сложных изображений и фотографий.
🟠SVG не всегда поддерживается в старых версиях Android без дополнительных библиотек (например, до Android 5.0, API Level 21).

🚩Практические примеры

Использования PNG:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo.png" />


Использования WebP:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/photo.webp" />


Использования SVG: SVG в Android используется через VectorDrawable. Пример vector drawable (res/drawable/ic_logo.xml):
<vector xmlns:android="https://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,12l-5,5 -5,-5 1.41,-1.41L11,13.17V7h2v6.17l3.59,-3.58L17,12z"/>
</vector>


Использование VectorDrawable в ImageView:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_logo" />


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 В Kotlin, какой ключевой элемент позволяет функции возвращать множество значений, используя один возвращаемый объект?
Anonymous Quiz
12%
Array
23%
Data class
38%
Pair/Triple
27%
List
🤔33🤯7
🤔 Что лучше, png или webp?

Выбор между PNG и WebP зависит от конкретных требований вашего проекта, таких как качество изображения, размер файла, поддержка прозрачности и совместимость с различными версиями Android. Вот сравнительный анализ этих форматов:

🚩PNG

Преимущества:
🟠Без потерь (lossless): PNG сохраняет качество изображения без потерь, что делает его идеальным для изображений с высокой детализацией и текстом.
🟠Прозрачность: PNG поддерживает прозрачность (альфа-канал), что полезно для иконок и графических элементов, которые требуют прозрачного фона.
🟠Широкая поддержка: PNG поддерживается всеми версиями Android и всеми браузерами.

Недостатки: Размер файла: PNG файлы могут быть значительно большими по сравнению с WebP, особенно для фотографий и изображений с множеством цветов и градиентов.

🚩WebP

Преимущества:
🟠Сжатие с потерями и без потерь: WebP поддерживает как сжатие с потерями, так и без потерь. Сжатие с потерями позволяет значительно уменьшить размер файла по сравнению с PNG и JPEG.
🟠Прозрачность: WebP также поддерживает прозрачность, аналогично PNG.
🟠Меньший размер файла: WebP обычно генерирует меньшие файлы по размеру по сравнению с PNG и JPEG, что может значительно снизить объем данных, передаваемых по сети, и время загрузки.

Недостатки:
🟠Совместимость: WebP поддерживается начиная с Android 4.0 (API Level 14). В старых версиях Android этот формат не поддерживается.
🟠Качество: Хотя WebP предлагает хорошее качество изображения при меньшем размере файла, в некоторых случаях сжатие с потерями может привести к видимым артефактам.

🚩Когда использовать PNG

🟠Изображения с высокой детализацией: Когда нужно сохранить все детали без компромиссов.
🟠Прозрачность: Для иконок и логотипов, которые требуют прозрачного фона.
🟠Широкая поддержка: Если нужно поддерживать очень старые версии Android или другие платформы, которые не поддерживают WebP.

🚩Когда использовать WebP

🟠Фотографии и сложные изображения: Когда важен меньший размер файла и допустимо некоторое сжатие с потерями.
🟠Оптимизация производительности: Для ускорения загрузки изображений в приложении за счет меньшего размера файлов.
🟠Прозрачность: Когда требуется прозрачность, но при этом нужен меньший размер файла по сравнению с PNG.

Пример использования PNG:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo.png" />


Использования WebP:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/photo.webp" />


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🤔2
🤔 Какой оператор в Kotlin исполняет блок кода и возвращает его значение?
Anonymous Quiz
41%
apply
15%
also
39%
run
5%
repeat
🤔11👍1
🤔 Как сделать polling?

Polling — это техника, при которой приложение периодически отправляет запросы на сервер, чтобы получить обновленные данные. В Android для реализации polling можно использовать различные подходы, такие как использование Handler и Runnable, ScheduledExecutorService, или RxJava. Выбор зависит от требований приложения и предпочтений разработчика.

🚩Подходы для реализации polling

Использование `Handler` и `Runnable`: Этот подход прост в реализации и хорошо подходит для простых задач.
class MainActivity : AppCompatActivity() {

private val pollingHandler = Handler(Looper.getMainLooper())
private val pollingInterval = 5000L // Интервал в миллисекундах (5 секунд)

private val pollingRunnable = object : Runnable {
override fun run() {
// Ваша логика запроса
fetchDataFromServer()

// Запуск polling через указанный интервал
pollingHandler.postDelayed(this, pollingInterval)
}
}

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

// Запуск polling при старте Activity
startPolling()
}

private fun startPolling() {
pollingHandler.post(pollingRunnable)
}

private fun stopPolling() {
pollingHandler.removeCallbacks(pollingRunnable)
}

private fun fetchDataFromServer() {
// Ваша логика запроса на сервер
// Например, использование Retrofit для выполнения сетевого запроса
// и обновление UI с помощью данных из ответа
}

override fun onDestroy() {
super.onDestroy()
// Остановка polling при уничтожении Activity
stopPolling()
}
}


Использование `ScheduledExecutorService`: Этот подход более гибкий и позволяет выполнять задачи в фоновом потоке.
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

private val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)
private val pollingInterval = 5L // Интервал в секундах

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

// Запуск polling при старте Activity
startPolling()
}

private fun startPolling() {
scheduler.scheduleAtFixedRate({
// Ваша логика запроса
fetchDataFromServer()
}, 0, pollingInterval, TimeUnit.SECONDS)
}

private fun stopPolling() {
scheduler.shutdown()
}

private fun fetchDataFromServer() {
// Ваша логика запроса на сервер
// Например, использование Retrofit для выполнения сетевого запроса
// и обновление UI с помощью данных из ответа
}

override fun onDestroy() {
super.onDestroy()
// Остановка polling при уничтожении Activity
stopPolling()
}
}


Использование RxJava: RxJava предоставляет мощные операторы для управления асинхронными операциями, что делает его отличным выбором для реализации polling.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥21
🤔 Какой ключевой элемент в Kotlin позволяет выполнить блок кода только для ненулевых значений?
Anonymous Quiz
10%
checkNotNull
24%
requireNotNull
55%
let
10%
notNull
🤯10👍1🤔1
🤔 Как сделать кэш?

Создание кэша в Android приложении помогает улучшить производительность и уменьшить количество сетевых запросов, сохраняя данные локально для быстрого доступа. В зависимости от типа данных и потребностей приложения, вы можете использовать различные методы для кэширования, такие как SharedPreferences, SQLite, Room, файлы, и библиотеки кэширования (например, Glide для изображений).

SharedPreferences подходит для сохранения небольших объемов данных, таких как настройки или кэшированные ответы от API.
// Сохранение данных в SharedPreferences
fun saveDataToCache(context: Context, key: String, value: String) {
val sharedPreferences = context.getSharedPreferences("app_cache", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putString(key, value)
editor.apply()
}

// Получение данных из SharedPreferences
fun getDataFromCache(context: Context, key: String): String? {
val sharedPreferences = context.getSharedPreferences("app_cache", Context.MODE_PRIVATE)
return sharedPreferences.getString(key, null)
}


Room — это библиотека для работы с базой данных SQLite, которая упрощает создание и использование базы данных в Android приложениях.

Создание Entity:
@Entity(tableName = "users")
data class User(
@PrimaryKey val id: Int,
val name: String,
val email: String
)


Создание DAO:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUserById(userId: Int): User?

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
}


Создание Database:
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}


Использование базы данных:
class UserRepository(context: Context) {
private val db = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java, "app_database"
).build()

suspend fun getUser(userId: Int): User? {
return db.userDao().getUserById(userId)
}

suspend fun saveUser(user: User) {
db.userDao().insertUser(user)
}
}


Glide — это мощная библиотека для загрузки и кэширования изображений.
// Загрузка и кэширование изображения с использованием Glide
Glide.with(context)
.load("https://example.com/image.jpg")
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView)


Retrofit можно настроить для работы с OkHttp, чтобы кэшировать сетевые запросы.
val cacheSize = 10 * 1024 * 1024 // 10 MB
val cache = Cache(context.cacheDir, cacheSize)

val okHttpClient = OkHttpClient.Builder()
.cache(cache)
.addInterceptor { chain ->
var request = chain.request()
request = if (hasNetwork(context))
request.newBuilder().header("Cache-Control", "public, max-age=" + 5).build()
else
request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build()
chain.proceed(request)
}
.build()


Настройка Retrofit с OkHttpClient:
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20
🤔 В Kotlin, какой оператор используется для безопасного вызова функции на объекте, который может быть `null`?
Anonymous Quiz
13%
`?:`
61%
`?.`
3%
`!!`
22%
`?.let`
🤔 Какие могут быть проблемы с элементами списка?

Проблемы с элементами списка в Android-приложениях могут быть разнообразными. Давайте рассмотрим некоторые из наиболее распространённых проблем и способы их решения.

🚩 Переполнение памяти (Out of Memory)

🟠Почему это происходит: Списки с большим количеством элементов, особенно если элементы содержат изображения или другие ресурсоемкие данные, могут вызывать переполнение памяти.
🟠Решение: Использовать RecyclerView вместо ListView. RecyclerView более эффективно управляет памятью за счет повторного использования представлений. Кроме того, стоит использовать библиотеки для загрузки изображений, такие как Glide или Picasso, которые обеспечивают кэширование и оптимизацию памяти
// Пример использования Glide для загрузки изображений
Glide.with(context)
.load(imageUrl)
.into(imageView)


🚩Медленная прокрутка (Lagging)

🟠Почему это происходит: Прокрутка может быть медленной, если элементы списка отрисовываются или загружаются слишком долго.
🟠Решение: Оптимизировать адаптер списка. Использовать ViewHolder паттерн, чтобы избежать ненужных вызовов findViewById. Также стоит выполнять тяжелые операции, такие как загрузка изображений, асинхронно.
   class MyAdapter(private val itemList: List<Item>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
val imageView: ImageView = itemView.findViewById(R.id.imageView)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return ViewHolder(view)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = itemList[position]
holder.textView.text = item.text
Glide.with(holder.itemView.context).load(item.imageUrl).into(holder.imageView)
}

override fun getItemCount() = itemList.size
}


🚩Неправильное отображение данных (Data Inconsistency)

🟠Почему это происходит: Если адаптер списка неправильно управляет обновлением данных, элементы могут отображать неправильные или устаревшие данные.
🟠Решение: Обеспечить корректное обновление данных в адаптере и использовать DiffUtil для вычисления изменений в данных и обновления только необходимых элементов.
class ItemDiffCallback : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem.id == newItem.id
}

override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem == newItem
}
}

// Использование в адаптере
val diffCallback = ItemDiffCallback()
val diffResult = DiffUtil.calculateDiff(diffCallback)
diffResult.dispatchUpdatesTo(myAdapter)


🚩Многопоточность (Concurrency Issues)

🟠Почему это происходит: Если обновление данных списка происходит из разных потоков без должной синхронизации, это может привести к ошибкам.
🟠Решение: Обеспечить синхронизацию обновлений данных. Использовать подходы, такие как LiveData или Flow из библиотеки Jetpack, чтобы обновления происходили в главном потоке.
val liveData = MutableLiveData<List<Item>>()

liveData.observe(viewLifecycleOwner, Observer { items ->
myAdapter.submitList(items)
})


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
🤔 Вопрос: Какое ключевое слово используется для создания асинхронной функции в Kotlin?
Anonymous Quiz
22%
async
2%
await
76%
suspend
0%
future
🤔 Какие основные причины торможения UI?

Основные причины торможения пользовательского интерфейса (UI) в Android-приложениях включают следующие аспекты:

🟠Тяжелые операции в главном потоке
Почему это происходит: Главный поток отвечает за отрисовку UI и обработку пользовательских взаимодействий. Если в нём выполняются длительные операции, такие как сетевые запросы или доступ к базе данных, это приводит к задержкам и подвисаниям интерфейса.
Решение: Выполнять тяжелые операции в фоновом потоке, используя AsyncTask, Handler, Thread, или современные решения, такие как Kotlin Coroutines или WorkManager.
// Пример использования Kotlin Coroutines для выполнения операции в фоне
CoroutineScope(Dispatchers.IO).launch {
// Выполнение фоновой операции
val result = someHeavyOperation()
withContext(Dispatchers.Main) {
// Обновление UI после завершения фоновой операции
updateUI(result)
}
}


🟠Неоптимизированные макеты (layouts)
Почему это происходит: Сложные макеты с глубокими уровнями вложенности или чрезмерным количеством элементов могут замедлять отрисовку интерфейса.
Решение: Использовать более простые и плоские макеты. Рассмотреть использование ConstraintLayout, который позволяет создавать сложные макеты с минимальной вложенностью.
<!-- Пример использования ConstraintLayout вместо вложенных LinearLayout -->
<ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="Hello World!" />

</ConstraintLayout>


🟠Неоптимизированная работа с изображениями
Почему это происходит: Загрузка и отображение изображений высокого разрешения без оптимизации может вызвать задержки в UI.
Решение: Использовать библиотеки для загрузки изображений, такие как Glide или Picasso, которые автоматически обрабатывают кэширование и масштабирование изображений.
// Пример использования Glide для загрузки изображений
Glide.with(context)
.load(imageUrl)
.into(imageView)


🟠Частое обновление UI
Почему это происходит: Частое обновление элементов интерфейса, особенно внутри циклов или таймеров, может перегружать главный поток.
Решение: Минимизировать количество обновлений UI. Объединять изменения и обновлять UI только при необходимости.
// Пример обновления UI только при изменении данных
fun updateData(newData: List<Item>) {
if (data != newData) {
data = newData
notifyDataSetChanged()
}
}


🟠Неоптимизированные анимации
Почему это происходит: Использование сложных или многочисленных анимаций без оптимизации может замедлить работу UI.
Решение: Использовать ViewPropertyAnimator или TransitionManager для более плавных анимаций. Ограничить количество одновременно выполняемых анимаций.
// Пример использования ViewPropertyAnimator для плавной анимации
imageView.animate()
.translationX(100f)
.setDuration(300)
.start()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
🤔 Вопрос: Как в Kotlin можно объявить переменную, значение которой известно компилятору во время компиляции и не изменяется во время выполнения?
Anonymous Quiz
28%
Используя ключевое слово `val`
2%
Используя ключевое слово `var`
66%
Используя ключевое слово `const`
3%
Используя ключевое слово `static`
🤔 Как можно исправить плохой layout элемента?

🟠Уменьшите вложенность макетов
Глубокая вложенность макетов приводит к большому количеству вычислений при отрисовке. Использование более плоской структуры макетов может значительно улучшить производительность.

🟠Используйте `ViewStub` для редко используемых элементов
ViewStub - это невидимый и легковесный элемент, который можно использовать для элементов, которые отображаются нечасто.
<ViewStub
android:id="@+id/viewStub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/your_layout" />


🟠Используйте `merge` для сокращения уровней вложенности
Если у вас есть включаемые макеты (include), использование merge может помочь уменьшить количество уровней.
Вместо:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<include layout="@layout/your_layout"/>
</LinearLayout>


Используйте: your_layout.xml:
<merge xmlns:android="https://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Label" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Value" />
</merge>


🟠Избегайте ненужных атрибутов и переопределений
Каждый дополнительный атрибут и стиль увеличивает время обработки макета. Убедитесь, что используете только необходимые атрибуты и избегайте дублирования.

🟠Профилируйте макеты с помощью инструментов
Используйте инструменты, такие как Layout Inspector и Profile GPU Rendering, для анализа и оптимизации производительности ваших макетов.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
🤔 Какая функция Kotlin используется для объединения двух коллекций?
Anonymous Quiz
32%
merge
21%
join
16%
concat
30%
zip
🤔 Как определить изменение скорости работы программы после наших действий?

🟠Profile GPU Rendering
Этот инструмент показывает время, затраченное на отрисовку каждого кадра. Использование этого инструмента позволяет выявить "тяжелые" кадры и измерить улучшения после оптимизации.

1⃣Включите режим разработчика на устройстве.
2⃣Перейдите в "Настройки" -> "Для разработчиков".
3⃣Включите опцию "Profile GPU Rendering" и выберите "On screen as bars".
4⃣Запустите ваше приложение и наблюдайте за графиками. Зеленые линии указывают на время отрисовки, и ваша цель - оставаться ниже 16 мс (для 60 кадров в секунду).

🟠Android Profiler
Предоставляет набор инструментов для анализа производительности приложения.

1⃣ Откройте Android Studio и запустите ваше приложение.
2⃣Перейдите в "View" -> "Tool Windows" -> "Profiler".
3⃣Выберите ваше устройство и запущенное приложение.
4⃣Используйте вкладки CPU, Memory, Network и Energy для анализа различных аспектов производительности.
5⃣Сравните данные до и после оптимизации.

🟠Benchmarking
Создание и использование тестов производительности помогает количественно оценить улучшения. Вы можете использовать библиотеку Jetpack Benchmark для создания и выполнения тестов производительности.

🚩Пример использования Jetpack Benchmark:

1⃣Добавьте зависимости в build.gradle:
dependencies {
androidTestImplementation "androidx.benchmark:benchmark-junit4:1.1.0"
androidTestImplementation "androidx.test:runner:1.3.0"
androidTestImplementation "androidx.test:rules:1.3.0"
}


2⃣Создайте класс теста:
import androidx.benchmark.junit4.BenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ExampleBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()

@Test
fun myFunctionBenchmark() {
benchmarkRule.measureRepeated {
// Вызов вашей функции или кода для тестирования производительности
myFunction()
}
}
}


🟠Logcat
Используйте журналирование для измерения времени выполнения определенных операций.
val startTime = System.currentTimeMillis()
// Ваш код
val endTime = System.currentTimeMillis()
Log.d("Performance", "Время выполнения: ${endTime - startTime} мс")


🟠StrictMode
StrictMode помогает обнаружить операции, которые могут замедлить работу приложения, такие как работа с сетью или базой данных в главном потоке.
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build()
)
}


🟠Systrace
Systrace позволяет собирать и анализировать трассировки производительности системы, предоставляя детализированные данные о времени выполнения различных операций.

1⃣Включите режим разработчика на устройстве.
2⃣В "Настройки" -> "Для разработчиков" включите "Enable GPU Debug Layers".
3⃣Запустите команду adb shell am broadcast -a com.android.systemui.screenshot.ScreenshotService.ACTION_SYSTRACE.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
🤔 Вопрос: Как в Kotlin привести переменную типа `Any` к типу `String` безопасно, чтобы избежать исключения?
Anonymous Quiz
12%
as String
2%
(String) variable
56%
variable as? String
30%
variable.toString()
🤔 Какое API или другие инструменты будешь использовать для отправления файлов на сервер?

Для отправки файлов на сервер в Android-приложении можно использовать несколько API и библиотек, в зависимости от ваших требований. Наиболее популярные и удобные решения включают использование HttpURLConnection, OkHttp, и Retrofit. Ниже я подробно расскажу о каждом из них и приведу примеры.

🟠Использование HttpURLConnection
HttpURLConnection — это встроенный инструмент в Android для выполнения HTTP-запросов, включая загрузку файлов.
fun uploadFileToServer(url: String, file: File) {
val boundary = "===" + System.currentTimeMillis() + "==="
val LINE_FEED = "\r\n"

val connection = URL(url).openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.doOutput = true
connection.doInput = true
connection.useCaches = false
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=$boundary")

val outputStream = connection.outputStream
val writer = PrintWriter(OutputStreamWriter(outputStream, "UTF-8"), true)

// Добавление файла
writer.append("--$boundary").append(LINE_FEED)
writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"${file.name}\"").append(LINE_FEED)
writer.append("Content-Type: ${URLConnection.guessContentTypeFromName(file.name)}").append(LINE_FEED)
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED)
writer.append(LINE_FEED).flush()

val inputStream = FileInputStream(file)
inputStream.copyTo(outputStream, 4096)
outputStream.flush()
inputStream.close()

writer.append(LINE_FEED).flush()
writer.append("--$boundary--").append(LINE_FEED)
writer.close()

val responseCode = connection.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
// Успешная загрузка
} else {
// Ошибка загрузки
}
}


🟠Использование OkHttp
OkHttp — это популярная и мощная библиотека для выполнения HTTP-запросов, которая значительно упрощает процесс отправки файлов на сервер.
fun uploadFileWithRetrofit(file: File) {
val requestBody = file.asRequestBody("application/octet-stream".toMediaTypeOrNull())
val multipartBody = MultipartBody.Part.createFormData("file", file.name, requestBody)

val call = apiService.uploadFile(multipartBody)
call.enqueue(object : Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
// Ошибка
}

override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful) {
// Успешная загрузка
} else {
// Ошибка загрузки
}
}
})
}


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