迁移至Kotlin Multiplatform后Room部分DAO插入方法未解析问题
Room迁移Kotlin Multiplatform后内部任务类中DAO未解析问题
问题描述
将应用迁移至Kotlin Multiplatform,使用Room版本2.7.0-alpha05。Room相关的@Database、@Entity、@DAO注解代码转换完成且无报错,19个实体的插入操作编译正常,但有两个插入函数的内部任务类call方法中出现DAO未解析错误——这两个DAO变量在MainViewModel的其他查询、删除操作中可正常识别。
相关代码
DAO定义
@Dao interface CustomGeofencePerimeterMarkerTableDao{ @Insert fun insertCustomGeofencePerimeterMarkerTable(customGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable): Long? @Query("DELETE FROM CustomGeofencePerimeterMarkerTable WHERE customGeofencePerimeterMarkerTablePrimaryKey = :customGeofencePerimeterMarkerTablePrimaryKey") fun deleteCustomGeofencePerimeterMarkerTable(customGeofencePerimeterMarkerTablePrimaryKey: Long) @Query("DELETE FROM CustomGeofencePerimeterMarkerTable") fun deleteAllCustomGeofencePerimeterMarkerTables() @Query("SELECT * FROM CustomGeofencePerimeterMarkerTable") fun findAllCustomGeofencePerimeterMarker(): MutableList<CustomGeofencePerimeterMarkerTable?>? @Query("SELECT * FROM CustomGeofencePerimeterMarkerTable WHERE customGeofencePerimeterMarkerContactAddressTableFirebaseKey = :customGeofencePerimeterMarkerContactAddressTableFirebaseKey") fun findAllCustomGeofencePerimeterMarkerByContactAddressFirebaseKey( customGeofencePerimeterMarkerContactAddressTableFirebaseKey: String? ): CustomGeofencePerimeterMarkerTable? @Query("SELECT * FROM CustomGeofencePerimeterMarkerTable WHERE customGeofencePerimeterMarkerTableFirebaseKey = :customGeofencePerimeterMarkerTableFirebaseKey") fun findCustomGeofencePerimeterMarkerTableByFirebaseKey( customGeofencePerimeterMarkerTableFirebaseKey: String? ): CustomGeofencePerimeterMarkerTable? @Query( "UPDATE CustomGeofencePerimeterMarkerTable SET " + "customGeofencePerimeterMarkerTablePrimaryKey = :customGeofencePerimeterMarkerTablePrimaryKey, " + "customGeofencePerimeterMarkerTableFirebaseKey = :customGeofencePerimeterMarkerTableFirebaseKey, " + "customGeofencePerimeterMarkerContactAddressTableFirebaseKey = :customGeofencePerimeterMarkerContactAddressTableFirebaseKey, " + "customGeofencePerimeterMarkerDateAndTime = :customGeofencePerimeterMarkerDateAndTime, " + "customGeofencePerimeterMarkerString = :customGeofencePerimeterMarkerString " + "WHERE customGeofencePerimeterMarkerTablePrimaryKey = :customGeofencePerimeterMarkerTablePrimaryKey" ) fun updateCustomGeofencePerimeterMarkerTable( customGeofencePerimeterMarkerTablePrimaryKey: Long, customGeofencePerimeterMarkerTableFirebaseKey: String?, customGeofencePerimeterMarkerContactAddressTableFirebaseKey: String?, customGeofencePerimeterMarkerDateAndTime: String?, customGeofencePerimeterMarkerString: String? ) } @Dao interface CurrentLocationTableDao{ @Insert fun insertCurrentLocation(currentLocationTable: CurrentLocationTable): Long? @Query("DELETE FROM CurrentLocationTable WHERE currentLocationTablePrimaryKey = :currentLocationTablePrimaryKey") fun deleteCurrentLocation(currentLocationTablePrimaryKey: Long) @Query("DELETE FROM CurrentLocationTable WHERE currentLocationTableFirebaseKey = :currentLocationTableFirebaseKey") fun deleteCurrentLocationByCurrentLocationFirebaseKey(currentLocationTableFirebaseKey: String?) @Query("DELETE FROM CurrentLocationTable") fun deleteAllCurrentLocations() @get:Query("SELECT * FROM CurrentLocationTable") val allCurrentLocation: LiveData<MutableList<CurrentLocationTable?>?> @Query("SELECT * FROM CurrentLocationTable WHERE currentLocationContactEmailAddress = :currentLocationContactEmailAddress ORDER BY currentLocationSequenceKey ASC") fun findAllCurrentLocation(currentLocationContactEmailAddress: String?): MutableList<CurrentLocationTable?>? @Query("SELECT * FROM CurrentLocationTable WHERE currentLocationTableFirebaseKey = :currentLocationTableFirebaseKey") fun findCurrentLocationTableByFirebaseKey(currentLocationTableFirebaseKey: String?): CurrentLocationTable? @Query("SELECT * FROM CurrentLocationTable WHERE currentLocationTablePrimaryKey = :currentLocationTablePrimaryKey") fun findCurrentLocationByCurrentLocationTablePrimaryKey(currentLocationTablePrimaryKey: Long): CurrentLocationTable? @Query( "UPDATE CurrentLocationTable SET " + "currentLocationTablePrimaryKey = :currentLocationTablePrimaryKey, " + "currentLocationTableFirebaseKey = :currentLocationTableFirebaseKey, " + "currentLocationContactEmailAddress = :currentLocationContactEmailAddress, " + "currentLocationTransactionType = :currentLocationTransactionType, " + "currentLocationSequenceKey = :currentLocationSequenceKey, " + "currentLocationAndDateTimeString = :currentLocationAndDateTimeString " + "WHERE currentLocationTablePrimaryKey = :currentLocationTablePrimaryKey" ) fun updateCurrentLocation( currentLocationTablePrimaryKey: Long, currentLocationTableFirebaseKey: String?, currentLocationContactEmailAddress: String?, currentLocationTransactionType: String?, currentLocationSequenceKey: Long, currentLocationAndDateTimeString: String? ) }
MainViewModel中的DAO初始化与定义
// init代码块 val db: PeepsConnectionRoomDatabase db = PeepsConnectionRoomDatabase.getDatabase(appContext)!! currentLocationTableDao = db.currentLocationTableDao()!! customGeofencePerimeterMarkerTableDao = db.customGeofencePerimeterMarkerTableDao()!!; // DAO变量定义 var checkInRouteAddressTableDao: CheckInRouteAddressTableDao var customGeofencePerimeterMarkerTableDao: CustomGeofencePerimeterMarkerTableDao
出现错误的插入函数代码
@Throws(ExecutionException::class, InterruptedException::class) fun insertCurrentLocation(currentLocationTable: CurrentLocationTable?): Long? { Log.i(" ", "MainViewModel insertCurrentLocation: ") mCurrentLocationTable = currentLocationTable val insertCurrentLocationTask = InsertCurrentLocationTask(currentLocationTable) val future = executor.submit(insertCurrentLocationTask) ?: null return future?.get() } internal class InsertCurrentLocationTask(mCurrentLocationTable: CurrentLocationTable?) : Callable<Long?> { @Throws(Exception::class) override fun call(): Long? { return currentLocationTableDao.insertCurrentLocation(mCurrentLocationTable) // currentLocationTableDao未解析 } } @Throws(ExecutionException::class, InterruptedException::class) fun insertCustomGeofencePerimeterMarker(customGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable?): Long? { Log.i(" ", "MainViewModel insertCustomGeofencePerimeterMarker: ") val insertCustomGeofencePerimeterMarkerTask = InsertCustomGeofencePerimeterMarkerTask(customGeofencePerimeterMarkerTable) val future = executor.submit(insertCustomGeofencePerimeterMarkerTask) ?: null return future?.get() } internal class InsertCustomGeofencePerimeterMarkerTask(mCustomGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable?) : Callable<Long?> { @Throws(Exception::class) override fun call(): Long { return customGeofencePerimeterMarkerTableDao.insertCustomGeofencePerimeterMarkerTable(mCustomGeofencePerimeterMarkerTable) // customGeofencePerimeterMarkerTableDao未解析 } }
解决方案
问题核心是Kotlin中嵌套类默认无法访问外部类的非final成员,在KMP环境下该访问规则更严格,可通过以下方式解决:
方式1:将DAO变量改为val
把MainViewModel中的DAO变量定义从var改为val,Kotlin嵌套类可直接访问外部类的不可变成员:
// 修改DAO变量定义 val checkInRouteAddressTableDao: CheckInRouteAddressTableDao val customGeofencePerimeterMarkerTableDao: CustomGeofencePerimeterMarkerTableDao
方式2:用inner修饰内部任务类
若需保留var,给内部任务类添加inner关键字,使其成为内部类(而非嵌套类),即可访问外部类成员:
internal inner class InsertCurrentLocationTask(mCurrentLocationTable: CurrentLocationTable?) : Callable<Long?> { @Throws(Exception::class) override fun call(): Long? { return currentLocationTableDao.insertCurrentLocation(mCurrentLocationTable) } } internal inner class InsertCustomGeofencePerimeterMarkerTask(mCustomGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable?) : Callable<Long?> { @Throws(Exception::class) override fun call(): Long { return customGeofencePerimeterMarkerTableDao.insertCustomGeofencePerimeterMarkerTable(mCustomGeofencePerimeterMarkerTable) } }
方式3:将DAO作为参数传入内部任务类
直接把DAO实例传递给内部任务类,避免依赖外部类成员访问:
// 修改任务类 internal class InsertCurrentLocationTask( private val dao: CurrentLocationTableDao, private val mCurrentLocationTable: CurrentLocationTable? ) : Callable<Long?> { @Throws(Exception::class) override fun call(): Long? { return dao.insertCurrentLocation(mCurrentLocationTable) } } // 创建任务时传入DAO val insertCurrentLocationTask = InsertCurrentLocationTask(currentLocationTableDao, currentLocationTable)
对应修改另一个任务类:
internal class InsertCustomGeofencePerimeterMarkerTask( private val dao: CustomGeofencePerimeterMarkerTableDao, private val mCustomGeofencePerimeterMarkerTable: CustomGeofencePerimeterMarkerTable? ) : Callable<Long?> { @Throws(Exception::class) override fun call(): Long { return dao.insertCustomGeofencePerimeterMarkerTable(mCustomGeofencePerimeterMarkerTable) } } // 创建任务时传入DAO val insertCustomGeofencePerimeterMarkerTask = InsertCustomGeofencePerimeterMarkerTask(customGeofencePerimeterMarkerTableDao, customGeofencePerimeterMarkerTable)
额外建议
在Kotlin Multiplatform中,建议用协程替代手动创建Callable任务处理Room异步操作,Room 2.1.0+支持协程挂起函数,代码更简洁且符合KMP最佳实践:
// 修改DAO中的插入方法为挂起函数 @Insert suspend fun insertCurrentLocation(currentLocationTable: CurrentLocationTable): Long? // MainViewModel中用协程调用 suspend fun insertCurrentLocation(currentLocationTable: CurrentLocationTable?): Long? { currentLocationTable ?: return null return withContext(Dispatchers.IO) { currentLocationTableDao.insertCurrentLocation(currentLocationTable) } }
内容的提问来源于stack exchange,提问作者GeorgeRussell
相关产品推荐
相关产品推荐

