Skip to content

The Foundations of MVVM, Inheritance, and Interfaces – Day 8 Android 14 Masterclass

Exploring the Foundations of MVVM, Inheritance, and Interfaces - Day 8 Android 14 Masterclass
Become a developer with our complete learning paths
Become a developer with our complete learning paths

The Foundations of MVVM, Inheritance, and Interfaces – Day 8 Android 14 Masterclass

Welcome to Day 8 of the Android 14 Masterclass, where we delve deep into the intricacies of MVVM (Model-View-ViewModel) architecture, inheritance, interfaces, and the role of repositories and APIs in Android development. Today, we focus on the foundational elements that make MVVM a robust and efficient pattern for organizing code. We will also dissect the concepts of inheritance and interfaces, which are pillars of object-oriented programming and instrumental in creating a scalable, modular application.

 

1. Understanding MVVM

MVVM stands for Model-View-ViewModel. It is an architectural pattern used in software development, which helps in organizing your code in a way that is easier to understand, test, and maintain.

Let’s break down what each component in MVVM does:

1. Model

  • What it is: The Model represents the data and business logic of the application. It is responsible for retrieving and storing data, as well as performing any necessary data processing.
  • Example: If you have an app that displays a list of books, the Model would be responsible for fetching the book data from a database or an online source.

2. View

  • What it is: The View is the user interface (UI) of the application. It displays the data to the user and interacts with the user.
  • Example: In the same book app, the View would be the actual screen that displays the list of books to the user.

3. ViewModel

  • What it is: The ViewModel acts as a bridge between the Model and the View. It takes data from the Model, applies UI logic, and then formats it for display in the View.
  • Example: For the book app, the ViewModel would take the raw book data, and format it nicely for display in the View.

Syntax and Code Examples: MVVM

Here’s a simplified example in Kotlin to give you a basic idea of how MVVM works:

  1. Model
    data class Book(val title: String, val author: String)
    
  2. ViewModel
    class BookViewModel {
        fun getBooks(): List<Book> {
            //Here you would normally fetch data from a database or online source
            return listOf(Book("Title1", "Author1"), Book("Title2", "Author2"))
        }
    }
    
  3. View
    • Your View would be an XML layout file where you design your UI.
    • You would also have a Kotlin file where you would interact with the ViewModel to get the data and display it.
    class BookActivity : AppCompatActivity() {
    
        private lateinit var viewModel: BookViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_book)
    
            viewModel = BookViewModel()
    
            val books = viewModel.getBooks()
            // Now you can display the books in your UI
        }
    }
    

    In this example, the Book class is the Model, BookViewModel is the ViewModel, and BookActivity is the View. The ViewModel fetches the book data, and the View displays it.

    Remember, this is a simplified example. In a real-world application, you would have more complex interactions, and you might fetch data from a database or an online API. You would also implement more advanced features like data binding and LiveData to make the app more responsive and user-friendly.

 

2. MVVM Architecture: What is a ViewModel Class?

A ViewModel Class in Android is a part of the MVVM architecture, as previously mentioned. It acts as a manager that handles the communication between the app’s data and the UI.

The ViewModel is responsible for holding and processing all the data needed for the UI while respecting the lifecycle of the app’s activities or fragments.

Why is ViewModel Important?

  1. Lifecycle Awareness: ViewModel is designed to store and manage UI-related data in a lifecycle-conscious way. It allows data to survive configuration changes such as screen rotations.
  2. Separation of Concerns: ViewModel helps to keep the UI code simple and focused on presenting data, as it takes care of the data handling.

How Does ViewModel Work?

  • Initialization: A ViewModel is usually initialized in an activity or fragment. It survives configuration changes and is destroyed when the activity or fragment is permanently removed.
  • Data Handling: ViewModel retrieves data from the Model, holds it, and the View observes this data to update the UI accordingly.

Basic Syntax and Code Example

Let’s look at a simple example to understand the ViewModel Class better:

  1. Creating a ViewModel Class
import androidx.lifecycle.ViewModel

class MyViewModel : ViewModel() {
    var number: Int = 0

    fun incrementNumber() {
        number++
    }
}

In this example, MyViewModel is a class that extends ViewModel(). It has a variable number and a function incrementNumber() to increase the number by one.

  1. Using ViewModel in an Activity
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    private val myViewModel: MyViewModel by viewModels()

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

        // Use the ViewModel
        myViewModel.incrementNumber()
    }
}

In the MainActivity, the ViewModel is initialized and used. The by viewModels() delegate is used to associate the ViewModel with the activity.

Key Points to Remember

  • ViewModel is not a replacement for onSaveInstanceState; it doesn’t handle all types of configuration changes.
  • ViewModel should never contain references to Views, Activities, Fragments, or any Context, as this can cause memory leaks.

 

3. Introduction to Inheritance

Inheritance is a fundamental concept in object-oriented programming (OOP), and it is widely used in Android development.

Inheritance allows a class to use methods and fields of another class, promoting reusability and a hierarchical organization of code.

Understanding Basic Concepts

  • Base Class (Parent Class): The class whose properties and methods are inherited by another class. It is also known as the superclass or parent class.
  • Derived Class (Child Class): The class that inherits the properties and methods from another class. It is also known as the subclass or child class.
  • Open Keyword: In Kotlin, classes are final by default, which means they can’t be inherited. To allow a class to be inherited, it must be declared with the open keyword.

Explaining Inheritance with an Example

Imagine you are creating an application that has several types of vehicles, like cars and bicycles. You can create a general class named Vehicle and then create more specific classes like Car and Bicycle that inherit from Vehicle.

  1. Base Class (Parent Class)
open class Vehicle {
    fun start() {
        println("The vehicle is starting.")
    }
}

Here, Vehicle is the base class. It has a method start() that prints a message. The open keyword allows this class to be inherited.

  1. Derived Class (Child Class)
class Car : Vehicle() {
    fun drive() {
        println("The car is driving.")
    }
}

Car is a derived class that inherits from Vehicle. It has an additional method drive().

  1. Using the Classes
val myCar = Car()
myCar.start()  // Output: The vehicle is starting.
myCar.drive()  // Output: The car is driving.

When you create an object of the Car class, you can access both the methods from the Car class and the inherited methods from the Vehicle class.

 

Key Points to Remember

  • Inheritance helps in reusing and organizing code efficiently.
  • The open keyword is essential to allow inheritance in Kotlin.
  • The derived class has access to the public and protected members (methods and fields) of the base class.

Inheritance in Android development, and programming in general, allows for a structured and reusable way of writing code. It enables derived classes to inherit features from base classes, making the code more modular and easier to manage and understand, especially as applications become more complex.

 

4. Open Function in Kotlin

In Kotlin, the open keyword is not limited to classes; it is also used with functions. By default, functions in Kotlin are final, meaning they can’t be overridden in a derived class.

When a function is declared with the open keyword, it allows that function to be overridden by derived classes, enabling them to provide a specific implementation.

Why Use Open Functions?

Using open functions is essential when you want to provide a default behavior that can be customized by derived classes. It promotes flexibility and allows derived classes to modify or extend the functionality of a function from the base class.

Explaining Open Function with an Example

Let’s continue with the vehicle example to understand open functions better.

  1. Base Class with Open Function
open class Vehicle {
    open fun start() {
        println("The vehicle is starting.")
    }
}

Here, the start() function is marked as open, allowing it to be overridden by derived classes.

  1. Derived Class Overriding the Open Function
class Car : Vehicle() {
    override fun start() {
        println("The car is starting with a roar!")
    }
}

In the Car class, the start() function is overridden, providing a new implementation that is specific to the Car class.

  1. Using the Classes
val myVehicle = Vehicle()
myVehicle.start()  // Output: The vehicle is starting.

val myCar = Car()
myCar.start()  // Output: The car is starting with a roar!

When you call the start() function on objects of the Vehicle and Car classes, the output will be based on their respective implementations of the function.

 

Key Points to Remember

  • An open function allows derived classes to provide a specific implementation by overriding it.
  • Overriding is declaring a function in the derived class with the same name and parameters as a function in the base class.

Open functions in Kotlin are a powerful feature that allows for more flexible and reusable code. They enable derived classes to customize or extend the functionality of functions declared in the base class, enhancing the adaptability of your code in various scenarios and use cases.

 

5. Override Function in Kotlin

n Kotlin, when a function in a derived class has the same name as a function in its base class, and the function in the base class is marked as open, the derived class has the option to provide a new implementation for this function. This is known as overriding the function.

Using the override Keyword

The override keyword is used in the derived class to modify the function and provide a new implementation.

Understanding the super Keyword

The super keyword is used inside the overridden function to call the function of the base class. It allows the derived class to use the original implementation of the function.

Explaining with Examples

  1. Override Function Without Using super
open class Vehicle {
    open fun start() {
        println("The vehicle is starting.")
    }
}

class Car : Vehicle() {
    override fun start() {
        println("The car is starting with a roar!")
    }
}

Here, the Car class overrides the start() function without calling the base class function, providing a completely new implementation.

  1. Override Function Using super
class SportCar : Vehicle() {
    override fun start() {
        super.start()
        println("The sports car is ready to zoom!")
    }
}

In this example, the SportCar class overrides the start() function and uses the super keyword to call the base class function, adding additional behavior.

  1. Using the Classes
val car = Car()
car.start()  // Output: The car is starting with a roar!

val sportCar = SportCar()
sportCar.start()
// Output: The vehicle is starting.
//         The sports car is ready to zoom!

Different outputs are displayed based on whether the super keyword is used in the overridden function.

 

Key Points to Remember

  • The override keyword is essential for providing a new implementation to an open function in the base class.
  • The super keyword allows accessing functions of the base class, enabling the reuse of code.

Overriding functions in Kotlin allows derived classes to provide specific implementations of functions from the base class, enabling customization and extended functionality.

The use of the super keyword further enhances this by allowing derived classes to build upon the existing implementations in the base class, promoting code reusability and a hierarchical structure in the codebase.

 

6. Understanding Interfaces

Interfaces in Kotlin are a way to define a contract for classes without implementing any behavior.

Interfaces can contain abstract methods (methods without a body) and methods with a default implementation. Classes that implement an interface must provide implementations for all of its abstract methods.

Why Use Interfaces?

  • Multiple Inheritance: Interfaces allow a class to inherit functionalities from multiple sources, as a class can implement multiple interfaces.
  • Flexibility: Interfaces provide a way to define methods that must be implemented by a class, ensuring that certain functionalities are present.

Explaining Interfaces with an Example

  1. Defining an Interface
interface Drivable {
    fun drive()
}

Here, an interface Drivable is defined with an abstract method drive().

  1. Implementing an Interface
class Car : Drivable {
    override fun drive() {
        println("The car is driving.")
    }
}

The Car class implements the Drivable interface and provides an implementation for the drive() method.

  1. Using the Interface
val myCar = Car()
myCar.drive()  // Output: The car is driving.

An object of the Car class is created, and the drive() method is called.

  1. Interface with Default Implementation
interface Drivable {
    fun drive() {
        println("Driving the vehicle.")
    }
}

class Bicycle : Drivable

val myBicycle = Bicycle()
myBicycle.drive()  // Output: Driving the vehicle.

The interface has a default implementation of the drive() method, so the Bicycle class doesn’t need to provide an implementation.

 

Key Points to Remember

  • Interfaces can contain abstract methods and methods with default implementations.
  • A class can implement multiple interfaces, providing a form of multiple inheritance.
  • When a class implements an interface, it needs to provide implementations for all its abstract methods.

Interfaces in Kotlin offer a powerful way to define contracts for classes, ensuring that they implement specific functionalities.

They provide flexibility and allow for a form of multiple inheritance, enabling classes to inherit functionalities from multiple sources, leading to a more organized and reusable code structure.

 

7. What are Repositories in Android Development?

In the context of Android development, particularly when following the MVVM (Model-View-ViewModel) architecture, a Repository is a class that acts as a clean API for data access to the rest of the application.

It abstracts the origin of the data, which can come from a network source, caching, or a local database.

MVVM Architecture: Why Use Repositories?

  • Decoupling: Repositories allow for decoupling of the data sources from the rest of the application. The ViewModel interacts with a Repository, and it doesn’t need to know where the data comes from.
  • Data Aggregation: A Repository can manage and coordinate data from multiple sources, providing a unified API.
  • Offline Capability: Repositories can cache network data, allowing apps to work offline and providing a better user experience.

Explaining Repositories with an Example

Imagine you are building an app that displays user profiles. The data can come from a local database or a network source.

  1. Defining a Repository
class UserRepository(private val userDao: UserDao, 
private val userService: UserService) {
    
    fun getUser(userId: String): LiveData<User> {
        // Logic to fetch data from network or local database
    }
}

UserRepository is a class that takes a UserDao (local database) and a UserService (network service) as parameters.

  1. Using the Repository in a ViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel() {
    
    fun getUser(userId: String): LiveData<User> {
        return repository.getUser(userId)
    }
}

UserViewModel interacts with UserRepository to get the user data. It doesn’t know whether the data comes from the network or a local database.

  1. Accessing Data in the View

The View observes the data provided by the ViewModel and updates the UI accordingly.

 

Key Points to Remember

  • Repositories abstract the origin of the data, providing a clean API to the rest of the application.
  • Repositories can manage data from multiple sources, such as network sources and local databases.
  • Using Repositories makes the code more modular, maintainable, and testable.

Repositories play a crucial role in Android application architecture by acting as a clean API for data access, managing data sources, and ensuring that the data flows seamlessly through the different layers of the application.

Understanding and implementing Repositories will help in creating robust, maintainable, and user-friendly Android applications.

 

8. MVVM Architecture: APIs Briefly

APIs, or Application Programming Interfaces, are sets of rules and protocols that allow one software application to interact with another. They define the methods and data formats that applications can use to communicate with each other.

APIs are used to integrate different software systems, enabling them to work together, share data, and enhance functionality.

In simpler terms, think of an API as a menu in a restaurant. The menu offers a list of dishes you can order, along with a description of each dish. When you specify which dish you want, the kitchen (i.e., the system) prepares the dish and serves it.

In this analogy, the menu is the API, the order is the request, and the dish that is served to you is the response.

 

Conclusion: The Foundations of MVVM, Inheritance, and Interfaces – Day 8 Android 14 Masterclass

After exploring the foundations and advanced concepts of MVVM, inheritance, and interfaces, it’s clear that these constructs are more than just theoretical concepts; they are practical tools that enable the creation of robust and maintainable Android applications. We’ve seen how a well-structured MVVM pattern enhances testability and maintainability, while inheritance and interfaces contribute to a clean and efficient codebase. As we wrap up Day 8 of the Android 14 Masterclass, remember that the power of these principles lies in their proper implementation. Use them wisely to build apps that not only function seamlessly across different Android versions and devices but also provide a seamless and enjoyable experience for the end-users.

 

If you want to skyrocket your Android career, check out our The Complete Android 14 & Kotlin Development Masterclass. Learn Android 14 App Development From Beginner to Advanced Developer.

Master Jetpack Compose to harness the cutting-edge of Android development, gain proficiency in XML — an essential skill for numerous development roles, and acquire practical expertise by constructing real-world applications.

 

Check out Day 6 of this course here.

Check out Day 7 of this course here.

Check out Day 9 of this course here.

 

Lost in coding? Discover our Learning Paths!
Lost in coding? Discover our Learning Paths!
Enter your email and we will send you the PDF guide:
Enter your email and we will send you the PDF guide