Такой тип называется FrameLayout в Android или Box в Jetpack Compose. Он позволяет размещать элементы поверх друг друга, управляя позицией вручную или через выравнивание.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
Android 5.0 Lollipop стал огромным обновлением по сравнению с Android 4.x, изменив интерфейс, архитектуру и производительность.
Android 4: использовал Holo UI (чёрно-серый стиль с минимализмом).
Android 5: представил Material Design — новый визуальный стиль с тенями, анимациями и цветными кнопками.
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_add"
app:backgroundTint="@color/colorAccent"/>
Android 4: использовал Dalvik (Just-in-Time, JIT) — код компилировался во время работы приложения.
Android 5: перешёл на ART (Ahead-of-Time, AOT) — код компилируется один раз при установке.
Android 4: уведомления были простыми и появлялись только в статус-баре.
Android 5: представил Heads-up Notifications (всплывающие уведомления).
val notification = NotificationCompat.Builder(context, "channel_id")
.setContentTitle("Новое сообщение")
.setContentText("Привет, как дела?")
.setPriority(NotificationCompat.PRIORITY_HIGH) // Heads-up уведомление
.setSmallIcon(R.drawable.ic_message)
.build()
Android 4: только 32-битная архитектура.
Android 5: появилась поддержка 64-битных процессоров, что улучшило производительность и работу с памятью.
Android 4: не имел централизованного контроля за энергопотреблением.
Android 5: представил Project Volta, который продлевает жизнь батареи.
val jobScheduler = getSystemService(JobScheduler::class.java)
val jobInfo = JobInfo.Builder(1, ComponentName(this, MyJobService::class.java))
.setRequiresCharging(true) // Выполнить только при зарядке
.build()
jobScheduler.schedule(jobInfo)
Android 4: старые приложения в "Недавних" выглядели как список окон.
Android 5: приложения теперь отображаются в виде стопки карт (Card Stack UI).
Шифрование устройства включено по умолчанию (Android 5).
SELinux (Security-Enhanced Linux) теперь работает в принудительном (
enforcing) режиме. Добавлен Smart Lock → автоматическая разблокировка устройства по Bluetooth / Wi-Fi / лицу.
Android 5 впервые добавил поддержку Android Wear (умные часы) и Android TV.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4💊3🤔1
2. Включить предзагрузку данных с помощью RecyclerView.OnScrollListener.
3. Реализовать Prefetching через LinearLayoutManager или RecyclerView.LayoutManager.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
В Retrofit, чтобы передать значение в определенное место URL-адреса для GET-запроса, нужно использовать аннотацию
@Path. Эта аннотация позволяет заменить параметр в строке пути на значение, переданное в метод.1. Внутри аннотации
@GET вы указываете строку пути, содержащую плейсхолдеры в фигурных скобках ({}).2. Аннотация
@Path связывает переменную метода с плейсхолдером в URL.3. Когда метод вызывается, значение переменной подставляется вместо плейсхолдера в URL.
Допустим, у вас есть API с эндпоинтом
https://api.example.com/users/{id}/detailsНастраиваем интерфейс Retrofit
interface ApiService {
@GET("users/{id}/details")
suspend fun getUserDetails(
@Path("id") userId: String
): Response<UserDetails>
}Теперь, когда вы вызываете этот метод, вы можете передать значение для
id, и Retrofit автоматически подставит его в URLval apiService = retrofit.create(ApiService::class.java)
suspend fun fetchUserDetails() {
val userId = "123" // Пример значения
val response = apiService.getUserDetails(userId)
if (response.isSuccessful) {
println("User details: ${response.body()}")
} else {
println("Error: ${response.errorBody()?.string()}")
}
}
Вы можете использовать несколько плейсхолдеров в пути и связать их с несколькими параметрами с помощью
@Path. Например:API-эндпоинт
https://api.example.com/users/{userId}/posts/{postId}Интерфейс Retrofit
interface ApiService {
@GET("users/{userId}/posts/{postId}")
suspend fun getPostDetails(
@Path("userId") userId: String,
@Path("postId") postId: String
): Response<PostDetails>
}Вызов метода
val response = apiService.getPostDetails("123", "456")
// URL: https://api.example.com/users/123/posts/456Если вы передаете
null или пустую строку в @Path, это вызовет ошибку, так как Retrofit требует обязательного значения для всех параметров пути. Убедитесь, что передаваемое значение всегда валидно.Если значение пути может содержать символы, требующие экранирования (например, пробелы, специальные символы), Retrofit автоматически обработает это с помощью кодировки URL. Это делает использование
@Path безопасным.@GET("search/{query}")
suspend fun search(
@Path("query") searchQuery: String
): Response<SearchResults>Если вы вызовете метод с поисковым запросом
apiService.search("hello world!")
// Retrofit закодирует URL: https://api.example.com/search/hello%20world%21Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Для этого создаётся новый поток (HandlerThread), запускается, затем получаем Looper и создаём Handler. Этот Handler будет отправлять задачи в новый поток. Такой подход позволяет выполнять фоновую работу без блокировки главного потока.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Dalvik Virtual Machine (DVM) и Android Runtime (ART) — это две среды выполнения для запуска Android-приложений. DVM использовалась в ранних версиях Android, в то время как ART пришла на замену DVM, начиная с Android 5.0 (Lollipop). Основное различие между ними заключается в способе выполнения кода и производительности.
Dalvik Virtual Machine (DVM): Just-In-Time (JIT) компиляция
Dalvik использует JIT-компиляцию (Just-In-Time), что означает, что код приложения компилируется в машинный код во время выполнения (runtime).
Когда приложение запускается, DVM интерпретирует байт-код (.dex-файлы), а при необходимости компилирует часть кода "на лету" для повышения производительности.
Этот подход требует дополнительных ресурсов во время работы приложения, что увеличивает задержки (лаг) при запуске и потребляет больше CPU и батареи.
ART использует AOT-компиляцию (Ahead-Of-Time), которая компилирует весь код приложения в машинный код заранее — во время установки приложения.
Это устраняет необходимость интерпретации и JIT-компиляции во время работы приложения, что снижает нагрузку на процессор и улучшает производительность.
Dalvik (DVM):
Поскольку JIT-компиляция происходит во время работы приложения, это создает дополнительную нагрузку на процессор и замедляет выполнение.
Производительность ниже из-за частой интерпретации кода.
ART:
Благодаря AOT-компиляции приложения запускаются быстрее и работают плавнее.
Потребление ресурсов (CPU, батарея) значительно ниже, поскольку интерпретация и компиляция кода уже выполнены на этапе установки.
Dalvik (DVM):
Приложения запускаются медленнее, так как DVM интерпретирует код во время каждого запуска.
ART:
Приложения запускаются быстрее, так как код уже компилирован в машинный код на этапе установки.
Dalvik (DVM):
Потребляет больше батареи из-за того, что JIT-компиляция выполняется постоянно во время работы приложения.
ART:
Более энергоэффективен, так как большая часть работы выполнена заранее, и процессор не нагружается так сильно.
Dalvik (DVM):
Приложения устанавливаются быстрее, так как код не компилируется заранее.
ART:
Приложения устанавливаются медленнее, так как на этапе установки выполняется AOT-компиляция.
Например, установка приложения в ART может занимать больше времени, чем в DVM, из-за компиляции кода.
Dalvik (DVM):
DVM использует меньше памяти на устройстве, так как код компилируется только во время работы приложения, и машинный код не сохраняется.
ART:
AOT-компиляция увеличивает размер приложения, так как компилированный машинный код сохраняется на устройстве. Это требует больше места в памяти.
Dalvik (DVM):
Ограниченные возможности отладки, так как JIT-компиляция не предоставляет доступа к заранее оптимизированному коду.
ART:
ART позволяет разработчикам использовать более продвинутые инструменты отладки (например, профилирование исполнения) и лучше анализировать производительность приложений.
Dalvik (DVM):
Dalvik был изначально разработан для устройств с ограниченными ресурсами (медленные процессоры, малый объем оперативной памяти).
Приложения работали в основном в условиях ограниченного оборудования.
ART:
ART ориентирован на современные устройства с мощными процессорами и большим объемом памяти.
Он лучше справляется с современными требованиями приложений.
- Установка быстрая.
- При запуске приложения DVM интерпретирует и компилирует код. Это требует времени и ресурсов.
- Приложение может работать медленно из-за интерпретации кода в реальном времени.
- Установка занимает больше времени, так как код компилируется сразу.
- Запуск приложения быстрый, потому что код уже готов к исполнению.
- Приложение работает плавно, так как отсутствует необходимость компиляции во время выполнения.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Горячий поток можно симулировать, используя Subject с несколькими подписчиками. Холодный поток можно создать заново или применить defer, чтобы каждый подписчик получал независимый поток.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Kotlin Flow есть два специальных вида потоков для управления состоянием и передачей данных:
StateFlow — используется для хранения и отслеживания состояния.
SharedFlow — используется для многократной отправки данных нескольким подписчикам.
Теперь разберёмся в деталях.
SharedFlow — это горячий (hot) поток, который можно использовать для передачи данных нескольким подписчикам. Он не хранит состояние и просто раздаёт значения подписчикам в реальном времени.
- Позволяет многим подписчикам получать одни и те же данные.
- Может буферизировать значения (хранить их для новых подписчиков).
- Может повторять последние значения (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 — это поток, который всегда хранит одно последнее значениЕ. Он идеально подходит для представления состояния (например, 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
👍6
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Nothing — это специальный bottom type (нижний тип), который означает: Функция никогда не возвращает результат (
throw, error()). Код после
Nothing недостижим. fun fail(): Nothing {
throw IllegalStateException("Ошибка!") // Никогда не возвращает значение
}Все классы в Kotlin могут иметь инстансы (объекты), кроме
Nothing. Nothing нельзя создать (instantiate), потому что он не имеет конструктора. Любая переменная типа
Nothing просто не существует. val x: Nothing = Nothing() // ❌ Ошибка: у Nothing нет конструктора
Используется в
throwfun fail(): Nothing = throw IllegalArgumentException("Ошибка")Используется в
TODO()fun getData(): String {
TODO("Функция ещё не реализована")
}Используется в
if-else, где один вариант throwfun getValue(x: Int): String {
return if (x > 0) "Позитивное число" else throw IllegalArgumentException("Отрицательное!")
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔5👍2
Минусы одного глобального компонента:
- Переполнение графа зависимостей.
- Потеря контроля над временем жизни объектов.
- Трудности в поддержке и тестировании.
Лучше использовать иерархию компонентов:
- AppComponent для глобальных зависимостей (например, Retrofit, Room).
- ActivityComponent, FragmentComponent для зависимостей с ограниченным временем жизни.
- Использовать Subcomponent или Component dependencies.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Sealed классы являются мощным инструментом для создания ограниченных иерархий классов, что обеспечивает безопасное и предсказуемое использование классов.
Все классы, которые наследуются от sealed класса, должны быть определены в одном файле с самим sealed классом. Это ограничение введено для обеспечения полной контрольной иерархии и предотвращения добавления новых подтипов в другом месте.
sealed class Result {
class Success(val data: String) : Result()
class Error(val exception: Exception) : Result()
} Sealed класс не может быть интерфейсом. Если вам нужно определить ограниченную иерархию интерфейсов, вы должны использовать обычные интерфейсы и sealed классы вместе.
Хотя sealed классы и являются абстрактными по своей природе (их нельзя напрямую инстанцировать), они не могут быть явно помечены как
abstract. Sealed классы и их подтипы не могут быть
private. Они должны быть либо public, либо internal, чтобы их можно было использовать в рамках всего файла иерархии.Sealed класс не может наследоваться от другого класса, кроме
Any. Это связано с тем, что sealed классы уже имеют специфическое предназначение и их иерархия должна быть полностью определена в одном месте.Sealed классы могут быть использованы для классов и объектов, но не могут использоваться для определения интерфейсов.
sealed class Operation {
class Addition(val value: Int) : Operation()
class Subtraction(val value: Int) : Operation()
object NoOp : Operation()
}
fun execute(operation: Operation, base: Int): Int {
return when (operation) {
is Operation.Addition -> base + operation.value
is Operation.Subtraction -> base - operation.value
Operation.NoOp -> base
}
}Это позволяет использовать исчерпывающие выражения
when, которые не требуют блока else, если все подтипы покрыты.Это делает код более предсказуемым и безопасным.
Это делает код более читаемым и менее подверженным ошибкам.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊5👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
Код для работы с SQLite в Android не генерируется автоматически — разработчик сам пишет SQL-запросы и управляет БД.
Но если используется Room (ORM для SQLite), то код генерируется на этапе компиляции с помощью Annotation Processing (
kapt) или KSP. Если используешь
SQLiteOpenHelper, то код не генерируется, ты сам пишешь SQL-запросы: class MyDatabaseHelper(context: Context) : SQLiteOpenHelper(context, "mydb", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS users")
onCreate(db)
}
}Если используешь Room, код автоматически генерируется во время компиляции с помощью Annotation Processing (
kapt) или KSP. Ты пишешь аннотации (
@Entity, @Dao, @Database). Room-аннотации анализируются при компиляции (
kapt / KSP). Генерируется SQL-код и DAO-методы.
@Entity
data class User(
@PrimaryKey val id: Int,
val name: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM User")
fun getAllUsers(): List<User>
@Insert
fun insert(user: User)
}
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Если используешь
kapt (Annotation Processor) или KSP, то код генерируется во время компиляции. dependencies {
implementation("androidx.room:room-runtime:2.6.1")
kapt("androidx.room:room-compiler:2.6.1") // Кодогенерация
}Пример с
KSP (быстрее kapt) dependencies {
implementation("androidx.room:room-runtime:2.6.1")
ksp("androidx.room:room-compiler:2.6.1") // Кодогенерация
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
– Холодные (cold) — начинают генерировать данные только после подписки (например, Flow, Observable).
– Горячие (hot) — генерируют данные независимо от подписчиков (например, SharedFlow, LiveData, Broadcast).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
Сборка Android-приложения проходит несколько этапов:
Компиляция (
.kt/.java → .class → .dex) Объединение ресурсов (
XML, PNG → R.java) Сборка APK/AAB
Подписывание и оптимизация
Установка и запуск на устройстве
Компиляция Kotlin/Java в байткод JVM (
.class) Kotlin-код (
.kt) → Java-байткод (.class) Java-код (
.java) → .class - Используются компиляторы:
-
kotlinc (Kotlin Compiler) -
javac (Java Compiler) // Kotlin-код (MainActivity.kt)
fun main() {
println("Привет, Android!")
}
kotlinc MainActivity.kt -d MainActivity.class
Преобразование
.class в `.dex* Android не использует байткод JVM, а преобразует
.class в Dalvik Executable (.dex). d8 MainActivity.class --output classes.dex
Android Gradle Plugin (AGP) обрабатывает ресурсы
-
res/layout/*.xml → UI файлы -
res/drawable/*.png → Иконки -
AndroidManifest.xml → Метаинформация public final class R {
public static final class layout {
public static final int activity_main = 0x7f0a0000;
}
}APK (старый формат) и AAB (новый формат Google Play)
classes.dex + res/ + AndroidManifest.xml объединяются Проходит обфускация (
ProGuard / R8) APK/AAB упаковывается в ZIP-архив
zipalign -v 4 app.apk signed_app.apk
APK/AAB должен быть подписан перед установкой
apksigner sign --ks my-release-key.jks --out signed_app.apk app-release.apk
Установка и запуск
adb install signed_app.apk
adb shell am start -n com.example.app/.MainActivity
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
В Kotlin:
- == вызывает equals(), то есть логическое сравнение значений.
- === — это сравнение по ссылке, то есть проверка, указывают ли обе переменные на один и тот же объект в памяти.
В Java: == сравнивает ссылки для объектов, а equals() — содержимое.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
В Android используются разные единицы измерения для работы с экраном, чтобы адаптировать UI под разные устройства.
px (pixel) – это самая маленькая единица изображения на экране. Не зависит от плотности экрана → на разных устройствах элементы могут выглядеть слишком маленькими или большими.
<TextView
android:layout_width="100px"
android:layout_height="50px"/>
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 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
👍1
Рекомендуемый способ:
- Создать Bundle, положить туда параметры (putString, putInt и т.д.).
- Установить arguments фрагменту.
- В onCreate() фрагмента извлечь через requireArguments().
Это безопасный способ, поддерживаемый Android-системой (в т.ч. при пересоздании фрагмента).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
Это ситуация, когда приложение выделяет память для каких-либо объектов, но затем не освобождает её, даже когда эти объекты больше не нужны. Это приводит к тому, что используемая память накапливается, что может со временем замедлить работу приложения и привести к его аварийному завершению (крашу) из-за нехватки доступной памяти.
В Android утечки памяти могут возникнуть из-за особенностей работы виртуальной машины (ART или Dalvik), а также из-за того, что сборщик мусора (Garbage Collector, GC) не может освободить память для объектов, на которые по-прежнему существуют ссылки. Это происходит в следующих случаях:
Если объект ссылается на другой объект, который больше не нужен, последний не может быть освобождён GC. Например:
Статические ссылки, которые продолжают "удерживать" объект.
Замыкания (closures), которые хранят ссылки на контекст активности.
Некоторые объекты системы Android, такие как
Context, View, Handler, хранят ссылки на компоненты приложения (например, Activity), из-за чего их нельзя освободить.Ошибки, такие как регистрация слушателей (listeners) без последующей отписки, использование таймеров, которые продолжают работать даже после уничтожения активности, и так далее.
public class MyActivity extends AppCompatActivity {
private static TextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = findViewById(R.id.my_text_view);
myTextView.setText("Hello, Memory Leak!");
}
}Исправленный код
public class MyActivity extends AppCompatActivity {
private TextView myTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = findViewById(R.id.my_text_view);
myTextView.setText("Hello, World!");
}
}Не используйте
static для объектов, ссылающихся на Context или Activity. Используйте WeakReference, если нужно сохранить ссылку, которая не должна блокировать сборщик мусора.Если вы регистрируете слушателей (например, через
setOnClickListener), обязательно удаляйте их в методах жизненного цикла, например, в onDestroy().Анонимные классы (например,
Runnable, Handler) могут неявно хранить ссылки на внешние классы, что может привести к утечке памяти.Android Profiler: встроенный инструмент Android Studio для мониторинга использования памяти. LeakCanary: библиотека, которая автоматически обнаруживает утечки памяти в вашем приложении.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1