Using Room in Jetpack Compose
Tags: android, computerscience, kotlin, programming
Published: 2021-04-28 09:21:15

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>

<!-- wp:paragraph -->
<p>We will cover briefly about</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Using Room in Jetpack Compose</li><li>Writing CRUD operations</li><li>Write Test for Database</li></ol>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: This article assumes the reader knows about Jetpack Compose</p></blockquote>
<!-- /wp:quote -->

{% youtube 4fzbxnzIJsI %}

<!-- wp:heading {"level":3} -->
<h3>Using Room in Jetpack&nbsp;Compose</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><a href="https://developer.android.com/jetpack/androidx/releases/room" rel="noreferrer noopener" target="_blank">What is Room</a>?</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>As per the documentation,</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>The Room persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/0*qunHQO8A27VTV9YI.png" alt=" Room in Jetpack"/><figcaption>&nbsp;Room in&nbsp;Jetpack</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":4} -->
<h4>Setup Room in&nbsp;Compose</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>To use Room in your app, add the following dependencies to your app’s <code>build.gradle</code> file:</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">dependencies {<br>  def room_version = "2.2.6"<br><br>  // FOR ROOM<br>  implementation "androidx.room:room-runtime:$room_version"<br>  kapt "androidx.room:room-compiler:$room_version"<br>  <br>  // optional - Kotlin Extensions and Coroutines support for Room<br>  implementation "androidx.room:room-ktx:$room_version"<br>  // END FOR ROOM<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:heading {"level":4} -->
<h4>Components of&nbsp;Room</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>There are 3 major components in Room:</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>The <a href="https://developer.android.com/reference/kotlin/androidx/room/Database" rel="noreferrer noopener" target="_blank">database class</a> that holds the database.</li><li><a href="https://developer.android.com/training/data-storage/room/defining-data" rel="noreferrer noopener" target="_blank">Data entities</a> that represent tables in your app’s database.</li><li><a href="https://developer.android.com/training/data-storage/room/accessing-data" rel="noreferrer noopener" target="_blank">Data access objects (DAOs)</a> that provide methods that your app can use to query, update, insert, and delete data in the database.</li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/0*bzP4ki1b291nO7yW.png" alt="Room Architecture"/><figcaption>Room Architecture</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Inside our code, it looks like this</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*B2HdcbkEJzqYepM19iG1Sw.png" alt="Room Structure"/><figcaption>Room Structure</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>Writing CRUD operations</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Let’s look into what each file inside <strong>database(above screenshot)</strong> will have</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li><a href="https://github.com/AseemWangoo/hands_on_kotlin/blob/master/app/src/main/java/com/aseemwangoo/handsonkotlin/database/TodoItem.kt" rel="noreferrer noopener" target="_blank"><strong>TodoItem</strong></a><strong> (Our entity class)</strong></li></ol>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>We create an entity called <strong>TodoItem</strong></p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>The class is annotated with @Entity and name of the table.</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">@Entity(tableName = "my_todo_list")
data class TodoItem(
    @PrimaryKey(autoGenerate = true)
    var itemId: Long = 0L,
    
    @ColumnInfo(name = "item_name")
    val itemName: String, 

    @ColumnInfo(name = "is_completed")
    var isDone: Boolean = false
)</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p><strong>2. </strong><a href="https://github.com/AseemWangoo/hands_on_kotlin/blob/master/app/src/main/java/com/aseemwangoo/handsonkotlin/database/TodoDatabaseDao.kt" rel="noreferrer noopener" target="_blank"><strong>TodoDatabaseDao</strong></a><strong> (Our Data Access Objects)</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We create an interface(<strong>TodoDatabaseDao</strong>)</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>The class is annotated with @ Dao</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">@Dao
interface TodoDatabaseDao {
    @Query("SELECT * from my_todo_list")
    fun getAll(): LiveData&lt;List&lt;TodoItem>>
    
    @Query("SELECT * from my_todo_list where itemId = :id")
    fun getById(id: Int) : TodoItem?
    
    @Insert
    suspend fun insert(item:TodoItem)
    
    @Update
    suspend fun update(item:TodoItem)
    
    @Delete
    suspend fun delete(item:TodoItem)
    
    @Query("DELETE FROM my_todo_list")
    suspend fun deleteAllTodos()
}</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li>Query Annotation is used for writing custom queries (like <strong>read</strong> or <strong>delete all</strong> in our case)</li><li>Annotations Insert, Update, Delete perform CUD operations.</li><li>We mark these functions as <a href="https://kotlinlang.org/docs/composing-suspending-functions.html" rel="noreferrer noopener" target="_blank">suspend</a> (so that we can call them inside <a href="https://kotlinlang.org/docs/coroutines-overview.html" rel="noreferrer noopener" target="_blank">coroutines</a>)</li></ul>
<!-- /wp:list -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:paragraph -->
<p><strong>3. </strong><a href="https://github.com/AseemWangoo/hands_on_kotlin/blob/master/app/src/main/java/com/aseemwangoo/handsonkotlin/database/TodoDatabase.kt" rel="noreferrer noopener" target="_blank"><strong>TodoDatabase</strong></a><strong> (Our database class)</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We define an abstract class(<strong>TodoDatabase</strong>) that extends <code>RoomDatabase</code></p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>This class is annotated with @ Database</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@Database(entities = &#91;TodoItem::class], version = 1)
abstract class TodoDatabase : RoomDatabase() {
    abstract fun todoDao(): TodoDatabaseDao
    companion object {
         private var INSTANCE: TodoDatabase? = null
         fun getInstance(context: Context): TodoDatabase {
            synchronized(this) {
                var instance = INSTANCE
                if (instance == null) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        TodoDatabase::class.java,
                        "todo_list_database"
                    ).fallbackToDestructiveMigration()
                     .build()
                 INSTANCE = instance
               }
             return instance
           }
       }
   }
}</code></pre>
<!-- /wp:code -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:list -->
<ul><li>A thread that enters a <strong>synchronized</strong> method obtains a lock and no other thread can enter the method until the lock is released. <strong>Kotlin</strong> offers the same functionality with the <strong>Synchronized</strong> annotation.</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p><strong>4. </strong><a href="https://github.com/AseemWangoo/hands_on_kotlin/blob/master/app/src/main/java/com/aseemwangoo/handsonkotlin/database/TodoRepository.kt" rel="noreferrer noopener" target="_blank"><strong>TodoRepository</strong></a><strong> (Our repository class)</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We create a repository class (<strong>TodoRepository</strong>) that takes in the <strong>TodoDatabaseDao</strong> as the constructor parameter</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>class TodoRepository(private val todoDatabaseDao: TodoDatabaseDao) {
    val readAllData : LiveData&lt;List&lt;TodoItem>> =  todoDatabaseDao.getAll()
    suspend fun addTodo(todoItem: TodoItem) {
        todoDatabaseDao.insert(todoItem)
    }
    suspend fun updateTodo(todoItem: TodoItem) {
        todoDatabaseDao.update(todoItem)
    }
    suspend fun deleteTodo(todoItem: TodoItem) {
        todoDatabaseDao.delete(todoItem)
    }
    suspend fun deleteAllTodos() {
        todoDatabaseDao.deleteAllTodos()
    }
}</code></pre>
<!-- /wp:code -->

<!-- wp:list -->
<ul><li>All the interactions to the database are done via this repository layer</li><li>Since, our implementations were suspend functions, so are our definitions</li><li>We expose <strong>readAllData, </strong>which is of type <a href="https://developer.android.com/topic/libraries/architecture/livedata" rel="noreferrer noopener" target="_blank">LiveData</a>, hence we can observe to the changes and notify UI</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":3} -->
<h3>Write Test for&nbsp;Database</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Since, we have our TodoDatabase ready, let’s test it</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Create a test class <a href="https://github.com/AseemWangoo/hands_on_kotlin/blob/master/app/src/androidTest/java/com/aseemwangoo/handsonkotlin/TodoDatabaseTest.kt" rel="noreferrer noopener" target="_blank">TodoDatabaseTest</a> under the <strong>androidTest</strong> folder</li><li>We annotate our class with <code>AndroidJUnit4</code> This is the thing that will drive the tests for a single class.</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@RunWith(AndroidJUnit4::class)
class TodoDatabaseTest {

    private lateinit var todoDao: TodoDatabaseDao
    private lateinit var db: TodoDatabase

    @Before
    fun createDb() {
        val context = InstrumentationRegistry.getInstrumentation().targetContext

        db = Room.inMemoryDatabaseBuilder(context, TodoDatabase::class.java)
            .allowMainThreadQueries()
            .build()

        todoDao = db.todoDao()
    }

    @After
    @Throws(IOException::class)
    fun deleteDb() {
        db.close()
    }

    @Test
    @Throws(Exception::class)
    fun insertAndGetTodo() = runBlocking {
        val todoItem = TodoItem(itemId = 1, itemName = "Dummy Item", isDone = false)
        todoDao.insert(todoItem)
        val oneItem = todoDao.getById(1)
        assertEquals(oneItem?.itemId, 1)
    }
}</code></pre>
<!-- /wp:code -->

<!-- wp:list -->
<ul><li>Before the test runs, we create the database using <strong>createDb</strong></li><li>After the test runs, we delete the database using <strong>deleteDb</strong></li><li>During the test is running, we invoke the <strong>insertAndGetTodo</strong></li><li>Since this is only a test, hence we use <a href="https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html" rel="noreferrer noopener" target="_blank"><em>runBlocking</em></a><em>.</em></li><li>Create a todo and assert it against the value created</li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*7eGRq0EgTQ-cTp_SRJxPFA.png" alt="Run the test"/><figcaption>Run the test</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Click on <strong>Run TodoDatabaseTest</strong> to run the test</li><li>If everything is fine, you should see</li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*FRw6TbqdbsaiPN-_jthTXg.png" alt="Test results for Room"/><figcaption>Test results for Room</figcaption></figure></div>
<!-- /wp:image -->

{% youtube 4fzbxnzIJsI %}

<!-- wp:paragraph -->
<p><a rel="noreferrer noopener" href="https://github.com/AseemWangoo/hands_on_kotlin" target="_blank"><em>Source code.</em></a>

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>