0% found this document useful (0 votes)
10 views

Room Database

Android Room is an ORM library for SQLite that simplifies database operations in Android projects by generating code based on annotations. It consists of three main components: Entity classes for database tables, DAO interfaces for data access methods, and a Database class for managing the database. Room also supports features like auto-migrations, LiveData integration, and complex queries, making it a robust solution for local data storage in Android applications.

Uploaded by

Yash Bhardwaj
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Room Database

Android Room is an ORM library for SQLite that simplifies database operations in Android projects by generating code based on annotations. It consists of three main components: Entity classes for database tables, DAO interfaces for data access methods, and a Database class for managing the database. Room also supports features like auto-migrations, LiveData integration, and complex queries, making it a robust solution for local data storage in Android applications.

Uploaded by

Yash Bhardwaj
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

Working With Room Short Note

Android Room is a ORM(object relational mapping) library created for


SQLite. SQLite is the database management system we use to create
databases in Android projects.

Before 2017, we had to write a lot of complex codes for database


operations. But now, Room recognises our requirements (considering
annotations) and generates most of the codes(in byte code format/class
files) for us.

Room acts as a layer on top of SQLite. It makes our coding lives much
easier and efficient.

Must have components for Android Room


To use Room library/framework, you need 3 code components. Entity
classes, Dao interface and the database class.

1) Room Entity Classes.


One entity class for each table
●​ We need to create entity classes for each database table.
●​ Moreover, to qualify a class as a room entity class, we need to
annotate it with @Entity .
●​ Holding data is the only purpose of these classes. So, we will create
them as Kotlin data classes.

Name of the database table


●​ Database table will have the same name as the class name.
●​ And, if we need a different name, we could give that name as the
value of the “tableName” property.

Name of the table columns


●​ Columns will have the same names as the data class’s variable
names.
●​ But, if we want different names we can provide them using
@ColumnInfo​

Primary Key
●​ Use the @PrimaryKey on the variable selected as the primary key of
the table.
●​ If you want the primary key to auto generate set autoGenerate =
true

2) Room DAO interface


We need to create an interface . And, mark it with @Dao .

DAO stands for “Data Access Object”. This interface is where we define
functions to deal with the database.

(you could also define DAO as an abstract class)

Function names
Function names are not important. You can give any name you like.

Annotations
But, annotating the function with correct annotation is very important.

For instance, we have annotated above “insertBook” function(method) with


@Insert . Therefore, room will know that the purpose of that function is
inserting data. Room library does not understand the function names. But,
room recognizes annotations.

Suspend keyword
“Kotlin coroutines” is the current best practice to manage threads in android
development. So, we are going to use them to execute database
operations in a background thread. Therefore, we need to define these
functions as suspending functions.
But, it is not required for query functions. Because, Room library uses its
own dispatcher to run queries on a background thread.

SQL Statement
For basic Insert, Update and Delete functions we don’t need to write SQL
statements.

But, we need to write a SQL statement for Query functions and for
customized Update and Delete functions.

Room Insert functions


●​ All insert functions should annotate with @Insert
●​ There is no need to write a SQL query for insert functions.
●​ Return type is optional. Most of the time we write room insert
functions without a return type.
●​ But, Room allows us to get the newly inserted row id as a “Long”
value.
●​ For example, if the insert function has a single parameter, return type
would be the new row id of type Long .
●​ On the other hand, if the parameter is an array or a collection, return
type would be an array or a collection of Long values.

Room Update and Delete functions


●​ Update functions should annotate with @Update and delete functions
should annotate with @Delete .
●​ These functions also don’t require a SQL statement. And, return
types are optional.
●​ But, we can add an “int” return type.
●​ These functions return an “int" value indicating the number of rows
that were updated or deleted successfully
3) Room Database Class
We need to create a Room Database class.

This class should be an abstract class. Most importantly, this should extend
the “RoomDatabase” class.

We need to annotate this class with @Database .

Then, provide the list of entity classes with it.

And, also provide the version number . Database’s version number is very
important when we are going to migrate the database.

Finally, we need to define abstract functions to get Dao interfaces inside the
class.

REFERENCE

https://appdevnotes.com/android-room-db-tutorial-for-beginners-in-kotlin/
https://appdevnotes.com/android-mvvm-project-example/

AutoMigration in Room DB
First we need to do the following changes in app level build.gradle
defaultconfig {
//……

kapt {
arguments {
arg("room.schemaLocation", "$projectDir/schemas")
}
}
}

Now add the new columns in data class

@ColumnInfo(name = "student_email", defaultValue = "No


Email")
var email: String,
@ColumnInfo(name = "course_name")
var course: String?

If we don’t give default value then we have to make this nullable.

In database class change the version = 2 and add automigrations


@Database(entities = [Student::class],version = 2,
autoMigrations =
[
AutoMigration(from = 1, to = 2)
]
)
AutoMigrationWith Specification

Like if we want to change the column name from course_name to


subject_name
First change in data class like

@ColumnInfo(name = "subject_name")
var course: String?

Later go to Database class add class with extends AutoMigrationSpec​

@RenameColumn(tableName = "student_info",
fromColumnName = "course_name", toColumnName =
"subject_name")
class Migration2TO3: AutoMigrationSpec

Last do changes in

@Database(entities = [Student::class],version = 3,
autoMigrations =
[
AutoMigration(from = 1, to = 2),
AutoMigration(from = 2, to = 3, spec =
StudentDatabase.Migration2TO3::class)
]
)
Like if we want to remove the column name student_email

@DeleteColumn(tableName = "student_info", columnName =


"student_email")
class Migration3TO4: AutoMigrationSpec

@Database(entities = [Student::class],version = 4,
autoMigrations =
[
AutoMigration(from = 1, to = 2)
AutoMigration(from = 2, to = 3, spec =
StudentDatabase.Migration2TO3::class),
AutoMigration(from = 3, to = 4, spec =
StudentDatabase.Migration3TO4::class)
]
)

Also remove the column name from data class(entity class)

Interview Questions:

Question 1: What is Room Database, and why is it used in Android development?


Answer: Room Database is an Android library that serves as a part of the Android
Architecture Components. It provides an SQLite database abstraction layer, making it
easier for developers to work with databases in their Android applications. Room offers
benefits like compile-time query verification, reduced boilerplate code, and better
performance compared to traditional SQLiteOpenHelper.
Question 2: What are the main components of Room Database?
Answer: Room Database consists of three main components:
1.​ Entity: Represents a table in the database and is defined as a data class
annotated with the @Entity annotation. Each property of the data class
corresponds to a column in the table.
2.​ DAO (Data Access Object): Contains methods to define database operations
(CRUD) for entities. DAOs are interfaces annotated with @Dao, and
methods inside the DAO are annotated with SQL queries using annotations
like @Insert, @Update, @Delete, etc.
3.​ Database: Represents the database itself and is defined as an abstract class
annotated with @Database. It includes a list of entities and a version
number. Room generates the implementation of this class during
compile-time.
Code Example:
@Entity(tableName = "user")
data class User(
@PrimaryKey val id: Int,
val name: String,
val age: Int
)
@Dao
interface UserDao {
@Insert
fun insert(user: User)
@Update
fun update(user: User)
@Delete
fun delete(user: User)
@Query("SELECT * FROM user")
fun getAllUsers(): List<User>
}
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}

Question 3: Explain the difference between @PrimaryKey, @ColumnInfo, and


@ForeignKey annotations.
Answer:
●​ @PrimaryKey: Marks a field in the entity as the primary key for the table.
Each entity must have at least one primary key.
●​ @ColumnInfo: Specifies the details of a column, such as its name in the
table, whether it's indexed, or if it allows null values.
●​ @ForeignKey: Defines a foreign key relationship between two entities. It
ensures referential integrity between the tables.
Question 4: How does Room handle database migrations?
Answer: Room provides database migrations using the Migration class. When you
update your database schema by modifying entities or DAOs, you need to create a new
Migration object and add it to the RoomDatabase.Builder. Room will automatically
execute the necessary migrations to keep the existing data intact.

Code Example:

val migration1to2 = object : Migration(1, 2) {


override fun migrate(database: SupportSQLiteDatabase) {
// Migration code from version 1 to 2
}
}
val appDatabase = Room.databaseBuilder(context, AppDatabase::class.java,
"app_database")
.addMigrations(migration1to2)
.build()
Question 5: What is the purpose of the @Relation annotation in Room?
Answer: The @Relation annotation is used to define relationships between entities in
Room. It allows you to retrieve related entities with a single query, simplifying complex
database queries.

Code Example:

@Entity(tableName = "user")
data class User(
@PrimaryKey val id: Int,
val name: String
)
@Entity(tableName = "book")
data class Book(
@PrimaryKey val id: Int,
val title: String,
@ColumnInfo(name = "user_id") val userId: Int
)
data class UserWithBooks(
@Embedded val user: User,
@Relation(
parentColumn = "id",
entityColumn = "user_id"
)
val books: List<Book>
)
@Dao
interface UserDao {
@Transaction
@Query("SELECT * FROM user")
fun getUsersWithBooks(): List<UserWithBooks>
}

Question 6: How can you observe database changes using LiveData with Room?
Answer: Room provides the LiveData class from the Android Architecture Components,
which can be used to observe changes in the database. By returning a LiveData object
from a DAO method, Room automatically updates the returned LiveData whenever
there are changes in the database.

Code Example:

@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAllUsers(): LiveData<List<User>>
}
// Observe the LiveData in an Activity or Fragment
userDao.getAllUsers().observe(this) { users ->
// Update UI with the latest user data
}

Question 7: How can you execute complex database queries using Room?
Answer: Room supports complex database queries using the @Query annotation in
DAO methods. You can write custom SQL queries and pass parameters using
placeholders.

Code Example:

@Dao
interface UserDao {
@Query("SELECT * FROM user WHERE age > :minAge")
fun getUsersOlderThan(minAge: Int): List<User>
}
Question 8: What is the purpose of the @TypeConverter annotation in Room?
Answer: The @TypeConverter annotation allows you to define custom type converters
in Room. It is useful when you need to store complex data types (such as Date, List, or
custom objects) in the database as primitive types.

Code Example:

class Converters {
@TypeConverter
fun fromTimestamp(value: Long): Date {
return Date(value)
}

@TypeConverter
fun dateToTimestamp(date: Date): Long {
return date.time
}
}
@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
// Database definition
}

Question 9: What are the main components of Android Room, and how do they
interact with each other?
Android Room has three main components: Database, Entity, and DAO.
1. Database: It is an abstract class extending RoomDatabase, acting as the main
access point for underlying SQLite database connections. Annotated with @Database,
it lists all entities and defines a version number.
2. Entity: Represents a table in the SQLite database. A POJO class annotated with
@Entity, its fields define columns while primary keys are specified using @PrimaryKey.
3. DAO (Data Access Object): Interface containing methods to interact with the
database. Annotated with @Dao, it includes query, insert, update, and delete
operations.
Interaction:​
Entities are defined within the Database class, which also provides DAO instances. The
Room library generates code based on annotations, creating a concrete implementation
of the Database class. This implementation manages SQLiteOpenHelper and creates
DAO implementations. Clients use these DAOs to perform CRUD operations on entities,
ensuring type-safety and compile-time checks.

Question 10: Can you explain the differences between Room, SQLite, and Realm,
and when you would use each for Android app development?
Room, SQLite, and Realm are data persistence solutions for Android app development.
Room is an abstraction layer over SQLite, providing a more convenient API and
compile-time checks. SQLite is a lightweight relational database management system
embedded in the Android framework. Realm is a third-party, object-oriented database
with its own storage engine.
Use Room when you need a robust, easy-to-use solution that integrates well with other
Android components (LiveData, ViewModel). It’s suitable for most apps requiring local
data storage.
Choose SQLite if you prefer working directly with SQL queries or require fine-grained
control over the database. However, it lacks some of the convenience features provided
by Room.
Opt for Realm when you need a high-performance, cross-platform solution not limited to
Android. It excels in real-time applications and complex data models but may have a
steeper learning curve compared to Room.

Question 11: How can you handle version updates and schema migrations in
Android Room?
To handle version updates and schema migrations in Android Room, follow these steps:
1. Increment the database version number in the @Database annotation.​
2. Create a Migration class that extends the abstract class “Migration” and implement its
“migrate()” method to define the necessary schema changes.​
3. In the “migrate()” method, use SQLite commands like ALTER TABLE, CREATE
TABLE, or DROP TABLE to modify the schema as needed.​
4. Add the migration object(s) to your RoomDatabase.Builder using the
“addMigrations()” method.
For example:
@Database(entities = {MyEntity.class}, version = 2)
public abstract class MyDatabase extends RoomDatabase {
// ...
}
// Define migration from version 1 to 2
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
// Schema modification code here
}
};
// Build the database with the migration
MyDatabase db = Room.databaseBuilder(context, MyDatabase.class, "my-db")
.addMigrations(MIGRATION_1_2)
.build();

Question 12: What is the role of DAO (Data Access Object) in Android Room, and
how do you create a DAO?
In Android Room, DAO (Data Access Object) plays a crucial role in abstracting
database access and enabling communication between the app components and
SQLite. It defines methods for performing CRUD operations on the database, ensuring
separation of concerns and testability.
To create a DAO:
1. Define an interface or abstract class.​
2. Annotate it with @Dao.​
3. Declare methods for required operations.​
4. Use annotations like @Insert, @Update, @Delete, and @Query to specify SQL
statements or actions.
Example:
@Dao
public interface UserDao {
@Insert
void insert(User user);
@Update
void update(User user);
@Delete
void delete(User user);
@Query("SELECT * FROM users WHERE id = :userId")
User getUserById(int userId);
}

Question 13: How do you handle complex relationships between entities, such as
many-to-many relationships?
To handle complex relationships like many-to-many in Android Room, use associative
entity (also known as join table) to break down the relationship into two one-to-many
relationships. Define entities and their primary keys, then create an associative entity
with foreign keys referencing the primary keys of related entities.
For example, consider Student and Course entities with a many-to-many relationship:
1. Create Student and Course entities with respective primary keys.​
2. Create an associative entity, e.g., StudentCourseJoin, containing foreign keys for
both Student and Course.​
3. Annotate the foreign keys with @ForeignKey annotation to enforce referential
integrity.​
4. Use @Relation annotation in DAOs to retrieve related data across multiple tables.
Here’s a code snippet illustrating this approach:
@Entity​
data class Student(@PrimaryKey val id: Int, val name: String)
@Entity​
data class Course(@PrimaryKey val id: Int, val title: String)
@Entity(primaryKeys = [“studentId”, “courseId”])​
data class StudentCourseJoin(​
@ForeignKey(entity = Student::class,​
parentColumns = [“id”],​
childColumns = [“studentId”])​
val studentId: Int,
@ForeignKey(entity = Course::class,​
parentColumns = [“id”],​
childColumns = [“courseId”])​
val courseId: Int​
)

Question 14: Can you explain the LiveData integration in Android Room and its
benefits in your application?
LiveData integration in Android Room allows observing changes in the database and
automatically updating UI components. It is a lifecycle-aware component, ensuring data
updates are only sent to active observers, preventing memory leaks and crashes.
Benefits include:​
1. Real-time UI updates: LiveData emits data changes, enabling automatic UI refresh.​
2. Lifecycle awareness: Observations stop when activity/fragment is destroyed, avoiding
memory leaks.​
3. Thread safety: LiveData ensures data modifications occur on the main thread,
preventing concurrency issues.​
4. Simplified code: Reduces boilerplate code for handling data updates and managing
lifecycles.
Example usage:
@Entity​
data class User(val id: Int, val name: String)
@Dao​
interface UserDao {​
@Query(“SELECT * FROM user”)​
fun getAllUsers(): LiveData>​
}

Question 15: How do you ensure thread safety when using Android Room in a
multi-threaded environment?
To ensure thread safety with Android Room in a multi-threaded environment, follow
these steps:
1. Use LiveData or Flow: These components automatically handle threading and notify
observers on the main thread when data changes.​
2. Utilize ViewModel: Encapsulate UI-related data within a ViewModel to separate it
from lifecycle-aware components like Activities and Fragments.​
3. Implement Coroutines or RxJava: Employ asynchronous programming techniques for
background tasks, such as database operations, without blocking the main thread.​
4. Apply Synchronization: If necessary, use synchronization mechanisms (e.g.,
synchronized blocks, locks) to protect shared resources from concurrent access.​
5. Design Thread-safe DAOs: Ensure Data Access Objects (DAOs) are designed to be
thread-safe by using appropriate annotations, such as @Transaction, to manage
transactions.​
6. Avoid Static Variables: Refrain from using static variables that can lead to
concurrency issues.

Question 16: What is a TypeConverter in Room and how do you implement


custom TypeConverters?
A TypeConverter in Room is a mechanism that allows conversion between custom data
types and SQLite-supported data types, enabling storage of complex objects in the
database. To implement custom TypeConverters:
1. Create a class with static methods for converting custom data type to
SQLite-compatible type and vice versa.​
2. Annotate each method with @TypeConverter.​
3. Define input parameter as source type and return target type (or reverse).​
4. In your Room Database class, annotate it with @TypeConverters and reference
converter class.
Example:
public class DateConverter {
@TypeConverter
public static Long fromDateToDateLong(Date date) {
return date == null ? null : date.getTime();
}
@TypeConverter
public static Date fromLongToDate(Long dateLong) {
return dateLong == null ? null : new Date(dateLong);
}
}
@Database(entities = {MyEntity.class}, version = 1)
@TypeConverters({DateConverter.class})
public abstract class AppDatabase extends RoomDatabase {
//...
}
Question 17: Can you explain the difference between @Query, @Insert, @Update,
and @Delete annotations in DAO?
In Android Room, DAO (Data Access Object) uses annotations to define CRUD
operations. Each annotation serves a specific purpose:
1. @Query: Executes custom SQL queries and returns results. It can fetch, filter, or join
data from tables. Example:
@Query("SELECT * FROM users WHERE id = :userId")
User getUserById(int userId);
2. @Insert: Inserts new records into the table. Can insert single or multiple objects.
Offers conflict resolution strategies. Example:
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertUsers(User... users);
3. @Update: Updates existing records in the table based on primary key(s). Accepts
single or multiple objects. Example:
@Update
int updateUsers(User... users);
4. @Delete: Removes records from the table using primary key(s). Deletes single or
multiple objects. Example:
@Delete
int deleteUsers(User... users);

Question 18: How can you optimize performance in Android Room using query
optimization techniques?
To optimize performance in Android Room, apply the following query optimization
techniques:
1. Use indices: Create indices on frequently queried columns to speed up searches. Be
cautious as excessive indexing can slow down insertions and updates.
2. Limit data retrieval: Utilize LIMIT and OFFSET clauses to fetch only required data,
reducing memory consumption and improving response time.
3. Opt for compile-time query verification: Annotate DAO methods with @Query to
enable SQLite syntax checking during compilation, preventing runtime errors.
4. Leverage LiveData or Flow: Employ LiveData or Kotlin’s Flow to observe database
changes, ensuring UI updates are efficient and thread-safe.
5. Choose appropriate threading strategy: Execute queries on a background thread
using AsyncTask, Executors, or coroutines to prevent blocking the main thread.
6. Minimize JOIN operations: Reduce complex JOINs by denormalizing tables or
utilizing embedded objects and relations when designing the schema.
7. Use Projections: Select specific columns instead of fetching entire entities to
minimize memory usage and improve query performance.

Question 19: What are the different types of conflict resolution strategies
available in Android Room, and when would you use them?
Android Room offers five conflict resolution strategies: IGNORE, REPLACE, ABORT,
FAIL, and ROLLBACK.
1. IGNORE: Use when you want to skip inserting a new row if it conflicts with an existing
one based on the unique constraint.​
2. REPLACE: Choose this strategy to replace the conflicting row entirely with the new
data.​
3. ABORT: Utilize this option to cancel the current transaction or statement if there’s a
conflict, preserving the original data.​
4. FAIL: Similar to ABORT, but only cancels the specific statement causing the conflict,
allowing other statements in the transaction to proceed.​
5. ROLLBACK: Select this strategy to revert the entire transaction back to its initial state
upon encountering a conflict.
Choose the appropriate strategy based on your application’s requirements for handling
duplicate or conflicting data.

Question 20: How do you handle on-demand or on-change database backups and
restore scenarios with Android Room?
To handle on-demand or on-change database backups and restore scenarios with
Android Room, follow these steps:
1. Create a backup: Use the framework’s
BackupAgentHelper class to create a custom backup agent that copies the SQLite
database file to the backup destination.
2. Register the backup agent: In the app manifest, add the <application> element’s
android:backupAgent attribute pointing to your custom backup agent.
3. Implement LiveData observers: Utilize LiveData in your DAOs to observe changes in
data and trigger backup when necessary.
4. Restore the backup: Override the onRestore() method in your custom backup agent
to copy the backed-up database file back to its original location.
5. Notify the app: After restoring the backup, use WorkManager or other means to notify
the app components about the restored data, so they can refresh their UI accordingly.

Question 21: How can you test your Room database in a Unit test or an
Instrumented test?
To test Room database in a Unit test or an Instrumented test, follow these steps:
1. Use In-Memory database: Create an in-memory version of the database for testing
purposes, as it’s faster and doesn’t persist data between tests.​
2. Test setup: Initialize the database and DAOs before each test using @Before
annotation, and close them after each test using @After annotation.​
3. Unit tests: For unit tests, use Robolectric to run tests on JVM without needing an
emulator or device. Add required dependencies and configure your test class with
@RunWith(RobolectricTestRunner.class) and @Config(sdk =
Build.VERSION_CODES.P).​
4. Instrumented tests: For instrumented tests, use AndroidJUnit4 runner. Configure your
test class with @RunWith(AndroidJUnit4.class), and add necessary dependencies.​
5. LiveData testing: To test LiveData objects, use InstantTaskExecutorRule JUnit rule to
execute tasks synchronously. Add this rule at the beginning of your test class: @Rule
public InstantTaskExecutorRule instantTaskExecutorRule = new
InstantTaskExecutorRule();​
6. Test cases: Write test cases for CRUD operations (Create, Read, Update, Delete)
and any custom queries defined in your DAOs. Use Assert methods to verify expected
results.

Question 22: Can you explain how Paging Library works with Room?
Paging Library, part of Android Jetpack, efficiently loads and displays large data sets in
chunks. It works seamlessly with Room by integrating LiveData and RxJava
components.
To use Paging Library with Room:
1. Define a DataSource.Factory: In your DAO, create a method returning
DataSource.Factory for the desired query.​
2. Configure PagedList.Builder: Set up PagedList configuration, specifying page size,
prefetch distance, and initial load size.​
3. Create LivePagedListBuilder: Combine DataSource.Factory and PagedList.Config to
build a LivePagedListBuilder.​
4. Observe PagedList: Retrieve LiveData from LivePagedListBuilder and observe it in
your ViewModel or Activity/Fragment.​
5. Implement PagedListAdapter: Extend RecyclerView.Adapter with PagedListAdapter,
handling placeholders and item binding.​
6. Submit data to adapter: When observing LiveData, submit new PagedList to
PagedListAdapter using submitList().
Room handles database queries on background threads, ensuring smooth UI
performance.

Question 23: How do you implement encryption in Android Room?


To implement encryption in Android Room, follow these steps:
1. Add dependencies: Include SQLCipher and SafeRoom libraries in your app’s
build.gradle file.
implementation 'net.zetetic:android-database-sqlcipher:4.x.x'
implementation 'com.commonsware.cwac:saferoom:1.x.x'
2. Create a custom implementation of SupportSQLiteOpenHelper.Factory to use
SafeHelperFactory for encrypted databases:
public class EncryptedOpenHelperFactory implements
SupportSQLiteOpenHelper.Factory {
private final char[] passphrase;
public EncryptedOpenHelperFactory(char[] passphrase) {
this.passphrase = passphrase;
}
@Override
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration
configuration) {
return SafeHelperFactory.fromUser(passphrase).create(configuration.context,
configuration.name, configuration.callback);
}
}
3. Modify the Room database builder to use the custom factory:
Room.databaseBuilder(context, MyDatabase.class, "my_database")
.openHelperFactory(new EncryptedOpenHelperFactory(passphrase))
.build();
4. Securely manage the passphrase: Store it securely using Android Keystore or ask
users to provide it when needed.

Question 24: How do you perform database transactions with Android Room, and
how does it handle rollback scenarios?
To perform database transactions with Android Room, use the @Transaction annotation
on a method within your DAO (Data Access Object). This ensures that all operations
within the method are executed atomically. If any operation fails, Room automatically
rolls back the transaction to maintain data integrity.
For rollback scenarios, Room handles them by default when using the @Transaction
annotation. If an exception occurs during the execution of the annotated method, Room
will catch it and roll back the transaction, preventing partial updates or inconsistent data
states.
In cases where you need more control over the transaction process, you can use the
SupportSQLiteDatabase.beginTransaction() and endTransaction() methods. To
manually handle rollback, call the setTransactionSuccessful() method before
endTransaction() if everything is successful, otherwise, don’t call it, and Room will roll
back the changes.
Example:
@Dao
public interface MyDao {
@Transaction
public void insertAndUpdate(MyEntity1 entity1, MyEntity2 entity2) {
// Perform multiple operations here
insert(entity1);
update(entity2);
}
}

Question 25: How do you execute a raw SQL query with Room, and what would
be the advantages and challenges of doing so?
To execute a raw SQL query with Room, use the @RawQuery annotation in your DAO
interface. Create a method that returns LiveData or List of desired objects and pass a
SupportSQLiteQuery as a parameter.
Advantages:​
1. Flexibility: Raw queries allow complex operations not supported by default.​
2. Performance: Customized queries can optimize performance for specific cases.
Challenges:​
1. Safety: Raw queries are prone to SQL injection attacks if not sanitized properly.​
2. Maintainability: Harder to refactor and maintain compared to standard Room queries.​
3. Type-safety: No compile-time checks; errors may occur at runtime.
Example:
@Dao​
public interface UserDao {​
@RawQuery(observedEntities = User.class)​
LiveData> getUsersWithCustomQuery(SupportSQLiteQuery query);​
}

Question 26: What is the FTS (Full-Text Search) support in Android Room, and
how can you implement it?
FTS (Full-Text Search) support in Android Room enables efficient text-based search
functionality within SQLite databases. It uses virtual tables and tokenizes data for faster
querying.
To implement FTS in Room:
1. Add dependencies: Include ‘androidx.room:room-compiler’ and
‘androidx.sqlite:sqlite-framework’ in build.gradle.​
2. Create an FTS entity: Annotate a class with @Entity(tableName =
“your_table_name”, indices = {@Index(value = {“column_name”}, unique = true)},
inheritSuperIndices = true, ftsOptions = @FtsOptions(languageId = “lid”)) to define the
FTS table schema.​
3. Define DAO: Create an interface annotated with @Dao, containing methods for
inserting, updating, deleting, and querying the FTS table.​
4. Implement database: Extend RoomDatabase, annotate with @Database(entities =
{YourFtsEntity.class}, version = 1), and declare abstract methods for accessing your
DAOs.​
5. Initialize database: Use Room.databaseBuilder(context, YourDatabaseClass.class,
“database_name”).build() to create an instance of your database.​
6. Perform operations: Access the DAO methods through the database instance to
interact with the FTS table.
Question 27: How can you handle large datasets and improve query performance
in Android Room?
To handle large datasets and improve query performance in Android Room, consider
the following strategies:
1. Pagination: Use Paging Library to load data in chunks, reducing memory usage and
improving responsiveness.​
2. Indexing: Create indices on frequently queried columns to speed up searches.​
3. Compile-time Query Verification: Utilize @Query annotations for SQL queries,
enabling compile-time checks and preventing runtime errors.​
4. Relationship Handling: Opt for explicit JOIN statements instead of embedded or
relation fields to avoid unnecessary data loading.​
5. Asynchronous Operations: Execute database operations off the main thread using
Kotlin coroutines or RxJava to prevent UI blocking.​
6. Efficient Data Structures: Choose appropriate data structures like LiveData or Flow to
observe changes and update UI efficiently.

Question 28: What would be the best strategy to create a synchronized


offline-first app using Android Room with a Firebase Realtime Database?
To create a synchronized offline-first app using Android Room and Firebase Realtime
Database, follow these steps:
1. Set up Room: Implement the Room database with DAOs, entities, and a repository
for local data storage.
2. Configure Firebase: Integrate Firebase SDK into your project and set up the Realtime
Database.
3. Data synchronization: Create listeners in the repository to observe changes in the
Firebase Realtime Database and update the local Room database accordingly.
4. Offline support: Enable Firebase’s persistence feature to cache server data locally,
ensuring smooth operation when offline.
5. Conflict resolution: Handle conflicts between local and remote data by implementing
a timestamp-based or versioning system to determine which data is more recent.
6. UI updates: Use LiveData or Flow to observe changes in the Room database and
automatically update the UI.

Question 29: How can you use Dependency Injection frameworks like Dagger2 or
Hilt with Android Room?
To use Dependency Injection frameworks like Dagger2 or Hilt with Android Room, follow
these steps:
1. Add required dependencies for Dagger2/Hilt and Room in the build.gradle file.​
2. Create a Database class extending RoomDatabase and annotate it with @Database.​
3. Define DAO interfaces for data access operations.​
4. In your AppModule (for Dagger2) or Application class (for Hilt), create a method
annotated with @Provides/@Singleton (Dagger2) or
@InstallIn(ApplicationComponent::class)/@Singleton (Hilt) to provide an instance of the
database.​
5. Inject the database instance into repositories or view models using constructor
injection by annotating the constructor with @Inject.​
6. Use the injected database instance to access DAOs and perform data operations.

Question 30: Can you explain the concept of Multi-Module architecture and how
Android Room fits into modularization?
Multi-Module architecture is a design pattern that divides an application into multiple,
smaller modules to improve scalability, maintainability, and reusability. Each module
focuses on a specific functionality or feature, allowing for independent development and
testing.
Android Room fits into modularization by providing a robust database layer within a
module. It simplifies data persistence with minimal boilerplate code, enabling
developers to create separate modules for different app components while maintaining
a consistent data storage solution across all modules. This approach promotes clean
architecture principles, separating concerns and reducing dependencies between
modules.
Question 31: How do you cache data with Android Room and what kind of
caching strategies can you implement?
To cache data with Android Room, create a local database using Room persistence
library. Define entities representing tables, Data Access Objects (DAOs) for queries, and
a RoomDatabase instance to access the database.
Caching strategies:​
1. Offline-first: Prioritize cached data; fetch from network only if unavailable or outdated.​
2. Online-first: Fetch from network first; use cache as fallback.​
3. Cache-then-refresh: Display cached data while fetching updated data from network.​
4. Two-layer cache: Combine in-memory cache (e.g., LRU) with Room for faster access
and persistence.​
5. Time-based expiration: Invalidate cache after a specific duration.​
6. Version-based invalidation: Invalidate cache when app version changes or
server-side schema updates.​
7. Smart-sync: Sync cached data with remote source based on user actions or
connectivity status.​
An embedded entity in Room allows for flattening of object hierarchy by including fields
from another class directly into the main table, while a relationship represents
associations between tables using foreign keys. Embedded entities are useful when
there’s a one-to-one mapping and no need to query related data separately.
Relationships, on the other hand, cater to one-to-many or many-to-many mappings and
enable efficient querying of related data.
For example, consider an Address class with fields street, city, and zipCode. Using
@Embedded, these fields can be included in a User table without creating a separate
Address table:
@Entity​
data class User(​
@PrimaryKey val id: Int,​
val name: String,​
@Embedded val address: Address​
)
In contrast, relationships require defining separate tables and establishing connections
via foreign keys. For instance, if a user has multiple addresses, we’d create a separate
Address table and use @Relation annotation to establish a connection:
@Entity​
data class User(@PrimaryKey val id: Int, val name: String)
@Entity(foreignKeys = [ForeignKey(entity = User::class, parentColumns = [“id”],
childColumns = [“userId”])])​
data class Address(@PrimaryKey val id: Int, val userId: Int, val street: String, val city:
String, val zipCode: String)

Question 32: How would you perform concurrency control in Android Room to
avoid conflicts between read and write operations?
To perform concurrency control in Android Room, use transactions and LiveData.
Transactions ensure atomicity, consistency, isolation, and durability (ACID) properties
while LiveData provides real-time updates.
1. Use @Transaction annotation for complex read/write operations to guarantee ACID
properties.​
2. Utilize coroutines or RxJava for asynchronous execution of database operations.​
3. Implement conflict resolution strategies using @Insert(onConflict =
OnConflictStrategy.REPLACE) or similar annotations.​
4. Employ LiveData or Flow to observe changes in the data and automatically update UI
components.​
5. For multi-threading, use Executors or Dispatchers to manage background threads.​
6. Optimize queries by indexing columns and limiting fetched data with pagination
techniques.

You might also like