Status Update
Comments
sc...@gmail.com <sc...@gmail.com> #2
For Kotlin 2.0 and KSP 2.0 the Cannot change attributes of configuration ':composeApp:debugFrameworkIosX64' after it has been locked for mutation
really seems like a KSP issue. You should file a bug in their repository with a sample app if possible.
If you downgrade to Kotlin 1.9 then things 'should' work, there are example apps out there with such configuration, like the following one:
yb...@google.com <yb...@google.com> #3
Will try to use the example provided by you to check if it fixes the issue.
ap...@google.com <ap...@google.com> #4
Note that this issue happens when applying the Compose, KSP and Room Plugin together in Kotlin 2.0.x, the workaround for now is to not use the Room Gradle Plugin and instead specify the schema location vis KSP arguments:
// In the build.gradle
ksp {
arg("room.schemaLocation", "${projectDir}/schemas")
}
da...@google.com <da...@google.com>
an...@gmail.com <an...@gmail.com> #5
Hi, I encountered a similar problem and was able to resolve it by updating the dependencies
room = "2.7.0-alpha08"
ksp = "2.0.20-1.0.25"
compose-plugin = "1.6.11"
kotlin = "2.0.20"
an...@gmail.com <an...@gmail.com> #6
// DAO
@Query("SELECT * FROM my_items WHERE synced_time is NULL ORDER BY captured_on")
fun unsyncedItemsFlow(): Flow<Item>
// usage
dao.unsyncedItemsFlow().collect {
// log it
}
Is there an example of proper usage I can look at?
I've used Kotlin flow in my own code to dump a result set to a text file, and it worked just fine.
Here is what I used, and expected to be able to replace:
@FlowPreview
private fun Cursor.export() = flow {
use { c ->
val columnCount = c.columnCount
val separator = "\t"
emit(c.columnNames.joinToString(separator))
while (c.moveToNext()) {
emit((0 until columnCount).joinToString(separator) {
when (c.getType(it)) {
Cursor.FIELD_TYPE_NULL -> "null"
Cursor.FIELD_TYPE_INTEGER -> c.getInt(it).toString()
Cursor.FIELD_TYPE_FLOAT -> c.getFloat(it).toString()
Cursor.FIELD_TYPE_STRING -> "\"${c.getString(it)}\""
Cursor.FIELD_TYPE_BLOB -> c.getBlob(it).toString()
else -> c.getString(it)
}
})
}
}
}
// in DAO
@Query("SELECT * FROM items WHERE synced_time is NULL")
fun unsyncedItems(): Cursor
// in Repository
@FlowPreview
override suspend fun unsyncedItems() = dao.unsyncedItems().export()
and then
flow: Flow<String> = unsyncedItems()
flow.collect { appendLn(it) }
[Deleted User] <[Deleted User]> #7
fun unsyncedItemsFlow(): Flow<Item>
This method should return Flow<List<Item>>. That's the reason you are only getting the first item.
The Cursor underneath is returning all lines in your query result, but you are only getting the first in your DAO because you are returning a single element.
I don't see the point why your example should work with Flow<Item>, the result is already loaded into memory anyway, why should it emit one element at a time through the Flow?
an...@gmail.com <an...@gmail.com> #8
I clearly misunderstood what this (returning Flow) is trying to achieve, I definitely do not want to load the whole query result into memory. I thought Flow would be used to emit single values (or preferably pages) as I do in my own usages of Flow.
For some of my use cases being able to iterate the entire result set is useful, with SQLiteCursor's internal buffering (CursorWindow) and setFillWindowForwardOnly it can be done fairly quickly even for large sets (that do not fit into memory as a whole). Wrapping the result in a Sequence or, as shown above, Flow, to abstract it away from Cursor seems to work well enough. I was hoping that this addition to Room meant I would not have to roll these by hand (the example above is just a simple version that emits strings).
an...@gmail.com <an...@gmail.com> #9
I just plugged this into TryKotlin, `f` is of type `Flow<String>`, and the whole array is printed, just as I want.
fun main() {
runBlocking {
val f = arrayOf("one", "two").asFlow()
f.collect {
println(it)
}
}
}
I see there is a modification made to Flow<T> results in Room 2.2.0-beta01, I'll check it out, but otherwise, if it's intended to return an entire result set at a time itis not what I'm looking for.
an...@gmail.com <an...@gmail.com> #10
Flow<Item> only returns the first while Flow<List<Item>> generates `final List<Reading> _result = new ArrayList<Reading>(_cursor.getCount());`, which is exactly what I'm trying to avoid.
da...@google.com <da...@google.com> #11
I would be careful in using a Flow<T> where T is a single item representing a row and you get N row emissions. It is not wise to keep a Cursor open for a long time, specifically in a way such that consuming every emission takes a while and another database operation modifies the same table, possibly adding rows or deleting rows. If the query result does not fit in a single window then other operations will cause the CursorWindow to throw due to inconsistencies with the original count and the result of filling a new window. This also bring the question of what is your expected behavior if you have not finished consuming the Flow but new operations have modified the underlying data, did you not expect the Flow to react to that?
Room consumes the cursor completely and loads the data into memory to avoid issues with the CursorWindow. If the data is to big then the recommendation is to paginate it.
Description
Check out