Phase 3: Native Development

Android architecture: Activities, Fragments & ViewModels

Intermediate ~3 min read
Think of it this way A friendly analogy. Read this if the technical version feels dense. Show Hide

Imagine you’re building an awesome LEGO creation, like a giant, super detailed castle or a huge spaceship! The whole project is really exciting.

Your Activity is like the big baseplate and the main structure of your castle or spaceship. It's the whole scene you're building on, one complete big adventure. It’s perfect for setting up the entire world, but if you want to build very detailed parts, or parts that you might want to easily move around or even use in other big projects, making everything part of the main castle can get really messy and hard to manage. It's like trying to build a tiny, complex robot inside the main castle structure without it being a separate, removable piece.

This is where Fragments come in. Think of Fragments as smaller, specialized LEGO sub-builds that fit perfectly inside your big castle Activity. For example, you might build a detailed drawbridge as one Fragment, a cozy king's throne room as another Fragment, or a secret treasure chest chamber as a third Fragment. Each Fragment is like its own mini-LEGO creation with its own instructions and purpose, but it always lives inside the bigger castle Activity. The cool thing is, you can easily swap out the drawbridge Fragment for a different kind of entrance, or arrange the throne room Fragment and treasure chamber Fragment side-by-side on a big table (like on a tablet screen), or show them one after the other on a smaller phone screen. This makes your LEGO castle much more flexible and fun to design!

Now, sometimes when you're building, you might accidentally bump the table, or turn your whole castle around to show someone. If you just finished building a super tricky part, like a detailed catapult Fragment, you wouldn't want to lose all the cannonballs you painstakingly loaded or forget where your archers were standing! The ViewModel is like having a special notepad or blueprint just for your catapult Fragment. It remembers all the important details for that specific part – how many cannonballs are loaded, where the archers are positioned, if the catapult is ready to fire. Even if the main castle Activity gets rotated (like turning your phone screen sideways), or briefly put aside, the ViewModel holds onto those specific details for the catapult Fragment. So when you look at the catapult again, everything is exactly where you left it, without you having to re-load the cannonballs every time the screen changes!

This means when you build your own apps, you can create really organized and flexible screens. You can design individual "LEGO pieces" (Fragments) for different parts of your app, like a list of friends, a chat window, or a profile page. Then, you can put these pieces together in different ways depending on if someone is using a small phone or a large tablet. And thanks to the ViewModel, all the important information for each piece stays safe and sound, no matter what happens to the main screen. This helps you build awesome apps that are easy to update and work great on any device!

At the core of an Android application's user interface is the Activity. Think of an Activity as a single, focused screen that users interact with, acting as the primary entry point for a specific part of your app's functionality. While an Activity manages the overall window and orchestrates system interactions like the back button, relying solely on them for complex UIs or multi-pane layouts can lead to monolithic codebases that are hard to maintain and reuse.

To address this, Android introduced Fragments. A Fragment is a modular, reusable piece of an Activity's UI that has its own lifecycle and layout. You can think of them as mini-Activities that live inside an Activity, allowing you to break down a complex screen into smaller, manageable, and swappable components. This is particularly useful for designing adaptable UIs that can seamlessly transition between different screen sizes, like showing a list and its detail side-by-side on a tablet, or as separate screens on a phone.

Finally, the ViewModel is crucial for handling UI-related data in a lifecycle-conscious way. A common problem in Android is that UI data gets lost during configuration changes (e.g., screen rotation, language change) because the Activity or Fragment is destroyed and recreated. ViewModels solve this by surviving these changes, ensuring your data persists. They separate the UI logic (handled by Activities/Fragments) from the data retrieval and management logic, making your code more testable, maintainable, and robust. Your Activities and Fragments will observe data from a ViewModel, updating the UI accordingly without directly managing data persistence themselves.

Key Takeaways

  • An Activity is a single screen, serving as the main entry point for a user interaction.
  • A Fragment is a reusable UI module that lives inside an Activity, ideal for complex or adaptive UIs.
  • A ViewModel stores and manages UI-related data, surviving configuration changes to prevent data loss.
  • Activities/Fragments handle UI, ViewModels handle data – this separation enhances maintainability and testability.
  • Together, these components form a robust architecture for building modern Android applications.

Code Example

kotlin
class MyViewModel : ViewModel() {
    private val _counter = MutableLiveData<Int>()
    val counter: LiveData<Int> = _counter

    init {
        _counter.value = 0
    }

    fun incrementCounter() {
        _counter.value = (_counter.value ?: 0) + 1
    }
}

class MyActivity : AppCompatActivity() {
    private val viewModel: MyViewModel by viewModels() // Lazily initializes ViewModel

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

        // Observe changes from the ViewModel
        viewModel.counter.observe(this) { count ->
            // Update UI (e.g., a TextView)
            findViewById<TextView>(R.id.textViewCounter).text = "Count: $count"
        }

        // Respond to user interaction by updating ViewModel
        findViewById<Button>(R.id.buttonIncrement).setOnClickListener {
            viewModel.incrementCounter()
        }
    }
}

How this code works

This code demonstrates a common pattern for managing UI-related data in Android apps, showing how a ViewModel interacts with an Activity to display and update a simple counter. The MyViewModel class is responsible for holding and managing the counter's state. It uses MutableLiveData<Int> named _counter for the internal, changeable value, and exposes it as an immutable LiveData<Int> called counter. This is a subtle but important detail: LiveData prevents the UI from accidentally modifying the counter directly, ensuring all changes go through the ViewModel's functions like incrementCounter(). The init block sets the initial count, and incrementCounter() safely increases it, handling potential null values with ?: 0.

The MyActivity class represents the user interface. It obtains an instance of MyViewModel using the by viewModels() delegate, which ensures the ViewModel survives configuration changes like screen rotations. Inside onCreate, the viewModel.counter.observe(this) call links the Activity to the ViewModel's data. This means whenever the counter value changes within the ViewModel, the provided lambda function executes, automatically updating the textViewCounter with the new "Count: X" text. User interaction, like tapping buttonIncrement, triggers viewModel.incrementCounter(), delegating the state change back to the ViewModel rather than modifying the UI directly. This separation keeps the Activity focused purely on displaying information.