I'm building a to-do list app in Android Studio using Jetpack:Room as the database library. I've followed the Android Developer tutorial to model my code, however I get an error that "java.lang.RuntimeException: Cannot find implementation for com.example.todolist.database.TaskRoomDatabase. TaskRoomDatabase_Impl does not exist" when I try to call the Insert function for the first time and create the database. I'm using Room version 2.5.2, ksp for the "room-compiler" and version 1_8 for java, and 2.7.3 for navigation.
Here is the task.kt class:
@Entity(tableName = "tasks")
data class Task(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
@ColumnInfo(name = "task_name") val taskName: String,
@ColumnInfo(name = "task_description") val taskDescription: String
){}
TaskDao.kt class:
//Database Access Object to the Task entity database
@Dao
interface TaskDao {
@Query("SELECT * from tasks WHERE task_name = :name")
fun getTask(name: String): List<Task>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(task: Task) : Long
@Update
suspend fun update(task: Task)
@Delete
suspend fun delete(task: Task)
}
the TaskRoomDatabase.kt
@Database(entities = [Task::class], version = 1, exportSchema = false)
abstract class TaskRoomDatabase : RoomDatabase() {
abstract fun taskDao(): TaskDao
companion object{
@Volatile
private var INSTANCE: TaskRoomDatabase? = null
fun getDatabase(context: Context): TaskRoomDatabase {
//if INSTANCE is not null, return it
//else, create the database
return INSTANCE ?: synchronized(this){
val instance = Room.databaseBuilder(
context,
TaskRoomDatabase::class.java,
"task_database")
.createFromAsset("database/task_database")
.build()
INSTANCE = instance
instance
}
}
}
}
and the function from the fragment that is supposed to call the insert function.
private fun addNewTask() : Boolean {
if (isEntryValid()) {
viewModel.addTask(
binding.editTaskTitle.text.toString(),
binding.taskDetails.text.toString()
)
return true
}
val toast = Toast.makeText(context, "Invalid Input", Toast.LENGTH_SHORT)
toast.show()
return false
}
//then the viewModel.addtask function referenced above
fun addTask(taskName: String, taskDescription: String){
Log.d("TaskView", "passed strings: $taskName , $taskDescription ")
val newTask = getNewTask(taskName, taskDescription)
insertTask(newTask)
}
//Insert a task object to DB in non-blocking coroutine
private fun insertTask(newTask: Task) {
viewModelScope.launch {
taskDao.insert(newTask)
}
}
What am I doing wrong?
EDIT: Here is the build.gradle:
mport org.jetbrains.kotlin.storage.CacheResetOnProcessCanceled.enabled
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp") apply true
}
android {
namespace = "com.example.todolist"
compileSdk = 34
defaultConfig {
applicationId = "com.example.todolist"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
buildFeatures{
viewBinding = true
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
val room_version = "2.5.2"
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation ("androidx.room:room-ktx:$room_version")
//kapt ("androidx.room:room-compiler:$room_version")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation ("com.google.android.material:material:<version>")
implementation ("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
val nav_version = "2.7.3"
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
//Databinding to use DataBindingUtil
implementation ("androidx.databinding:databinding-runtime:4.2.0")
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
// To use Kotlin annotation processing tool (kapt)
ksp("androidx.room:room-compiler:$room_version")
// optional - Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:$room_version")
// optional - RxJava2 support for Room
implementation("androidx.room:room-rxjava2:$room_version")
// optional - RxJava3 support for Room
implementation("androidx.room:room-rxjava3:$room_version")
// optional - Guava support for Room, including Optional and ListenableFuture
implementation("androidx.room:room-guava:$room_version")
// optional - Test helpers
testImplementation("androidx.room:room-testing:$room_version")
// optional - Paging 3 Integration
implementation("androidx.room:room-paging:$room_version")
}
When calling the function to add the bound text data to a Task object and then store it in a database, the program crashes and generates the error noted above.
As per the comments, there is no issue with your code (at least the database code). So the issue is likely with ksp.
As a test, took your code, commented out .createFromAsset()
and included .allowMainThreadQueries
(for easy/simple testing).
The main thing is that the java(generated) (as per Android View) included TaskRoomDatabase_Impl, a component that kapt/ksp builds.
When run with a very basic activity just gets the db and then the dao into lateinits and then calls the getTask with an arbritrary string (no data so does not matter). This enabling App Inspection to be used which shows:-
For ksp the Project Build gradle used:-
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.2.1' apply false
id 'com.android.library' version '7.2.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
}
task clean(type: Delete) {
delete rootProject.buildDir
}
The module Build Grade used:-
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.devtools.ksp' version '1.8.0-1.0.8'
//id 'kotlin-kapt'
}
android {
compileSdk 33
defaultConfig {
applicationId "a.a.so77219401kotlinroomksp"
minSdk 26
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 {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.room:room-ktx:2.5.2'
implementation 'androidx.room:room-runtime:2.5.2'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
ksp 'androidx.room:room-compiler:2.5.2'
}
If changing from/to kapt from/to ksp I suggest that you do a Clean, then a Rebuild and the Compile.
Activity code used to test:-
class MainActivity : AppCompatActivity() {
lateinit var db: TaskRoomDatabase
lateinit var dao: TaskDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TaskRoomDatabase.getDatabase(this)
dao = db.taskDao()
val x = dao.getTask("Blah")
}
}
TaskRoomDatabase code used for test :-
@Database(entities = [Task::class], version = 1, exportSchema = false)
abstract class TaskRoomDatabase : RoomDatabase() {
abstract fun taskDao(): TaskDao
companion object{
@Volatile
private var INSTANCE: TaskRoomDatabase? = null
fun getDatabase(context: Context): TaskRoomDatabase {
//if INSTANCE is not null, return it
//else, create the database
return INSTANCE ?: synchronized(this){
val instance = Room.databaseBuilder(
context,
TaskRoomDatabase::class.java,
"task_database")
//.createFromAsset("database/task_database")
.allowMainThreadQueries()
.build()
INSTANCE = instance
instance
}
}
}
}
Android View - Explorer looks like:-
And just in case the gradle settings:-