Android Room错误:无法识别枚举列表的TypeConverter

时间:2017-10-17 08:15:38

标签: android kotlin android-room android-architecture-components

Room库无法识别我为TypeConverter个枚举创建的List。但是,当我将其更改为ArrayList枚举时,它可以正常工作。任何人都知道为什么以及如何使用List来完成这项工作? (在Kotlin中使用List更容易,我真的不想仅仅因为这个而转发回ArrayList。)

这是我的代码:

我的模特:

@Entity
data class Example(@PrimaryKey val id: String?,
                   val name: String,
                   var days: List<DayOfWeek>?)

DayOfWeek是一个枚举:

enum class DayOfWeek {

    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY;

    val value: Int
        get() = ordinal + 1


    companion object {

        private val ENUMS = DayOfWeek.values()

        fun of(dayOfWeek: Int): DayOfWeek {
            if (dayOfWeek < 1 || dayOfWeek > 7) {
                throw RuntimeException("Invalid value for DayOfWeek: " + dayOfWeek)
            }

            return ENUMS[dayOfWeek - 1]
        }

    }

}

我的TypeConverter

private const val SEPARATOR = ","

class DayOfWeekConverter {

    @TypeConverter
    fun daysOfWeekToString(daysOfWeek: List<DayOfWeek>?): String? {
        return daysOfWeek?.map { it.value }?.joinToString(separator = SEPARATOR)
    }

    @TypeConverter
    fun stringToDaysOfWeek(daysOfWeek: String?): List<DayOfWeek>? {
        return daysOfWeek?.split(SEPARATOR)?.map { DayOfWeek.of(it.toInt()) }
    }

}

我在我的DB类中设置它:

@Database(entities = arrayOf(Example::class), version = 1)
@TypeConverters(DayOfWeekConverter::class)
abstract class AppDatabase : RoomDatabase() {

    abstract fun exampleDao(): ExampleDao

}

我的DAO看起来像这样:

@Dao
interface ExampleDao {

    @Query("SELECT * FROM example")
    fun getAll(): LiveData<List<Example>>

    @Insert(onConflict = REPLACE)
    fun save(examples: List<Example>)

}

我使用此代码获得的错误是:

error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
e: 

e:     private java.util.List<? extends com.example.DayOfWeek> days;

就像我上面所说,如果我将days属性更改为ArrayList<DayOfWeek>(并在ArrayList中对DayOfWeekConverter进行更改),那么一切正常。如果有人能帮助我解决这个问题并告诉我如何在List使用<?xml version="1.0" encoding="utf-8"?> <CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" style="?android:attr/spinnerDropDownItemStyle" android:singleLine="true" android:layout_width="match_parent" android:layout_height="?android:attr/dropdownListPreferredItemHeight" android:ellipsize="marquee" android:background="MY REQUIRED COLOR"/> 它会给你带来很大的帮助,它会让我发疯:/。

4 个答案:

答案 0 :(得分:12)

出于某种原因,Room不喜欢Kotlin List,但当我用List取代MutableList时,它开始起作用了:

@Entity
data class Example(@PrimaryKey val id: String,
                   val name: String,
                   var days: MutableList<DayOfWeek>?)

class DayOfWeekConverter {
    companion object {

        @TypeConverter
        @JvmStatic
        fun daysOfWeekToString(daysOfWeek: MutableList<DayOfWeek>?): String? =
                daysOfWeek?.map { it.value }?.joinToString(separator = SEPARATOR)

        @TypeConverter
        @JvmStatic
        fun stringToDaysOfWeek(daysOfWeek: String?): MutableList<DayOfWeek>? =
                daysOfWeek?.split(SEPARATOR)?.map { DayOfWeek.of(it.toInt()) }?.toMutableList()
    }
}

这不是完美的解决方案,但希望你能用此进行更多调查。

此外,您需要将@PrimaryKey更改为不可为空的

答案 1 :(得分:2)

在Kotlin中List的完整签名是List<out E>(在Java中是List<? extend E>),转换这种通用类型没有任何意义。换句话说,Room不知道输入是DayOfWeek还是它的子类。

对于ArrayListMutableList,它们的完整签名分别为ArrayList<E>MutableList<E>,输入类型是固定的,因此Room知道如何进行转换。

答案 2 :(得分:-1)

我们无法存储和获取没有数组列表的List枚举。房间不支持它。但是如果你想避免使用数组列表,你可以创建一个ListDayOfWeek对象,List是一个属性。我试过了,没关系。如果您需要代码,请在此处回复。我会发布它。

答案 3 :(得分:-1)

您不应该将其存储到数据库中。更好地构建类似的东西并将其存储为int:

enum class DaysOfWeek(var bitValue: Int) {
    Monday(64),
    Tuesday(32),
    Wednesday(16),
    Thursday(8),
    Friday(4),
    Saturday(2),
    Sunday(1);
}

最后,您的实用程序可以从/转换为枚举。

fun daysToBitValue(days: EnumSet<DaysOfWeek>): Int {
        var daysBitValue = 0
        for (`val` in days) daysBitValue += `val`.bitValue
        return daysBitValue
}


private fun fromBitValues(origBitMask: Int): EnumSet<DaysOfWeek> {

        val ret_val = EnumSet.noneOf(DaysOfWeek::class.java)

        var bitMask = origBitMask

        for (`val` in DaysOfWeek.values()) {
            if (`val`.bitValue and bitMask == `val`.bitValue) {
                bitMask = bitMask and `val`.bitValue.inv()
                ret_val.add(`val`)
            }
        }

        if (bitMask != 0) {
            throw IllegalArgumentException(String.format(Locale.getDefault(), "Bit mask value 0x%X(%d) has unsupported bits 0x%X.  Extracted values: %s", origBitMask, origBitMask, bitMask, ret_val))
        }

        return ret_val
  }

现在你可以存储一个int并在以后的工作日获得:

@Entity
data class Example(@PrimaryKey val id: String?,
                   val name: String,
                   var days: Int = 0) 

或者你使用enum并为此编写一个合适的typeconverter。

@Entity
data class Example(@PrimaryKey val id: String?,
                   val name: String,
                   var days: EnumSet<DaysOfWeek>?) 


class DayOfWeekConverter {

    @TypeConverter
    fun daysOfWeekToString(daysOfWeek: EnumSet<DayOfWeek>?) =
        daysOfWeek?.let{ YourUtilities.daysToBitValue(it) }

    @TypeConverter
    fun intToDaysOfWeek(daysOfWeek: Int?) = 
        daysOfWeek?.let {  YourUtilities.fromBitValues(it) } 
}

您可以循环使用枚举,并使用

获取所有日期
for (aDaysOfWeekEnumSet in daysOfWeekEnumSet) 
             info{ "day = ${aDaysOfWeekEnumSet.name"} 

代码未经测试,因为我不在我的计算机上,但这应该显示更好的概念,然后存储枚举(这只是预定义值的别名!)