lundi 8 mars 2021

Reference to suspend function randomly gets invalid

I have a Room @Dao with CRUD functions declared as follows:

interface IMutableDao<T> {

 
    @RawQuery
    fun syncBrokerGet(query: SupportSQLiteQuery): T?
    /////////////////////////////////////////////////////////////////////////////////////////////////////

    @Insert(onConflict = OnConflictStrategy.ABORT)
    suspend fun insert(e: T)

    @Update
    suspend fun update(e: T)

    @Delete
    suspend fun delete(e: T)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun batchInsert(l: List<T>)

    @Update
    suspend fun batchUpdate(l: List<T>)

    @Delete
    suspend fun batchDelete(l: List<T>)
}

all other DAO classes implement this interface.

In a separate module (dynamic feature) I get a reference to the CRUD functions above, that I use to insert/update/delete records using reflection as in the following snippet:

class EntityInfoExt(val nestName: String, val localizedName: String, val clazz: KClass<out ILorikeetDbEntity>, val daoGetter: () -> IMutableDao<out ILorikeetDbEntity>) {

    private var dao: IMutableDao<out ILorikeetDbEntity>? = null

    private val insertRef: KFunction<*> by lazy {
        dao!!::class.functions.find {it.name.equals("insert", true)}!!
    }
    private val deleteRef: KFunction<*> by lazy {
        dao!!::class.functions.find {it.name.equals("delete", true)}!!
    }
    private val updateRef: KFunction<*> by lazy {
        dao!!::class.functions.find {it.name.equals("update", true)}!!
    }
    private val getRef: KFunction<*> by lazy {
        dao!!::class.functions.find { it.name.equals("syncBrokerGet", true)}!!
    }

    private fun validateDao() {
        if (dao == null) {
            dao = daoGetter()
        }
    }

    suspend fun insert(obj: ILorikeetDbEntity) {
        try {
            validateDao()
            insertRef.invokeSuspend(dao!!, obj)
        }
        catch (e: Throwable) {
            if (e is InvocationTargetException) {
                throw e.targetException
            }
            else {
                throw e
            }
        }
    }

    suspend fun update(obj: ILorikeetDbEntity) {
        try {
            validateDao()
            updateRef.invokeSuspend(dao!!, obj)
        }
        catch (e: Throwable) {
            if (e is InvocationTargetException) {
                throw e.targetException
            }
            else {
                throw e
            }
        }
    }

    suspend fun delete(obj: ILorikeetDbEntity) {
        try {
            validateDao()
            deleteRef.invokeSuspend(dao!!, obj)
        }
        catch (e: Throwable) {
            if (e is InvocationTargetException) {
                throw e.targetException
            }
            else {
                throw e
            }
        }
    }

    fun getObject(id: UUID): ILorikeetDbEntity? {
        try {
            validateDao()
            return getRef.call(dao!!, SelectObjectQuery(nestName, id).query) as? ILorikeetDbEntity?
        }
        catch (e: Throwable) {
            if (e is InvocationTargetException) {
                throw e.targetException
            }
            else {
                throw e
            }
        }
    }
}

The problem is that in some devices I tested on it works on others randomly the function references are not valid anymore, and I do not understand how that can happen. The following call sometimes works other it doesn't, it is like the function reference is relocated... or something like that. The call is made through a helper method because the insert/update functions are declared as suspend, the call is the following:

First I get a reference to the functions:

 funInsert = dao::class.functions.find {
                        it.name.equals("batchInsert", true)
                    }!!

                    funUpdate = dao::class.functions.find {
                        it.name.equals("batchUpdate", true)
                    }!!

the I call the insert/update functions:

funInsert.invokeSuspend(dao, objects)

The function invokeSuspend is declared as follows:

suspend fun KFunction<*>.invokeSuspend(obj: Any, vararg args: Any?): Any? =
    suspendCoroutineUninterceptedOrReturn { cont ->
        call(obj, *args, cont)
    }

What I cannot understand is that sometimes the call:

insert.invokeSuspend(dao, objects)

randomly throws a "method not found exception" and I really do not understand why.





Aucun commentaire:

Enregistrer un commentaire