Search code examples
androidandroid-studiokotlinandroid-room

RoomDataBase Empty in device file explorer


When I look at the databases file in device file explorer the folder is empty. I have tried to fix the problem myself but I haven't been able to since I'm quite new to android development. This is all the code that has been written so far:

Main Activity

package com.example.todoit

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todoit.data.Todo
import com.example.todoit.data.TodoViewModel
import kotlinx.android.synthetic.main.activity_main.*


class MainActivity : AppCompatActivity() {
    private lateinit var todoAdapter: TodoAdapter
    private lateinit var todoViewModel: TodoViewModel

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

        todoAdapter = TodoAdapter(mutableListOf())
        rvTodoItems.layoutManager = LinearLayoutManager(this)
        rvTodoItems.adapter = todoAdapter

        btnAddTodo.setOnClickListener {
            val todoTitle = etTodoTitle.text.toString()
            if (todoTitle.isNotEmpty()) {
                val todo = Todo(0,todoTitle,false)
                etTodoTitle.text.clear()
                insertDataToDataBase(todo)
                todoAdapter.addTodo(todo)
        }
        btnDeleteTodo.setOnClickListener {
            todoAdapter.deleteDoneTodos()
        }
    }}

    private fun insertDataToDataBase(todo: Todo) {
        val todoTitle = etTodoTitle.text.toString()

        if(todoTitle.isNotEmpty()) {
            //Add data to database
            todoViewModel.addTodoToDataBase(todo)
        }
    }
}

TodoAdapter

package com.example.todoit

import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.todoit.data.Todo
import kotlinx.android.synthetic.main.item_todo.view.*

class TodoAdapter(
    private val todos: MutableList<Todo>,
    ) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() {

    class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
        return TodoViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.item_todo,
                parent,
                false
            )
        )
    }

    fun addTodo(todo: Todo) {
        todos.add(todo)
        notifyItemInserted(todos.size - 1)
    }

    fun deleteDoneTodos() {
        todos.removeAll { todo ->
            todo.isChecked
        }
        notifyDataSetChanged()
    }

    private fun toggleStrikeThrough(tvTodoTitle: TextView, isChecked: Boolean) {
        if (isChecked) {
            tvTodoTitle.paintFlags = tvTodoTitle.paintFlags or STRIKE_THRU_TEXT_FLAG
        } else {
            tvTodoTitle.paintFlags = tvTodoTitle.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
        }
    }

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val curTodo = todos[position]
        holder.itemView.apply {
            tvTodoTitle.text = curTodo.title
            cbDone.isChecked = curTodo.isChecked
            toggleStrikeThrough(tvTodoTitle, curTodo.isChecked)
            cbDone.setOnCheckedChangeListener { _, isChecked ->
                toggleStrikeThrough(tvTodoTitle, isChecked)
                curTodo.isChecked = !curTodo.isChecked
            }
        }
    }

    override fun getItemCount(): Int {
        return todos.size
    }
}

TodoViewModel

package com.example.todoit.data

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch




class TodoViewModel(application: Application) : AndroidViewModel(application) {

    private val readAllData: LiveData<List<Todo>>
    private val repository: TodoRepository




    init {
        val todoDao = TodoDataBase.getDataBase(application).todoDao()
        repository = TodoRepository(todoDao)
        readAllData = repository.readAllData
    }


    fun addTodoToDataBase(todo: Todo) {
        viewModelScope.launch(Dispatchers.IO) {
            repository.addTodo(todo)
        }
    }
}

TodoRepository

package com.example.todoit.data

import androidx.lifecycle.LiveData

class TodoRepository(private val todoDao:TodoDao) {
    val readAllData: LiveData<List<Todo>> = todoDao.readAllData()

    suspend fun addTodo(todo:Todo) {
        todoDao.addTodo(todo)
    }
}

TodoDataBase

package com.example.todoit.data

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [Todo::class],version = 1, exportSchema = false)
abstract class TodoDataBase: RoomDatabase() {

    abstract fun todoDao(): TodoDao

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

        fun getDataBase(context: Context):TodoDataBase{
            val tempInstance = INSTANCE
            if(tempInstance != null){
                return tempInstance
            }
            synchronized(this){
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    TodoDataBase::class.java,
                    "todo_database"
                ).build()
                INSTANCE = instance
                return instance
            }
        }


    }
}

TodoDao

package com.example.todoit.data

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query

@Dao
interface TodoDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun addTodo(todo: Todo)

    @Query("SELECT * FROM todo_data ORDER BY id ASC")
    fun readAllData(): LiveData<List<Todo>>
}

Todo

package com.example.todoit.data

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "todo_data")
data class Todo (
    @PrimaryKey(autoGenerate = true)
    var id: Int,
    val title: String,
    var isChecked: Boolean = false
)

Build.gradle(Module)

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-android-extensions'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.example.todoit"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}



dependencies {
    //ROOM
    def roomVersion = "2.4.2"
    implementation("androidx.room:room-runtime:$roomVersion")
    annotationProcessor("androidx.room:room-compiler:$roomVersion")

    // Navigation Component
    implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
    implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'


    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.6.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    // Lifecycle components
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

    // Kotlin components
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72"
    api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5"
    api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5"
}

Solution

  • function that creates an instance of room database:

    fun getDataBase(context: Context):TodoDataBase{
                val tempInstance = INSTANCE
                if(tempInstance != null){
                    return tempInstance
                }
                synchronized(this){
                    val instance = Room.databaseBuilder(
                        context.applicationContext,
                        TodoDataBase::class.java,
                        "todo_database"
                    ).build()
                    INSTANCE = instance
                    return instance
                }
            }
    

    it is called from your viewModel here:

       init {
            val todoDao = TodoDataBase.getDataBase(application).todoDao()
            repository = TodoRepository(todoDao)
            readAllData = repository.readAllData
        }
    

    so, when that viewModel is instantiated, your room db will be created (if It wasn't before), but inside your mainActivity the only reference to your viewModel is here:

     private lateinit var todoViewModel: TodoViewModel
    

    which is not an initialization, and won't trigger the "init" block, a fix and more about that you can find here (Ricardo Costeira answer seems the best)

    but if you are simply seeking the solution:

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            todoViewModel = ViewModelProvider(this).get(TodoViewModel::class.java) // <-- added
            todoAdapter = TodoAdapter(mutableListOf())
            rvTodoItems.layoutManager = LinearLayoutManager(this)
            rvTodoItems.adapter = todoAdapter
    
            btnAddTodo.setOnClickListener {
                val todoTitle = etTodoTitle.text.toString()
                if (todoTitle.isNotEmpty()) {
                    val todo = Todo(0,todoTitle,false)
                    etTodoTitle.text.clear()
                    insertDataToDataBase(todo)
                    todoAdapter.addTodo(todo)
            }
            btnDeleteTodo.setOnClickListener {
                todoAdapter.deleteDoneTodos()
            }
        }}