问题
I'm trying to get LiveData from Room. So my RecycleView can have Live updates if anything in database is changed
I have tried with out LiveData and that works, but when i add LiveData that always shows this error.
error: Not sure how to convert a Cursor to this method's return type (androidx.lifecycle.LiveData<java.util.List<com.example.models.Club>>).
public abstract java.lang.Object getAll(@org.jetbrains.annotations.NotNull()
I googled and looked on this site for solution, but every on with this issue have used rxjava, rxandroid, rxkotlin or ArrayList. And for them solution is to replace ArrayList with List, and for RX to try coroutine. Well i am using Coroutine and List and still no progress.
This is my ClubDao
ClubDao
@Query("SELECT * FROM club")
suspend fun getAll(): LiveData<List<Club>>
In Club i have this attributes
Club
@Entity
data class Club(@PrimaryKey var id: Int,
@ColumnInfo(name = "logo_url") var logoUrl: String,
@ColumnInfo(name = "name") var name: String,
@ColumnInfo(name = "town") var town: String,
@ColumnInfo(name = "address") var address: String,
@ColumnInfo(name = "contact_name") var contactName: String,
@ColumnInfo(name = "phone_numbers") var phoneNumbers: String,
@ColumnInfo(name = "email") var email: String)
phoneNumbers should be List, but i convert those to and from json with TypeConverters
TypeConverter is here
TypeConverter
class ConvertersDB {
@TypeConverter
fun fromString(value: String): ArrayList<String> {
val listType = object : TypeToken<ArrayList<String>>() {
}.type
return Gson().fromJson(value, listType)
}
@TypeConverter
fun fromArrayList(list: ArrayList<String>): String {
val gson = Gson()
return gson.toJson(list)
}
}
And my DB
DataBase
@Database(entities = [Club::class], version = 1, exportSchema = false)
@TypeConverters(ConvertersDB::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun clubDao(): ClubDao
companion object {
@Volatile
private var instance: AppDatabase? = null
private val LOCK = Any()
operator fun invoke(context: Context) = instance ?: synchronized(LOCK) {
instance ?: buildDatabase(context).also { instance = it }
}
private fun buildDatabase(context: Context) = Room.databaseBuilder(context,
AppDatabase::class.java, "pss.db")
.allowMainThreadQueries()
.build()
}
}
In my fragment i need to observe all the clubs from database via ViewModel and Repository and send them to RecycleView
Now i am getting error:
error: Not sure how to convert a Cursor to this method's return type (androidx.lifecycle.LiveData<java.util.List<com.example.models.Club>>).
public abstract java.lang.Object getAll(@org.jetbrains.annotations.NotNull()
Does anyone know solution for this?
EDIT:
Gradle
apply plugin: 'com.android.application'
apply plugin: 'androidx.navigation.safeargs'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.overswayit.plesnisavezsrbije"
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dataBinding {
enabled true
}
packagingOptions {
exclude 'META-INF/atomicfu.kotlin_module'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31"
implementation 'com.jakewharton:butterknife:10.1.0'
implementation 'com.google.android.material:material:1.1.0-alpha07'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
// Room components
implementation "androidx.room:room-runtime:$rootProject.roomVersion"
implementation "androidx.room:room-ktx:$rootProject.roomVersion"
kapt "androidx.room:room-compiler:$rootProject.roomVersion"
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
// Room and RxJava
implementation "androidx.room:room-rxjava2:$rootProject.roomVersion"
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.archLifecycleVersion"
kapt "androidx.lifecycle:lifecycle-compiler:$rootProject.archLifecycleVersion"
androidTestImplementation "androidx.arch.core:core-testing:$rootProject.androidxArchVersion"
// ViewModel Kotlin support
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.archLifecycleVersion"
//LiveData Kotlin
implementation "androidx.lifecycle:lifecycle-livedata:$rootProject.archLifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.archLifecycleVersion"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$rootProject.archLifecycleVersion"
// Coroutines
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines"
//RxJava
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "io.reactivex.rxjava2:rxjava:2.2.6"
implementation 'com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha2'
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.kaopiz:kprogresshud:1.0.5'
implementation 'com.squareup:otto:1.3.8'
implementation 'agency.tango.android:avatar-view:0.0.2'
implementation 'agency.tango.android:avatar-view-picasso:0.0.2'
implementation 'com.mikhaellopez:circularimageview:3.2.0'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.1.0'
// Data Binding
kapt "com.android.databinding:compiler:3.1.4"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation project(path: ':tuple')
// Navigation Component
implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.0.0'
//Gson
implementation 'com.google.code.gson:gson:2.8.5'
}
kapt {
generateStubs = true
}
Top Level Gradle
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31"
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
roomVersion = '2.1.0'
archLifecycleVersion = '2.2.0-alpha01'
androidxArchVersion = '2.0.0'
coroutines = '1.2.0'
}
回答1:
As mentioned in the comments, remove suspend
.
When a method returns an observable, there is no reason to make it suspend since it just returns and object, does not run any query until it is observed.
@Query("SELECT * FROM club")
fun getAll(): LiveData<List<Club>>
Room's coroutines integration brings ability to return suspend values but when the value itself is asnyc, there is no reason to use it.
回答2:
I found a good solution to the problem with using rxJava with room and livedata. The approach is similar to using Retrofit with RxJava, using LiveDataReactiveStreams, which by default uses flowable.
In your approach i would recomend is:
@Query("SELECT * FROM club")
suspend fun getAll(): LiveData<List<Club>>
changed to
DAO:
@Query("SELECT * FROM club")
fun getAll(): (Any rxJava object) Flowable<List<Club>>
Repository:
fun getAllClubs() : LiveData<List<Club>>{
return LiveDataReactiveStreams.fromPublisher(dao.getAll()"do any transformation or
other functionality you want and then if using Maybe for example transform to
flowable by .toFlowable()")
}
ViewModel:
val clubList : MediatorLiveData<List<Club>>()
fun setClubData(){
clubList.addSource(repository.getAllClubs(), {
clubList.value = it
})
}
then you can observe the livedata object as usual in your view.
Hope what i wrote is understandable, if not i will try to make it better :)
来源:https://stackoverflow.com/questions/56833407/how-to-get-livedata-from-room