Fixed
Status Update
Comments
sc...@gmail.com <sc...@gmail.com> #2
Hi. Thanks for reporting this. Fixed in alpha-04
yb...@google.com <yb...@google.com> #3
Project: platform/frameworks/support
Branch: androidx-main
commit e782987543a9f8ccd485e970ddc74564b24378db
Author: Vighnesh Raut <vighnesh.raut13@gmail.com>
Date: Mon Jan 02 15:27:40 2023
fix: tab row crashes when only 1 tab is added
Bug: b/264018028
Test: Added unit test
Change-Id: I6381dbac304fc1d69d3708c6655f8b595668e93f
M tv/tv-material/src/androidTest/java/androidx/tv/material/TabRowTest.kt
M tv/tv-material/src/main/java/androidx/tv/material/TabRow.kt
https://android-review.googlesource.com/2373449
Branch: androidx-main
commit e782987543a9f8ccd485e970ddc74564b24378db
Author: Vighnesh Raut <vighnesh.raut13@gmail.com>
Date: Mon Jan 02 15:27:40 2023
fix: tab row crashes when only 1 tab is added
Bug:
Test: Added unit test
Change-Id: I6381dbac304fc1d69d3708c6655f8b595668e93f
M tv/tv-material/src/androidTest/java/androidx/tv/material/TabRowTest.kt
M tv/tv-material/src/main/java/androidx/tv/material/TabRow.kt
ap...@google.com <ap...@google.com> #4
deleted
da...@google.com <da...@google.com>
an...@gmail.com <an...@gmail.com> #5
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.tv:tv-material:1.0.0-alpha04
an...@gmail.com <an...@gmail.com> #6
Am I missing something? I just tested it out and it seems as if the resultant function only returns the first item in the result set, and then --waits. Is this the intended behaviour? I'm struggling to see how it would prove useful.
// 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) }
// 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
@Query("SELECT * FROM my_items WHERE synced_time is NULL ORDER BY captured_on")
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?
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
Just tried out Room 2.2.0-beta01, it's use of Flow<T> is unfortunately not what I need, although it could prove useful as a scaffolding tool.
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.
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
Its intended to return the result all at once.
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.
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