Lab 6
Activity Lifecycle
Activity is the main Android component and is a particular user interface screen. For example:
Login screen (LoginActivity)
Settings screen (SettingsActivity)
Main screen (MainActivity)
When an application is started, the system creates activity and goes through various states (lifecycle).
The lifecycle defines what is happening with given activity from the moment of its creation till its destruction. Android automatically calls methods on each state change.
Lifecycle’s main methods
| Method | Call | Description |
|---|---|---|
onCreate() | when an activity is created | Variables, layout and data initialization |
onStart() | when an activity becomes visible | Prepares screen to be displayed |
onResume() | when an activity starts accepting user input | Screen is active and on focus |
onPause() | when an activity loses focus (e.g., another screen opened) | Stops animations, stores temp data |
onStop() | when an activity is not visible anymore | Stops redundant resources |
onDestroy() | when an activity is destroyed | Frees memory and resources |
onRestart() | after onStop(), when an activity is back at the screen | Prepares given activity for re-display |
Relation between methods
onCreate() → onStart() → onResume() ↓ ↑ onPause() ← onRestart() ↓ onStop() ↓ onDestroy()
Jetpack Compose and Lifecycle
In Jetpack Compose, the UI is built declaratively using functions. However, the Activity Lifecycle remains the same — the ComponentActivity (or MainActivity) controls when the UI is created and destroyed.
Compose can “react” to changes in the lifecycle using remember, LaunchedEffect, and other mechanisms.
Reactive approach of Jetpack Compose
Reactive programming means that the UI automatically reacts to changes in the data. In other words, there is no need to tell the system what to do, but how the interface should look when there is data changes.
🧱 Example of imperative approach (old): textView.text = “Hello” textView.visibility = View.VISIBLE
Here there is explicit description to the UI element what to do — this is an imperative (stepwise) style. If the data changes, the screen should be manually refreshed (for example, via notifyDataSetChanged()).
🌿Example of reactive approach (Compose): var name by remember { mutableStateOf(“Иван”) }
Text(text = “Hello, $name!”)
Here, Compose will automatically redraw the text if name changes — no need to call invalidate() or notifyDataSetChanged().
The UI is declaratively described and always presents the current state of the data.
Action
Jetpack Compose uses a reactive state management system. When a value (e.g. mutableStateOf) changes:
-
Compose detects the change.
-
Determines which Composable functions use that value.
-
Only calls those functions again.
-
Redraws the UI with the new data.
Main terms
State
Represents data that can change over time (e.g., counter, text, list, etc.).
var count by remember { mutableStateOf(0) }
🔸 remember
Compose forgets everything when redrawing, so remember preserves values between calls to Composable functions.
var count by remember { mutableStateOf(0) }
→ count value won’t be lost if the UI is redrawn.
🔸 mutableStateOf
Creates an observable value. When it changes, Compose automatically responds and refreshes the screen.
count++
→ immediately updates UI.
Example: reactive counter @Composable fun CounterScreen() { var count by remember { mutableStateOf(0) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize()
) {
Text(text = "Counter: $count", fontSize = 24.sp)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text("Increase")
}
} }
Execution steps:
-
count starts from 0;
-
pressing button leads to count increment;
-
Compose automatically detects the change and redraws only Text, which uses changed variable count.
Approach comparison
| Feature | Imperative (old UI) | Reactive (Compose) |
|---|---|---|
| UI change | Manually via findViewById and setText | Automatically on data change |
| Control | Developer decides what to happen | Developer describes how should look like |
| Perfomance | Frequent full UI update | Updates only necessary parts |
| Code | Lot of code, hard to maintain | Short and readble |
Reactivity and Lifecycle
Compose works in sync with Lifecycle — when an Activity or Fragment is stopped, Compose automatically stops monitoring state. This avoids memory leaks and unnecessary redraws.
| Term | Meaning |
|---|---|
| Reactivity | UI automatically reacts to data changes |
mutableStateOf | Observable value that triggers an update |
remember | Preserves value between redraws |
Composable | A function that describes how the UI should look in a given state |
mutableStateOf
mutableStateOf is part of Jetpack Compose and represents a reactive variable (observable state). When its value changes, Compose automatically updates the UI that uses it.
Example:
var count by mutableStateOf(0)
Variable count could:
-
be changed (mutable);
-
when changed triggers screen redraw.
In the above Lifecycle example:
private var lifecycleState by mutableStateOf(“”)
— when the value of lifecycleState changes (for example from onStart to onPause), Compose automatically updates the text displayed on the screen.
🔸 This is part of Jetpack Compose’s reactive approach - the UI always reflects the current state of the data..
Toast
A toast is a short message that Android displays on the screen for a few seconds. It does not interrupt the user and does not require interaction (it disappears automatically)..
Example:
Toast.makeText(context, “Hello!”, Toast.LENGTH_SHORT).show()
-
context – тthe current context (in Activity this can be used);
-
the second parameter is the text to be displayed;
-
Toast.LENGTH_SHORT or Toast.LENGTH_LONG – display duration;
-
.show() – displays the Toast on the screen.
In this lab, Toast is used to indicate which lifecycle method was called, for example:
Toast.makeText(this, “Състояние: onStart”, Toast.LENGTH_SHORT).show()
Logcat
Logcat (short for Log + Concatenate) is a system for collecting and displaying logs (messages) from the Android operating system and developer’s applications.
Shows in real time:
-
system event (e.g., activity start),
-
developers messages (Log.d, Log.e, etc.),
-
errors, warnings and exceptions,
-
other processes messages (if allowed).
In other words, Logcat is Android’s “console” where it can be seen everything that’s happening behind the scenes.
In Android Studio:
-
Start an application (Run ▶️)
-
Open tab Logcat
-
There will be real-time logs as app is running.
Log levels
| Method | Level | Meaning | When to be used |
|---|---|---|---|
Log.v() | VERBOSE | Most detailed info | For detailed debugging |
Log.d() | DEBUG | Diagnostic info | In time of development |
Log.i() | INFO | Common info | To assert successful execution of a function |
Log.w() | WARN | Warning | Potential problem yet not an error |
Log.e() | ERROR | Error | Error |
Log.wtf() | ASSERT / WTF | „What a Terrible Failure“ 😅 | Critical error that should not be happening |
Example for all levels:
val TAG = “LifecycleDemo”
Log.v(TAG, “Verbose: detailed info”) Log.d(TAG, “Debug: debug messages”) Log.i(TAG, “Info: standard info”) Log.w(TAG, “Warning: warning”) Log.e(TAG, “Error: error!”) Log.wtf(TAG, “WTF: fatal error!”)
Filtering in Logcat
Logcat displays all messages from the Android system and all applications. To avoid being overwhelmed by information, logs could be filterd by:
App name / package name – e.g. com.example.lifecycledemo
Log level – only Debug, Error, etc.
Tag – e.g. TAG = “LifecycleDemo”
In Android Studio, filter could be selected from the Log Level drop-down menu or manually to type tag:LifecycleDemo.
LifecycleObserver
LifecycleObserver is an interface from the Android Jetpack library (androidx.lifecycle) that allows a class to observe Lifecycle events (e.g. onStart, onStop, onResume, onPause) of LifecycleOwner objects — these are usually Activities.