Android Room using Kotlin, Coroutines, ViewModel, LiveData

Room is a part of the Android Architecture components which provides an abstraction layer over SQlite which allows for a more robust database acces while still providing the full power of SQlite. You can read about Room in details here.

Room 2.1 (SQLite Persistence Library) added the support for coroutines. Now you can add the suspend keyword to DAO class methods and ensures that they are not executed in the MainThread.

Coroutines are light-weight threads. They are launched with launch coroutine builder in a context of some CoroutineScope.

LiveData is an observable data holder, part of the Android Jetpack. LiveData considers an observer, which is represented by the Observer class, to be in an active state if its lifecycle is in the STARTED or RESUMED state. LiveData only notifies active observers about updates.

Dependencies

First, we need to add the needed dependencies that will enable us to use the Room library. We can do so with some simple lines in the build.gradle (Module:app) file. Last version of Room here. Last version of Lifecycle here.

apply plugin: 'kotlin-kapt'
...
dependencies {
    def lifecycle_version = "2.1.0"
    def room_version = "2.2.0-rc01"
    def kotlin_version = "1.3.2"

    // Kotlin coroutine dependencies
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_version"

    // Room architecture component dependencies
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"

    //Live data and life cycles
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
}

Room

The Room library consists of 3 major components: Entity, DAO (Database Access Object), Database.

The Entity represents a table within the database and has to be annotated with @Enity. Each Entity consist of a minimum of one field has to define a primary key.

@Entity(tableName = "movie_items")
data class MovieEntity(
    @ColumnInfo(name = "title") var title: String,
    @ColumnInfo(name = "year") var year: Int
) {
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0
}

In Room you use DAO to access and manage your data. The DAO is the main component of Room and includes methodes that offer access to your apps database it has to be annotated with @Dao. DAOs are used instead of query builders and let you seperate differend components of your database e.g. current data and statistics, which allows you to easily test your database.

@Dao
interface MovieDao {
    @Query("SELECT * FROM movie_items ORDER BY title ASC")
    fun getAll(): LiveData<List<MovieEntity>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(vararg movies: MovieEntity)

    @Delete
    suspend fun delete(movie: MovieEntity)

    @Update
    suspend fun update(vararg movies: MovieEntity)
}

Serves as the database holder an is the main accespoint to your relational data. It has to be annotated with @Database and extents the RoomDatabase. It also containes and returns the Dao (Database Access Object).

@Database(entities = [MovieEntity::class], version = 1)
abstract class MovieDatabase: RoomDatabase() {
    abstract fun movieDao(): MovieDao

    companion object {
        @Volatile
        private var INSTANCE: MovieDatabase? = null

        fun getInstance(context: Context): MovieDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }

            synchronized(MovieDatabase::class) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    MovieDatabase::class.java,
                    "movie_database"
                )
                .build()

                INSTANCE = instance
                return instance
            }
        }
    }
}

Repository

This is a class where we will check whether to fetch data from API or local database, or you can say we are putting the logic of database fetching in this class.

class MovieRepository(private val movieDao: MovieDao) {
    val allMovies: LiveData<List<MovieEntity>> = movieDao.getAll()

    suspend fun insert(movie: MovieEntity) {
        movieDao.insert(movie)
    }
}

ViewModel

This is the part of lifecycle library; this will help you to provide data between repository and UI. This survives the data on configuration changes and gets the existing ViewModel to reconnect with the new instance of the owner.

class MovieViewModel(application: Application): AndroidViewModel(application) {
    private val repository: MovieRepository
    val allMovies: LiveData<List<MovieEntity>>

    init {
        val movieDao = MovieDatabase.getInstance(application).movieDao()
        repository = MovieRepository(movieDao)
        allMovies = repository.allMovies
    }

    fun insert(movie: MovieEntity) = viewModelScope.launch {
        repository.insert(movie)
    }
}

Activity

class RoomActivity : AppCompatActivity() {
    private lateinit var vm: MovieViewModel
    private val TAG = "TAG"

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

        vm = ViewModelProviders.of(this).get(MovieViewModel::class.java)

        vm.allMovies.observe(this, Observer { items ->
            if (items.isEmpty()) {
                vm.insert(MovieEntity("Movie 1", 2001))
            }

            Log.d(TAG, "ITEMS: $items")
        })
    }
}

SQLite debug tools

  • Stetho is a debug bridge for Android that allows you to connect to a device and to inspect your app using Chrome Developer Tools.
  • SQLScout is a plugin for Android Studio and Intellij IDEA which allows you to connect to SQLite databases, browse tables and schemes.
  • Liya
  • SQLPro for SQLite