Лабораторно упражнение 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.


Table of contents