Why use a well structured architecture in Android?

yasmmin

Yasmmin Claudino

Posted on September 2, 2024

Why use a well structured architecture in Android?

The Android Operating System (AOSP) is designed to enhance the user experience by managing system resources efficiently. To maintain optimal performance, it may terminate processes as needed. If it "decides" to kill your process, there's little you can do to prevent it. Therefore, maintaining a consistent architecture is crucial to avoid data loss. Understanding the principle of separation of concerns is essential as you embark on your journey with Android architecture.

Separation of Concerns

Separation of Concerns (SoC) is a principle that organizes code into distinct layers, each representing a specific aspect of the application, such as the data layer, UI layer, and business logic layer. This structure enhances code maintainability, readability, and comprehension, facilitating a quicker learning curve for newcomers. SoC also promotes code reusability and simplifies updates by allowing changes to be made within individual layers without impacting others.

In the book Elements of Functional Programming, the concept of separation of concerns is explored through several key topics:

  • Describing what is to be computed;
  • Organizing the computation sequencing into small steps;
  • Managing memory during computation.

UI Layer

The UI layer encompasses all aspects of the user interface and serves as the entry point of the application. It contains everything the user needs to see and interact with. However, the data displayed in the UI layer is typically loaded from another distinct layer known as the Data Layer.

The UI state should not be updated directly within the UI components. Instead, it should utilize a state holder. One of the most well-known state holders on Android is the ViewModel. The ViewModel classes define the logic applied to events in the app and produce updated states as a result.

However, ViewModels, or view holders, have a lifecycle that extends beyond that of an Activity. They should not manage any UI flow directly to avoid memory leaks. Additionally, observables and flow collections should only be activated to observe or collect data when the activity starts.

Layer Communication

As illustrated in the image below, the ViewModel is an integral part of the UI Layer, responsible for responding to UI element interactions initiated by the user. For instance, when a user clicks a button, this triggers an event that is relayed to the ViewModel. The ViewModel then processes the necessary logic and forwards the request to the Data Layer. Upon receiving a response, the ViewModel updates the UI with the new state. The updates in data are either observed or collected—using LiveData for observation or StateFlow for collection.

Image description

Data Layer

The Data Layer is responsible for managing all application data and business logic. This separation centralizes information management away from the UI Layer and facilitates unit testing. It specifies the protocols for data creation, storage, and modification.

The Data Layer consists of repositories—classes responsible for handling specific types of data. Each repository is responsible for:

  • Exclusively exposing data to the application;
  • Modifying the data;
  • Resolving conflicts between data sources;
  • Incorporating business logic.

Now let's see a bit of how this would look in real life:

Here’s an example of how layer communication works. The Activity should not directly manipulate the data. Instead, it should use the ViewModel to access data from the repository. The ViewModel gets an instance of the repository through dependency injection.

class BookListViewModel(
    private val repository: BookRepository
) : ViewModel() {
    private val _books = MutableLiveData<BookResponse>()
    val books: LiveData<BookResponse> = _books
    fun searchBooks(query: String) {
        viewModelScope.launch(Dispatchers.IO) {
             _books.postValue(repository.getBooks(query))
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Understanding and implementing the principle of separation of concerns is a crucial step in mastering Android development. By clearly defining and separating the responsibilities of different layers in your application, you can create code that is easier to maintain, test, and scale.

Feel free to share your thoughts or questions in the comments.
See you in the next topic!

💖 💪 🙅 🚩
yasmmin
Yasmmin Claudino

Posted on September 2, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related