# Managing State with OOP in Android Applications

In Android development, managing application state is crucial for creating robust, responsive, and user-friendly applications. Application state refers to the data that an application maintains while it is running, including user inputs, preferences, and session information. Using Object-Oriented Programming (OOP) principles to manage state can help you build scalable and maintainable applications. This blog explores techniques for managing application state in Android using OOP principles, providing best practices and practical examples.

### **Understanding Application State in Android**

Application state in Android can be categorized into several types:

* **UI State**: Information about the current state of the user interface, such as selected items, form inputs, and navigation history.
    
* **Session State**: Data that persists during a user's session, such as login status and temporary user settings.
    
* **Persistent State**: Data that remains even after the application is closed, such as user preferences and database entries.
    

Managing state effectively is essential for ensuring that your application behaves consistently, maintains user context, and provides a smooth user experience across different scenarios, such as screen rotations, backgrounding, and application restarts.

### **OOP Principles for State Management**

Using OOP principles to manage application state involves designing your application’s architecture in a way that encapsulates state, promotes reusability, and facilitates maintenance. Key OOP principles to consider include:

* **Encapsulation**: Encapsulate state management logic within classes to hide the internal details and expose only the necessary interfaces.
    
* **Abstraction**: Abstract state management into reusable components to simplify the overall design and promote code reuse.
    
* **Inheritance**: Use inheritance to extend or modify state management behavior without altering the existing code.
    
* **Polymorphism**: Implement polymorphism to handle different state scenarios flexibly, allowing the same interface to manage various states.
    

### **Techniques for Managing State in Android Using OOP**

#### **1\. Using ViewModel for UI State Management**

The `ViewModel` class, part of the Android Architecture Components, is designed to store and manage UI-related data in a lifecycle-conscious way. It helps preserve UI state across configuration changes, such as screen rotations.

**Example**: Using `ViewModel` to Manage UI State

**ViewModel Class**:

```java
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {
    private final MutableLiveData<String> userInput = new MutableLiveData<>();

    public LiveData<String> getUserInput() {
        return userInput;
    }

    public void setUserInput(String input) {
        userInput.setValue(input);
    }
}
```

**Activity Implementation**:

```java
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private MainViewModel viewModel;
    private EditText editText;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);

        viewModel = new ViewModelProvider(this).get(MainViewModel.class);
        viewModel.getUserInput().observe(this, input -> textView.setText(input));

        editText.addTextChangedListener(new SimpleTextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                viewModel.setUserInput(s.toString());
            }
        });
    }
}
```

In this example:

* The `MainViewModel` class manages the UI state by holding user input.
    
* The `MainActivity` observes the `LiveData` from the `ViewModel` to update the UI when the data changes, preserving the state across configuration changes.
    

#### **2\. Using Singleton for Global State Management**

A singleton class ensures that only one instance of the class exists throughout the application’s lifecycle, making it ideal for managing global state, such as user session data or application settings.

**Example**: Singleton Pattern for Global State

**Singleton Class**:

```java
public class SessionManager {
    private static SessionManager instance;
    private String authToken;

    private SessionManager() {
        // Private constructor to prevent instantiation
    }

    public static synchronized SessionManager getInstance() {
        if (instance == null) {
            instance = new SessionManager();
        }
        return instance;
    }

    public void setAuthToken(String token) {
        this.authToken = token;
    }

    public String getAuthToken() {
        return authToken;
    }
}
```

**Usage in Activity**:

```java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SessionManager sessionManager = SessionManager.getInstance();
        sessionManager.setAuthToken("exampleToken");

        String token = sessionManager.getAuthToken();
        // Use the token as needed
    }
}
```

In this example:

* The `SessionManager` class follows the Singleton pattern to manage the user session state across the application.
    
* The `MainActivity` accesses the global state via the `SessionManager` instance.
    

#### **3\. Using Repository Pattern for Persistent State Management**

The Repository pattern provides a clean API for data access, abstracting the details of data storage and retrieval. It separates the data layer from the rest of the application, making it easier to manage persistent state.

**Example**: Repository Pattern for Data Management

**UserRepository Class**:

```java
public class UserRepository {
    private static UserRepository instance;
    private final UserDao userDao;

    private UserRepository(Context context) {
        UserDatabase db = UserDatabase.getInstance(context);
        userDao = db.userDao();
    }

    public static synchronized UserRepository getInstance(Context context) {
        if (instance == null) {
            instance = new UserRepository(context);
        }
        return instance;
    }

    public LiveData<User> getUser(int userId) {
        return userDao.getUserById(userId);
    }

    public void insertUser(User user) {
        userDao.insert(user);
    }
}
```

**Usage in ViewModel**:

```java
public class UserViewModel extends ViewModel {
    private final UserRepository userRepository;
    private LiveData<User> user;

    public UserViewModel(Application application) {
        userRepository = UserRepository.getInstance(application);
    }

    public LiveData<User> getUser(int userId) {
        if (user == null) {
            user = userRepository.getUser(userId);
        }
        return user;
    }

    public void insertUser(User user) {
        userRepository.insertUser(user);
    }
}
```

In this example:

* The `UserRepository` provides a clean interface for accessing user data, managing the persistent state in the database.
    
* The `UserViewModel` interacts with the `UserRepository` to fetch and manage user data.
    

#### **4\. Using EventBus for Event-Driven State Management**

EventBus allows decoupled components to communicate by subscribing and publishing events, making it useful for managing state changes across different parts of the application.

**Example**: Using EventBus for State Management

**Event Class**:

```java
public class UserEvent {
    public final String message;

    public UserEvent(String message) {
        this.message = message;
    }
}
```

**Publisher Activity**:

```java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import org.greenrobot.eventbus.EventBus;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Publish an event
        EventBus.getDefault().post(new UserEvent("User logged in"));
    }
}
```

**Subscriber Activity**:

```java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class AnotherActivity extends AppCompatActivity {
    private TextView messageTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_another);
        messageTextView = findViewById(R.id.messageTextView);
    }

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onUserEvent(UserEvent event) {
        messageTextView.setText(event.message);
    }
}
```

In this example:

* The `UserEvent` class represents an event that carries a message.
    
* `MainActivity` publishes an event using EventBus.
    
* `AnotherActivity` subscribes to the event and updates the UI when the event is received.
    

#### **5\. Using SharedPreferences for Simple Persistent State**

`SharedPreferences` is a lightweight way to store key-value pairs, suitable for managing simple persistent state like user settings and preferences.

**Example**: Using `SharedPreferences` for User Settings

**SharedPreferences Helper Class**:

```java
public class PreferenceManager {
    private static final String PREFERENCES_FILE = "com.example.myapp.PREFERENCES";
    private static final String KEY_USER_NAME = "username";

    private SharedPreferences sharedPreferences;

    public PreferenceManager(Context context) {
        sharedPreferences = context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
    }

    public void saveUserName(String userName) {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString(KEY_USER_NAME, userName);
        editor.apply();
    }

    public String getUserName() {
        return sharedPreferences.getString(KEY_USER_NAME, "default_name");
    }
}
```

**Usage in Activity**:

```java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    private PreferenceManager preferenceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        preferenceManager = new PreferenceManager(this);
        preferenceManager.saveUserName("Alice");

        String userName = preferenceManager.getUserName();
        // Use the user name as needed
    }
}
```

In this example:

* The `PreferenceManager` class provides methods to save and retrieve user settings using `SharedPreferences`.
    
* The `MainActivity` saves and retrieves the user name using the `PreferenceManager`.
    

### **Best Practices for Managing State with OOP in Android**

1. **Encapsulate State Management**: Encapsulate state management logic within classes to promote reusability and maintainability. Use access modifiers to control access to state data.
    
2. **Separate Concerns**: Follow the Single Responsibility Principle by separating state management logic from UI logic and business logic.
    
3. **Use Lifecycle-Aware Components**: Utilize lifecycle-aware components, such as `ViewModel` and `LiveData`, to manage state in a way that respects the lifecycle of Android components.
    
4. **Persist Critical State**: Ensure critical state data is persisted appropriately using databases, `SharedPreferences`, or other storage mechanisms to survive application restarts and configuration changes.
    
5. **Avoid Memory Leaks**: Be mindful of object lifecycles and avoid keeping references to contexts or views that may lead to memory leaks.
    
6. **Handle Configuration Changes**: Implement proper handling for configuration changes, such as screen rotations, to preserve and restore state seamlessly.
    
7. **Use Dependency Injection**: Use dependency injection frameworks to manage dependencies and state, promoting loose coupling and easier testing.
    

### **Conclusion**

Managing state in Android applications using Object-Oriented Programming principles helps create robust, maintainable, and scalable applications. By leveraging techniques such as using `ViewModel` for UI state, implementing the Singleton pattern for global state, adopting the Repository pattern for persistent state, and employing `SharedPreferences` for simple state management, you can ensure that your application handles state efficiently and provides a seamless user experience. Embrace these OOP principles and best practices to build better, more reliable Android applications.
