Bolt UIX
Bolt UIX

Reputation: 7022

How to handle query method's return type room using Kotlin, Coroutines, ViewModel, LiveData

How to handle query method's return type room using Kotlin, Coroutines, ViewModel, LiveData

the building is failed and I'm getting lots of errors that are pointing into my Dao class and the error is

Error 1:

Not sure how to handle query method's return type (java.lang.Object). DELETE query methods must either return void or int (the number of deleted rows).

Error 2:

error: Query method parameters should either be a type that can be converted into a database column or a List / Array that contains such type. You can consider adding a Type Adapter for this. kotlin.coroutines.Continuation<? super kotlin.Unit> continuation);

Error 3:

error: Unused parameter: continuation public abstract java.lang.Object clear(@org.jetbrains.annotations.NotNull()

Error 4:

error: Type of the parameter must be a class annotated with @Entity or a collection/array of it. kotlin.coroutines.Continuation<? super kotlin.Unit> continuation);

Error 5:

error: Not sure how to handle insert method's return type. public abstract java.lang.Object insert(@org.jetbrains.annotations.NotNull()

**Here is my full code: https://drive.google.com/drive/folders/1qWoud5XogzkTmpa-GWxLJStfdUPSoV7r?usp=sharing

android kotlin - Coroutines Room ViewModel LiveData MainActivity.kt

package com.example.coroutine

import android.os.Bundle
import android.text.method.ScrollingMovementMethod
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import kotlinx.android.synthetic.main.activity_main.*
import java.util.UUID
import kotlin.random.Random


class MainActivity : AppCompatActivity() {

    private lateinit var model: StudentViewModel

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

        // make text view text scrollable
        textView.movementMethod = ScrollingMovementMethod()

        // initialize the student view model
        model = ViewModelProvider(this).get(StudentViewModel::class.java)


        // observe the students live data
        model.students.observe(this, Observer { students->
                textView.text = "Students(${students.size})..."

                students.forEach {
                    textView.append("\n${it.id} | ${it.fullName} : ${it.result}")
                }
            }
        )


        btnInsert.setOnClickListener {
            // generate a new student
            val student = Student(
                null,
                UUID.randomUUID().toString(),
                Random.nextInt(100)
            )

            // insert new student into room database
            model.insert(student)
        }


        btnClear.setOnClickListener {
            // delete all students from room student table
            model.clear()
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FAE6FA"
    tools:context=".MainActivity">

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btnInsert"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:backgroundTint="#8DB600"
        android:text="Insert"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btnClear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:backgroundTint="#E52B50"
        android:text="Clear"
        app:layout_constraintBottom_toBottomOf="@+id/btnInsert"
        app:layout_constraintStart_toEndOf="@+id/btnInsert" />

    <com.google.android.material.textview.MaterialTextView
        android:id="@+id/textView"
        style="@style/TextAppearance.MaterialComponents.Subtitle1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:textColor="#1B1811"
        android:textStyle="bold"
        android:padding="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btnInsert"
        app:layout_constraintVertical_bias="1.0"
        tools:text="TextView" />

</androidx.constraintlayout.widget.ConstraintLayout>

RoomSingleton.kt

package com.example.coroutine

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

@Database(entities = [Student::class], version = 1)
abstract class RoomSingleton : RoomDatabase() {
    abstract fun studentDao():StudentDao

    companion object {
        private var INSTANCE: RoomSingleton? = null
        fun getInstance(context: Context): RoomSingleton {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(
                    context,
                    RoomSingleton::class.java,
                    "roomdb")
                    .build()
            }
            return INSTANCE as RoomSingleton
        }
    }
}

RoomDao.kt

package com.example.coroutine

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

@Dao
interface StudentDao{
    @Query("SELECT * FROM studentTbl ORDER BY id DESC")
    fun getStudents():LiveData<List<Student>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(student:Student)

    @Query("DELETE FROM studentTbl")
    suspend fun clear()
}

RoomEntity.kt

package com.example.coroutine

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

@Entity(tableName = "studentTbl")
data class Student(
    @PrimaryKey
    var id:Long?,

    @ColumnInfo(name = "uuid")
    var fullName: String,

    @ColumnInfo(name = "result")
    var result:Int
)

StudentViewModel.kt

package com.example.coroutine

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


class StudentViewModel(application:Application): AndroidViewModel(application){
    private val db:RoomSingleton = RoomSingleton.getInstance(application)

    internal val students : LiveData<List<Student>> = db.studentDao().getStudents()

    fun insert(student: Student){
        viewModelScope.launch(Dispatchers.IO) {
            db.studentDao().insert(student)
        }
    }

    fun clear(){
        viewModelScope.launch(Dispatchers.IO) {
            db.studentDao().clear()
        }
    }
}

Upvotes: 2

Views: 4658

Answers (2)

MikeT
MikeT

Reputation: 56938

Copied your code, bar the gradle files, and a slight adjustment to main activity. Compiled ok and ran Insert and Clear buttons appear to work fine.

So either you build gradle is incorrect or you need to do a clean and or rebuild of the project.

e.g. :-

enter image description here

MainActivity I used declared the Views as lateinit's and late did the findViewById's:-

class MainActivity : AppCompatActivity() {

    private lateinit var model: StudentViewModel
    lateinit var textView: TextView
    lateinit var btnClear: Button
    lateinit var btnInsert: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView = this.findViewById(R.id.textView)
        btnClear = this.findViewById(R.id.btnClear)
        btnInsert = this.findViewById(R.id.btnInsert)

        // make text view text scrollable
        textView.movementMethod = ScrollingMovementMethod()

        // initialize the student view model
        model = ViewModelProvider(this).get(StudentViewModel::class.java)


        // observe the students live data
        model.students.observe(this, Observer { students->
            textView.text = "Students(${students.size})..."

            students.forEach {
                textView.append("\n${it.id} | ${it.fullName} : ${it.result}")
            }
        }
        )


        btnInsert.setOnClickListener {
            // generate a new student
            val student = Student(
                    null,
                    UUID.randomUUID().toString(),
                    Random.nextInt(100)
            )

            // insert new student into room database
            model.insert(student)
        }


        btnClear.setOnClickListener {
            // delete all students from room student table
            model.clear()
        }
    }
}

Build Gradle (Module) used :-

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "a.a.so69998391kotlinroom_to_handle_query_return_types"
        minSdk 21
        targetSdk 31
        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.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
    implementation 'androidx.room:room-ktx:2.4.0-beta01'
    implementation 'androidx.room:room-runtime:2.4.0-beta01'
    implementation 'androidx.lifecycle:lifecycle-common:2.4.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    kapt 'androidx.lifecycle:lifecycle-compiler:2.4.0'
    kapt 'androidx.room:room-compiler:2.4.0-beta01'
}

Upvotes: 0

rost
rost

Reputation: 4077

Your problem in the keyword suspend in methods insert and delete. After remove your errors is gone.

@Insert(onConflict = OnConflictStrategy.REPLACE)
   fun insert(student:Student)

    @Query("DELETE FROM studentTbl")
   fun clear()

Btw your build still cant be successful. For fix other errors you should add 'kotlin-android-extensions' on block plugins in you app build.gradle file.

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

But this solution is deprecated and you should use viewBinding instead. Check this out. https://developer.android.com/topic/libraries/view-binding/migration

Upvotes: 5

Related Questions