Room TypeConverter with constructor

Viewed 3152

I have Room TypeConverter and I need to inject parameter to it's constructor

class RoomConverters(moshi Moshi) {

    @TypeConverter
    fun fromUserActionLog(data: UserActionLogModel): String {
        return moshi.adapter(UserActionLogModel::class.java).toJson(data)
    }

    @TypeConverter
    fun toUserActionLog(json: String): UserActionLogModel {
        return moshi.adapter(UserActionLogModel::class.java).fromJson(json)}
    }
}

But when I can not annotate TypeConverter to database object with contructor;

@Database(entities = [SsidModel::class], version = 1, exportSchema = false)
@TypeConverters(RoomConverters::class)   
abstract class AppDatabase : RoomDatabase() {

    abstract fun ssidDao(): SsidDao
}

Is there any way to achieve this?

5 Answers

You can create Room TypeConverter with constructor parameters from version 2.3.0-alpha03

Release notes:

Room now has APIs for providing instances of type converters such that the app can control their initialization. To mark a type converter that will be provided to Room use the new annotation @ProvidedTypeConverter

https://developer.android.com/jetpack/androidx/releases/room#2.3.0-alpha03

In your case you should add @ProvidedTypeConverter to RoomConverter

@ProvidedTypeConverter    
class RoomConverters(moshi: Moshi)

Create converter at db creation time and pass it to database builder:

val roomConverter = RoomConverters(Moshi())
val db = Room.databaseBuilder()
         .addTypeConverter(roomConverter)
         .build()

Also you can use DI framework e.g. Dagger2

Now that 2.3.0 of Room library is released. It is possible to instantiate Room type converters and provide them to database builder.

Add @ProvidedTypeConverter annotation to the TypeConverter class.

@ProvidedTypeConverter
class RoomConverter(moshi Moshi) {

    @TypeConverter
    fun fromUserActionLog(data: UserActionLogModel): String {
        return moshi.adapter(UserActionLogModel::class.java).toJson(data)
    }

    @TypeConverter
    fun toUserActionLog(json: String): UserActionLogModel {
        return moshi.adapter(UserActionLogModel::class.java).fromJson(json)}
    }
}

Mention the TypeConverter in the @TypeConverter annotation of the database abstract class.

@Database(entities = [/* entities here */], version = 1, exportSchema = false)
@TypeConverters(RoomConverters::class)   
abstract class AppDatabase : RoomDatabase() {
    ......
    // your code here
    ......
}

Now build the database using the static method databaseBuilder of Room class and provide TypeConverter using the method addTypeConverter()

val roomConverter = RoomConverter(moshi)
val appDb = Room.databaseBuilder(
                            applicationContext,
                            AppDatabase::class.java, DB_NAME
                        )
                            .addTypeConverter(roomConverter)
                            .build()

I use dagger-android, and faced same problem. Solution is when creating AppDatabase

@Provides @Reusable
fun provideDatabase(context: Context, moshi: Moshi): AppDatabase = 
    Room.databaseBuilder(...).build().apply { AppDatabase.moshi = moshi }

AppDatabase is simple RoomDatabase:

@Database(
    entities = [OrderEntity::class],
    version = 1,
    exportSchema = false
)
@TypeConverters(DbConverters::class)
abstract class AppDatabase : RoomDatabase() {
    companion object {
        lateinit var moshi: Moshi
    }

    abstract fun orderDao(): OrderDao
}

Then use this companion object in converter:

class DbConverters {

    @TypeConverter
    fun orderInfoToString(orderInfo: OrderInfo?): String? = 
           AppDatabase.moshi.adapter(OrderInfo::class.java).toJson(orderInfo)

    @TypeConverter
    fun stringToOrderInfo(value: String): OrderInfo? = 
          AppDatabase.moshi.adapter(OrderInfo::class.java).fromJson(value)
}

This is looking ugly, I guess, but works. Maybe using static/companion object with @Reuseable scope is a bad idea. Moshi, though, is provided using @Singleton scope in AppModule, so basically live through entire application life

I checked documentation even for the newest rc version (2.2.0-rc01) and there is no way to do that. I think it's a joke that we can't register custom TypeConverters in DatabaseBuilder, so I created bug for that on Google issue tracker.

Related