Share
Explore

w24 Android : March 21 Lab Persisting data using the Room Persistent Library Approach.

megaphone

Introduction:

Android Kotlin application programming, particularly with a focus on the Room Persistent Library, is an important aspect of Android development.
In constrast to the very simple JSON approach of {KEY:Value} pairs in Shared Preferences, now we see how to use the Data Handling Framework of ROOM Framework:
Room Persistence Framework is a Wrapper (Mediator) set of APIs to give us connection to the SQLITE database built into Android OS to enable CRUD: Create Read Update Delete records in a SQL format.

Goal is the Persistence of User Data: Shared Preferences vs SQLite
When it comes to persisting user data in an Android app, there are two commonly used approaches: Shared Preferences and SQLite.
Here's how each approach persists data:
Shared Preferences:
Shared Preferences is a lightweight storage option for small amounts of data, typically used for storing user preferences or simple key-value pairs.
Data stored in Shared Preferences is stored in a private partition accessible only by the application that created it, ensuring security.
Shared Preferences data persists across user sessions, even if the app is stopped, restarted, or if the device is rebooted.
It is represented by a small number of key-value pairs and is commonly used for storing user preferences that should be remembered across sessions, such as a user's preferred settings or their game score.
However, Shared Preferences is not suitable for storing large or complex datasets.
Shared Preferences is built into the Android Kotlin operating system. It is an Android API that allows activities or applications to store and retrieve data in the form of key-value pairs. The data stored in Shared Preferences remains persistent even if the app is closed, until it is deleted or cleared. The Android system uses Shared Preferences to store app settings data in the form of an XML file under the data/data/{application package}/share_prefs directory.


To access Shared Preferences in an application, you need to get an instance of it using methods such as getSharedPreferences() or getPreferences(). The modifications to the preferences data are performed through the SharedPreferences.Editor object. You can also delete the preferences data of an application using the appropriate method.

Shared Preferences is commonly used for storing small amounts of primitive data, such as user preferences or simple key-value pairs. It provides a convenient way to store and retrieve data without the need for a full-fledged database like SQLite.

SQLite: SQL is what we need for more data heavy applications requiring build a Data Model.
(Expert practioner’s Note: Look for ways to push as much as possible of the algorithm processing in your data persistence layer such as the SQL. Should be able to push about 70% of your algorithm execution into the Data Container).

SQLite is a relational database management system that provides a more robust and structured approach to data storage. Implements the ANSI 77 Implementation of SQL.
It is an implementation of the SQL language and is used for storing structured data in a private database.
SQLite databases are stored as files on the device's file system and can be accessed by the application that created them.
SQLite is suitable for scenarios where there is a large amount of data to be stored, and where more complex querying and manipulation (and data modelling) of data is required.
It provides the ability to perform advanced database operations, such as filtering, sorting, and joining data.
Room, which is part of the Android Development Framework called Android Jetpack, is an abstraction layer over SQLite that simplifies database setup, configuration, and interactions with the app. ROOM is a FRAMEWORK which a set of APIs that deliver the low level implementation details of CRUD.

In summary, Shared Preferences is ideal for storing small amounts of simple data, such as user preferences, while SQLite is better suited for larger and more complex datasets that require structured storage and advanced querying capabilities.

Android Developer Library for Room
The Android Developer Library provides comprehensive documentation and resources for Room, which is a part of Android Jetpack and serves as an abstraction layer over SQLite.
Room allows for more robust database access while harnessing the full power of SQLite.

Here are some useful links to the Android Developer Library for Room:
This article provides an introduction to Android Jetpack and explains how Room is a part of it. It also highlights the benefits of using Room for database access.
This official Android Developers guide explains how to save data in a local database using Room. It covers topics such as caching data, compile-time verification of SQL queries, and streamlined database migration paths.
This page provides information about Room, including the latest version, release notes, and dependencies required to use Room in your app.
This page provides information about SQLite, which is the underlying database technology used by Room. It also mentions that Room is an abstraction layer over SQLite and provides more robust database access.
This article provides a basic implementation of Room Database with Repository and ViewModel. It explains the advantages of using Room, such as compile-time verification of SQL queries, less boilerplate code, and easy integration with other architecture components.
This article explains how to use Room Database with MVVM Architecture and Kotlin Coroutines. It provides a step-by-step guide to creating a Note App using Room for CRUD operations.
This article provides a tutorial on implementing Room Database in an Android application. It covers topics such as creating a new project, adding Room dependencies, and setting up the database.
This official Android Developers guide explains how to persist data with Room. It provides an overview of Room's features, such as simplified database setup, configuration, and compile-time checks of SQLite statements.
This tutorial provides an example of building a Todo App using Room Persistence Library. It covers topics such as the components of Room, creating a database, and performing CRUD operations.
This article provides an introduction to Room Persistent Library in Android. It explains how Room serves as an abstraction layer over SQLite and simplifies database access.

Room is a part of Android Jetpack and is an abstraction layer over SQLite. It allows for more robust database access while harnessing the full power of SQLite.
### Understanding Room Persistent Library
#### Key Features: 1. **Compile-Time Verification:** Room verifies SQL queries at compile time. This means errors are caught earlier in the development process, making your database code more robust and stable.
2. **Boilerplate Code Reduction:** Room minimizes the amount of boilerplate code required to interact with the database, as compared to raw SQLite.
3. **Integration with LiveData and RxJava:** Room works well with other architecture components like LiveData and RxJava, facilitating easy data observation and stream management.
4. **Easy Database Migration:** Room provides support for database migrations with less effort.
5. **Annotations-Based:** Developers use annotations like `@Entity`, `@Dao`, and `@Database` to define tables, database access objects (DAOs), and databases.

#### Components:
1. **Entity:** Represents a table within the database. Annotated with `@Entity`.
2. **DAO KOTLIN OBJECT (Data Access Object):** Provides the methods that your app uses to query, update, insert, and delete data in your database.
You put methods in your DAO corresponding to create / read / update / delete of records in your Tables (ENTITIES).
3. **Database:** An abstraction class that holds the database and serves as the main access point for the underlying connection to your app's persisted data.

### Persistence in Local Storage - Traditional Methods

Traditionally, persisting data to local storage in Android could be done using:
1. **Shared Preferences:** Ideal for storing small amounts of data in key-value pairs. Not suitable for complex data structures.
2. **SQLite Database:** Provides a structured database environment but requires a significant amount of boilerplate code for setup, queries, and updates.
3. **File Storage:** Saving data directly to the device's file system. Useful for text, binary data, or even object serialization.

How Room Differs from Traditional Local Storage Methods
1. **Abstraction Over SQLite:** Room provides a higher level of abstraction over SQLite, simplifying database operations.
2. **Compile-Time Safety:** SQL queries are checked at compile time in Room, reducing runtime errors.
3. **Less Boilerplate:** Room significantly reduces the amount of boilerplate code, especially compared to raw SQLite usage.
4. **Integration with Modern Tools:** Room's integration with LiveData, RxJava, and Kotlin Coroutines makes it more suited for modern Android development with efficient data handling.
5. **Migration Support:** Room provides an easier path for database migrations and schema updates.
6. **Annotation-Driven:**
Room uses annotations for database entities and operations, making the code more declarative and easier to understand.

In summary, while traditional methods like SharedPreferences and raw SQLite are still valid, Room provides a more sophisticated and efficient way to interact with SQLite databases, especially suitable for larger, more complex applications where database integrity, ease of maintenance, and scalability are important.
The big win with ROOM is support for constructing complex data modeling to model the business objects in your Business Domain.

To create an Android Kotlin application utilizing the Room Persistence Library approach for data storage and retrieval, here are the
MainActivity.kt and the
activity_main.xml file
for the desired functionality:

MainActivity.kt using Room Persistence Library:

import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import androidx.room.Room import com.example.myapp.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var database: AppDatabase // Assuming AppDatabase is the Room Database class private lateinit var messageDao: MessageDao // Assuming MessageDao is the Data Access Object interface
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root)
database = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "my-database").build() messageDao = database.messageDao()
binding.buttonSave.setOnClickListener { val message = binding.editTextMessage.text.toString() saveMessage(message) }
displayMessage(readMessage()) }
private fun saveMessage(message: String) { // Assuming that the Message class has been defined for the data structure val messageObject = Message(message) messageDao.insert(messageObject) Toast.makeText(this, "Message saved", Toast.LENGTH_SHORT).show() }
private fun readMessage(): String? { val lastMessage = messageDao.getLastMessage() return lastMessage?.text }
private fun displayMessage(message: String?) { binding.textViewDisplay.text = message } }
megaphone

Code Analysis:

Line-by-Line Analysis and Visualization of MainActivity.kt using Room Persistence Library:
1. `import` Statements: - `import androidx.appcompat.app.AppCompatActivity`:
Imports the `AppCompatActivity` class for the main activity. - `import android.os.Bundle`: Imports the `Bundle` class for managing activity state. - `import android.widget.Toast`: Imports the `Toast` class for displaying short-lived messages. - `import androidx.room.Room`: Imports the Room library's `Room` class for database creation. - `import com.example.myapp.databinding.ActivityMainBinding`: Imports the auto-generated `ActivityMainBinding` class for data binding.
2. Class Declaration and Initialization:
- `class MainActivity : AppCompatActivity() {`: Defines the `MainActivity` class that extends `AppCompatActivity` as the main activity.
- `private lateinit var binding: ActivityMainBinding`: Declares a lateinit variable `binding` of type `ActivityMainBinding` for view binding.
- `private lateinit var database: AppDatabase`: Declares a lateinit variable `database` of type `AppDatabase` for the Room database.
- `private lateinit var messageDao: MessageDao`: Declares a lateinit variable `messageDao` of type `MessageDao` for the Data Access Object.

3. `onCreate` Method:
- `onCreate` method is called when the activity is starting.
- `database = Room.databaseBuilder(...)`: Initializes the Room database by building a database instance using the `databaseBuilder` method.
- `messageDao = database.messageDao()`: Initializes the Data Access Object for database operations.
- `binding.buttonSave.setOnClickListener { ... }`:
Sets a click listener for the "Save" button to handle saving input message.
- `displayMessage(readMessage())`:
Displays the last saved message on UI when the activity is created.

4. `saveMessage` Method:
- Saves the user input message by creating a new `Message` object and inserting it into the database using the `insert` method of the `messageDao`.
- Displays a "Message saved" `Toast` message upon successful saving.

5. `readMessage` Method:
- Retrieves the last saved message from the database using the `getLastMessage` method of the `messageDao`.
- Returns the retrieved message text or `null` if no message is found.
6. `displayMessage` Method:
- Displays the retrieved message on the `textViewDisplay` using view binding.
The `MainActivity.kt` file utilizes the Room Persistence Library for data storage, enabling data persistence and retrieval operations to save and display user messages efficiently in an Android Kotlin application.

activity_main.xml for the Layout:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<EditText
android:id="@+id/editTextMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter your message"
android:layout_margin="16dp"/>

<Button
android:id="@+id/buttonSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save"
android:layout_below="@id/editTextMessage"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"/>
<TextView
android:id="@+id/textViewDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/buttonSave"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"/>
</RelativeLayout>

The activity_main.xml code yields the basic layout, but there are a few things to consider to ensure it works effectively in your Android Studio project:

XML Namespace Declarations: The provided XML has the necessary namespace declarations for Android (xmlns:android="http://schemas.android.com/apk/res/android") and tools (xmlns:tools="http://schemas.android.com/tools"). This is essential for the XML to be parsed correctly.

Widget IDs: The IDs for the EditText, Button, and TextView are correctly defined. These IDs must match those used in your ActivityMainBinding class in Kotlin, which they seem to do.

Layout Structure: The RelativeLayout is a valid choice for simple layouts. However, consider using ConstraintLayout for more complex layouts, as it offers more flexibility and efficiency.

Compatibility with Your Kotlin Code: Ensure that the IDs used in this XML file match those referenced in your Kotlin code. For instance, editTextMessage, buttonSave, and textViewDisplay should be the same in both your Kotlin code and XML layout.

Dimensions and Styles: The layout dimensions and styles (like wrap_content, match_parent, margins, etc.) are basic and should work for a simple UI. However, you may want to customize the styles, sizes, and positions to better fit the design requirements of your app.

Missing Attributes: There are no constraints defined (as this uses RelativeLayout), but ensure any specific positioning or alignment requirements are met. Additionally, attributes like android:layout_height and android:layout_width are defined, which is necessary.
In this implementation, the MainActivity utilizes the Room Persistence Library, an SQLite object mapping library, for data storage and retrieval.
The layout file activity_main.xml contains views to handle user input, saving, and displaying data.
This example demonstrates the use of Room Persistence Library for local data persistence in Android Kotlin applications.
This code uses rooms library.
The code snippet provided in the second part of the response indeed uses the Room Persistence Library for data storage and retrieval.
This library is instrumental in managing the local database for Android applications, offering efficient data handling and enabling seamless interaction with structured data.


error

The complete build.gradle.kts file to make the room persistence library work in an Android Kotlin project.


plugins { id("com.android.application") kotlin("android") kotlin("kapt") }
android { compileSdk = 31 defaultConfig { applicationId = "com.example.myapp" minSdk = 21 targetSdk = 31 versionCode = 1 versionName = "1.0" } buildTypes { getByName("release") { isMinifyEnabled = false proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } buildFeatures { viewBinding = true } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" } }
dependencies { implementation(fileTree()) implementation("androidx.appcompat:appcompat:1.4.0") implementation("com.google.android.material:material:1.5.0") implementation("androidx.constraintlayout:constraintlayout:2.1.3") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0") implementation("androidx.room:room-runtime:2.3.0") kapt("androidx.room:room-compiler:2.3.0") implementation("androidx.room:room-ktx:2.3.0") // Other dependencies as per project requirements
testImplementation("junit:junit:4.13.3") androidTestImplementation("androidx.test.ext:junit:1.1.3") androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") }
This build.gradle.kts file includes the necessary configurations to enable the Room Persistence Library and ViewBinding, as well as setting up the required dependencies for your Android Kotlin project, facilitating the use of Room for data persistence.
megaphone

Code analysis of build.gradle.kts

1. `plugins` Section: - `id("com.android.application")`: Specifies the Android application plugin for Android projects. - `kotlin("android")`: Enables the Kotlin Android Extensions for Kotlin usage in the Android project. - `kotlin("kapt")`: Enables Kotlin Annotation Processing for generating code during compilation based on annotations.

2. `android` Block: - `compileSdk = 31`: Specifies the SDK version used for compiling the project. - `defaultConfig { ... }`:
Specifies basic configuration details for the Android project, including application ID, minimum SDK version, target SDK version, version code, and version name.
- `buildTypes { ... }`: Configures build types, with special settings like minification being disabled in the release build to keep code readable and retain useful names post-compilation.
- `buildFeatures { ... }`:
Enables specific build features, such as
ViewBinding in this case to simplify view access in the code. - `compileOptions { ... }`: Sets up compatibility options for Java source and target versions. - `kotlinOptions { ... }`: Sets the JVM target version for Kotlin code compilation.

3. `dependencies` Block: - `implementation(fileTree())`: Includes files into the project from the file tree.
- `implementation("androidx.appcompat:appcompat:1.4.0")`: Adds the AppCompat library for providing backward compatibility to the app.
- `implementation("com.google.android.material:material:1.5.0")`: Incorporates the Material Components library for consistent UI elements and features.
- `implementation("androidx.constraintlayout:constraintlayout:2.1.3")`: Adds ConstraintLayout for flexible and dynamic layouts.
- `implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0")`:
Includes the Lifecycle ViewModel library for lifecycle management.
- `implementation("androidx.room:room-runtime:2.3.0")`:
Brings in the Room Persistence Library for managing a local SQLite database.
- `kapt("androidx.room:room-compiler:2.3.0")`: Adds the Room compiler for annotation processing required by Room.
- `implementation("androidx.room:room-ktx:2.3.0")`: Includes Room Kotlin Extensions for enhanced Kotlin support with Room.
- Other dependencies are typically added as per specific project requirements.
4. `testImplementation` and `androidTestImplementation`: - These include dependencies required for testing, such as JUnit for unit testing and Espresso for UI testing.
This comprehensive `build.gradle.kts` file sets up the Android project with necessary dependencies, configurations, and plugins for Room Persistence Library integration, enabling efficient data storage and retrieval in the Android Kotlin application.


megaphone

The `MainActivity.kt` file utilizing the Room Persistence Library demonstrates superior data handling capabilities compared to the Shared Preferences approach in the following ways:


1. **Structured Data Persistence**:
- Room allows for storing structured data in a local SQLite database with relationships between entities, offering greater flexibility and scalability compared to the key-value pairs storage in Shared Preferences.

2. **Efficient Data Retrieval**:
- Room provides efficient query mechanisms for data retrieval based on specific criteria, allowing for complex data querying and filtering options that Shared Preferences lack.

3. **Complex Data Operations**:
- With Room, complex database operations like inserting, updating, deleting, and querying data are easily managed through Data Access Objects (DAOs), enabling more intricate data operations compared to the basic read/write actions of Shared Preferences.

4. **Concurrency and Thread Safety**:
- Room handles concurrency and thread safety automatically, ensuring data consistency and preventing data corruption or conflicts in multi-threaded environments, offering a higher level of data integrity compared to Shared Preferences.

5. **Data Integrity and Relationships**:
- Room allows defining entity relationships, enforcing data integrity constraints with foreign key constraints, ensuring data consistency and referential integrity, which is not supported by Shared Preferences.

6. **Migration Support**:
- Room provides built-in migration support for database schema changes, allowing for seamless updates to the database structure without data loss or compatibility issues, a feature that Shared Preferences lack.

7. **Code Maintainability**:
- With Room, data handling operations are organized within the database layer, promoting a cleaner code structure and separation of concerns, enhancing code maintainability compared to scattering data storage logic throughout the application with Shared Preferences.

8. **Integration with LiveData and ViewModels**:
- Room seamlessly integrates with Android Architecture Components, such as LiveData and ViewModels, facilitating data observation and reactive UI updates based on data changes, enhancing the app's responsiveness and user experience beyond the capabilities of Shared Preferences.

In conclusion, the Room Persistence Library empowers developers to handle data more efficiently, maintain data integrity, and support complex data operations, making it a superior choice for robust data persistence requirements in Android applications compared to the simplistic key-value storage mechanism of Shared Preferences.

A few concluding Observations on this Lab:
This Android Kotlin application is aligned with the current best practices in Android development.
Let's break down the main aspects:
1. **Use of View Bindings**: You're correctly using View Bindings in `MainActivity.kt`.
This is a recommended practice over `findViewById`, as it provides null safety and type safety.
2. **Room Persistence Library**: Utilizing Room for database operations is a great choice. It provides an abstraction layer over SQLite and simplifies database access.
3. **Event Handling**: You've correctly used `setOnClickListener` in Kotlin for handling button clicks. This is the preferred way over the older method of implementing an `OnClickListener` interface.
4. **Layout XML**: Your `activity_main.xml` layout uses a `RelativeLayout`. While this is not deprecated, consider using `ConstraintLayout` for more flexible and complex layouts with a flat view hierarchy. `ConstraintLayout` is generally preferred for modern Android development due to its flexibility and performance benefits.
5. **Data Access Object (DAO)**: The usage of a DAO interface (`MessageDao`) for database operations adheres to best practices by separating the data layer and promoting a clean architecture.
6. **Asynchronous Processing**: One aspect to consider is the handling of database operations. Room supports Coroutines in Kotlin, which allows for clean and concise asynchronous database operations. Make sure that your database operations (like `messageDao.insert(messageObject)` and `messageDao.getLastMessage()`) are performed off the main thread to prevent blocking the UI. This can be achieved using Kotlin Coroutines.
7. **Error Handling and Validation**: There should be proper error handling and input validation when dealing with user inputs and database operations. This isn't visible in the code snippet provided, but it's an important part of developing robust applications.
8. **XML Layout Naming**: It's a minor point, but using consistent and descriptive naming conventions for layout files (like `activity_main.xml`) and IDs (like `editTextMessage`, `buttonSave`) is a good practice for maintainability.
In summary, our application adheres to several key best practices in Android Kotlin development.
However, always be open to integrating new patterns and practices as the Android development ecosystem evolves.
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.