Лабораторно упражнение 6
Activity Lifecycle
Activity е основният компонент в Android, който представлява един екран от потребителския интерфейс. Например:
Екран за вход (LoginActivity)
Екран с настройки (SettingsActivity)
Главен екран (MainActivity)
Когато стартираме приложение, системата създава Activity и я преминава през поредица от състояния (lifecycle).
Lifecycle (жизнен цикъл) определя какво се случва с Activity-то от момента, в който се създаде, докато бъде унищожено. Android автоматично извиква методи при всяка промяна на състоянието.
Основни методи на жизнения цикъл
| Метод | Кога се извиква | Описание |
|---|---|---|
onCreate() | Когато Activity-то се създава за първи път | Инициализация на променливи, layout и данни |
onStart() | Когато Activity-то става видимо | Подготвя екрана за показване |
onResume() | Когато Activity-то започва да приема въвеждане от потребителя | Екранът е активен и на фокус |
onPause() | Когато Activity-то губи фокус (напр. отваряне на друг прозорец) | Спира анимации, запазва временни данни |
onStop() | Когато Activity-то вече не е видимо | Спира ресурси, които не са нужни |
onDestroy() | Когато Activity-то се унищожава | Освобождава памет и ресурси |
onRestart() | След onStop(), когато Activity-то се връща на екрана | Подготвя Activity-то за повторно показване |
Връзка между състоянията
onCreate() → onStart() → onResume() ↓ ↑ onPause() ← onRestart() ↓ onStop() ↓ onDestroy()
Jetpack Compose и Lifecycle
В Jetpack Compose UI-то се изгражда декларативно чрез функции. Въпреки това Activity Lifecycle остава същият — ComponentActivity (или MainActivity) управлява кога се създава и унищожава UI-то.
Compose може да „реагира“ на промените в lifecycle чрез remember, LaunchedEffect и други механизми.
Реактивният подход в Jetpack Compose
Реактивен подход (reactive programming) означава, че UI-то реагира автоматично на промени в данните. С други думи — не казваме на системата какво да направи, а как трябва да изглежда интерфейсът, когато данните се променят.
💬 „Данните управляват интерфейса, а не обратното.“
🧱 Пример от стария (императивен) подход: textView.text = “Hello” textView.visibility = View.VISIBLE
Тук ние изрично казваме на UI елемента какво да направи — това е императивен (стъпков) стил. Ако данните се променят, ние трябва ръчно да обновим екрана (например чрез notifyDataSetChanged()).
🌿 Пример от реактивния подход (Compose): var name by remember { mutableStateOf(“Иван”) }
Text(text = “Здравей, $name!”)
Тук Compose автоматично ще прерисува текста, ако name се промени — няма нужда да извикваме invalidate() или notifyDataSetChanged().
UI-то е декларативно описано и винаги представя актуалното състояние на данните.
Метод на действие
Jetpack Compose използва реактивна система за наблюдение на състоянието (state management). Когато дадена стойност (напр. mutableStateOf) се промени:
Compose засича промяната.
Определя кои Composable функции използват тази стойност.
Извиква отново само тези функции.
Прерисува UI-то с новите данни.
Основни понятия
State
Представлява данни, които могат да се променят с времето (например брояч, текст, списък и т.н.).
var count by remember { mutableStateOf(0) }
🔸 remember
Compose забравя всичко при прерисуване, затова remember запазва стойности между извикванията на Composable функции.
var count by remember { mutableStateOf(0) }
→ стойността на count няма да се губи при прерисуване на UI.
🔸 mutableStateOf
Създава наблюдаема стойност. Когато тя се промени, Compose автоматично реагира и обновява екрана.
count++
→ веднага обновява UI-то.
Пример: Реактивен брояч @Composable fun CounterScreen() { var count by remember { mutableStateOf(0) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize()
) {
Text(text = "Брояч: $count", fontSize = 24.sp)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text("Увеличи")
}
} }
Стъпки на изпълнение:
count започва от 0;
натискаш бутона → count++;
Compose автоматично засича промяната и прерисува само Text, който използва count.
Сравнение с императивния подход
| Характеристика | Императивен (стария UI) | Реактивен (Compose) |
|---|---|---|
| Промяна на UI | Ръчно чрез findViewById и setText | Автоматично при промяна на данните |
| Контрол | Програмистът казва какво да се случи | Програмистът описва какво трябва да изглежда |
| Производителност | Често се обновява целият UI | Обновяват се само нужните части |
| Код | По-дълъг, по-труден за поддръжка | Кратък и четим |
Реактивност и Lifecycle
Compose работи в синхрон с Lifecycle — когато Activity или Fragment се спре, Compose автоматично спира наблюдението на state. Така се избягват изтичания на памет и ненужни прерисувания.
| Термин | Значение |
|---|---|
| Реактивност | UI автоматично реагира на промени в данните |
mutableStateOf | Наблюдаема стойност, която задейства обновяване |
remember | Запазва стойност между прерисувания |
Composable | Функция, която описва как трябва да изглежда UI-то при дадено състояние |
mutableStateOf
mutableStateOf е част от Jetpack Compose и представлява реактивна променлива (observable state). Когато стойността ѝ се промени, Compose автоматично обновява UI-то, който я използва.
Пример:
var count by mutableStateOf(0)
Тук count е променлива, която:
може да се променя (mutable);
при промяна автоматично кара екрана да се прерисува.
В нашия пример с Lifecycle:
private var lifecycleState by mutableStateOf(“”)
— когато стойността на lifecycleState се промени (например от onStart на onPause), Compose автоматично обновява текста, показан на екрана.
🔸 Това е част от реактивния подход в Jetpack Compose – UI-то винаги отразява текущото състояние на данните.
Toast
Toast е кратко съобщение, което Android показва на екрана за няколко секунди. То не прекъсва работата на потребителя и не изисква взаимодействие (изчезва автоматично).
Пример:
Toast.makeText(context, “Hello!”, Toast.LENGTH_SHORT).show()
context – текущият контекст (в Activity може да се използва this);
вторият параметър е текстът, който ще се покаже;
Toast.LENGTH_SHORT или Toast.LENGTH_LONG – продължителност на показване;
.show() – показва Toast-а на екрана.
В нашето упражнение Toast се използва, за да показва кой lifecycle метод е бил извикан, например:
Toast.makeText(this, “Състояние: onStart”, Toast.LENGTH_SHORT).show()
Logcat
Logcat (съкратено от Log + Concatenate) е система за събиране и показване на логове (съобщения) от Android операционната система и твоите приложения.
Тя показва в реално време:
системни събития (напр. стартиране на Activity),
съобщения от разработчици (Log.d, Log.e и др.),
грешки, предупреждения и изключения,
съобщения от други процеси (ако разрешиш това).
С други думи — Logcat е „конзолата“ на Android, където виждаш всичко, което се случва зад кулисите.
В Android Studio:
Стартирай приложението си (Run ▶️)
В долната част отвори таба Logcat
Там ще видиш логове в реално време, докато приложението работи.
Основни нива на логове
| Метод | Ниво | Значение | Кога се използва |
|---|---|---|---|
Log.v() | VERBOSE | Най-подробна информация | За детайлно дебъгване |
Log.d() | DEBUG | Диагностична информация | По време на разработка |
Log.i() | INFO | Обща информация | Когато искаш да знаеш, че нещо се е случило успешно |
Log.w() | WARN | Предупреждение | Потенциален проблем, но не грешка |
Log.e() | ERROR | Грешка | Когато нещо се е объркало |
Log.wtf() | ASSERT / WTF | „What a Terrible Failure“ 😅 | Критични грешки, които не би трябвало да се случват |
Пример за всички нива:
val TAG = “LifecycleDemo”
Log.v(TAG, “Verbose: подробна информация”) Log.d(TAG, “Debug: съобщение за дебъгване”) Log.i(TAG, “Info: нормална информация”) Log.w(TAG, “Warning: предупреждение”) Log.e(TAG, “Error: грешка!”) Log.wtf(TAG, “WTF: критична грешка!”)
Филтриране в Logcat
Logcat показва всички съобщения от Android системата и всички приложения. За да не потънеш в информация, можеш да филтрираш логовете по:
App name / package name – напр. com.example.lifecycledemo
Log level – само Debug, Error, и т.н.
Tag – например TAG = “LifecycleDemo”
В Android Studio можеш да избереш филтъра от падащото меню Log Level или да напишеш ръчно tag:LifecycleDemo.
LifecycleObserver
LifecycleObserver е интерфейс от Android Jetpack библиотеката (androidx.lifecycle), който позволява на даден клас да наблюдава Lifecycle събития (напр. onStart, onStop, onResume, onPause) на LifecycleOwner обекти — това са обикновено Activity.