最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android - Trying to concatenate two flows from Room DAO query without success in kotlin - Stack Overflow

programmeradmin6浏览0评论

I want to concatenate the results of two SQL queries I implemented using the Room library. I will put an example to present it more clearly:

In my DAO file:

@Query(SELECT plantId FROM PlantView WHERE language = :language
fun getPlantByLang(language: String): Flow<List<Plant>>

@Query(SELECT plantId FROM PlantView WHERE location = :location
fun getPlantByLoc(location: String): Flow<List<Plant>>

Now, in my viewController, I want that getPlants returns a Flow of Plant that is a concatenation of the two flows above [flow1, flow2]. One after the other. So then I can collect both flows into one in the UI state (Screen file):

val plantList by viewModel.getPlants().collectAsState(emptyList())

The reason why I want these two flows and not a single query is because actually each one will generate slightly different Plant instances, so the output of each flow will be drawn differently in the UI, being both inside the plantList.

I tried to concatenate the two flows using onCompletion as in this SO answer:

fun getPlants (lang: String, loc: String) = getPlantByLang(lang).onCompletion{emitAll(getPlantByLoc(loc)}

But this returns the results of the first flow (byLang) only. I guess this is because of this DAO query being a hot flow, which never completes.

I tried also using flattenConcat with same outcome:

fun getPlants (lang: String, loc: String) = flowOf( getPlantByLang(lang), getPlantByLoc(loc) ).flattenConcat()

Finally, I tried using merge hoping to at least get both flows in one besides they wouldn't be one after the other:

fun getPlants (lang: String, loc: String) = merge( getPlantByLang(lang), getPlantByLoc(loc) )

And the outcome is a infinite loop of getting first one flow, UI refreshes, then the other, UI refreshes, and so on.

Any ideas? I supposed it shouldn't be that difficult to concatenate the output of two flows into one flow.

I want to concatenate the results of two SQL queries I implemented using the Room library. I will put an example to present it more clearly:

In my DAO file:

@Query(SELECT plantId FROM PlantView WHERE language = :language
fun getPlantByLang(language: String): Flow<List<Plant>>

@Query(SELECT plantId FROM PlantView WHERE location = :location
fun getPlantByLoc(location: String): Flow<List<Plant>>

Now, in my viewController, I want that getPlants returns a Flow of Plant that is a concatenation of the two flows above [flow1, flow2]. One after the other. So then I can collect both flows into one in the UI state (Screen file):

val plantList by viewModel.getPlants().collectAsState(emptyList())

The reason why I want these two flows and not a single query is because actually each one will generate slightly different Plant instances, so the output of each flow will be drawn differently in the UI, being both inside the plantList.

I tried to concatenate the two flows using onCompletion as in this SO answer:

fun getPlants (lang: String, loc: String) = getPlantByLang(lang).onCompletion{emitAll(getPlantByLoc(loc)}

But this returns the results of the first flow (byLang) only. I guess this is because of this DAO query being a hot flow, which never completes.

I tried also using flattenConcat with same outcome:

fun getPlants (lang: String, loc: String) = flowOf( getPlantByLang(lang), getPlantByLoc(loc) ).flattenConcat()

Finally, I tried using merge hoping to at least get both flows in one besides they wouldn't be one after the other:

fun getPlants (lang: String, loc: String) = merge( getPlantByLang(lang), getPlantByLoc(loc) )

And the outcome is a infinite loop of getting first one flow, UI refreshes, then the other, UI refreshes, and so on.

Any ideas? I supposed it shouldn't be that difficult to concatenate the output of two flows into one flow.

Share Improve this question asked 19 hours ago AkronixAkronix 2,1572 gold badges21 silver badges34 bronze badges 3
  • You probably want to use combine for this usecase. – Atick Faisal Commented 19 hours ago
  • Please note that Room flows are not hot. That terminology is reserved for a SharedFlow (and its sub types like StateFlow). Hot flows emit data even without a collector (that's why they are called hot, they work on their own), which Room flows don't. Room flows are cold flows. Though it is still true that they usually don't complete, that is just a specific semantic of the Room logic, not inherently baked into the Flow. – tyg Commented 17 hours ago
  • Can you please show example of the data you want to get? How the data should look like returned by the function getPlants (lang: String, loc: String) – Kiryl Tkach Commented 13 hours ago
Add a comment  | 

2 Answers 2

Reset to default 0

I think what you want to achieve is not to concatenate the Flows, you want to concatenate the Lists (i.e., the content of the flows).

This can be done using combine:

fun getPlants(lang: String, loc: String): Flow<List<Plant>> =
    combine(
        getPlantByLang(lang), 
        getPlantByLoc(loc),
    ) { plantsByLanguage, plantsByLocation ->
        plantsByLanguage + plantsByLocation
    }

Pass all the flows you want to combine as parameters. You can then use the lambda to access their content. plantsByLanguage and plantsByLocation are both of type List<Plant> because that's the type of their respective flows. The return value of the lambda determines the content of the combined flow. Since you want the lists to be concatenated, I used plantsByLanguage + plantsByLocation which returns a new List<Plant> which contains the entries of plantsByLanguage and then the entries of plantsByLocation.

Why doesn't work?

onCompletion -> Since Room flow is cold, it nevers completes, so emitAll will never be reached.

flattenConcat -> Doesn't work because flow1 never completes.

merge -> Interleaves emits from both flows, so it's normal what you say of

And the outcome is a infinite loop of getting first one flow, UI refreshes, then the other, UI refreshes, and so on.

Idea number 1 :

Use flatMapConcat.

Example :

fun getPlants(lang: String, loc: String): Flow<Plant> =
    flowOf(getPlantByLang(lang), getPlantByLoc(loc)).flatMapConcat { it }

Idea number 2 :

Use channelFlow.

Example :

fun getPlants(lang: String, loc: String): Flow<Plant> = channelFlow {
    getPlantByLang(lang).collect { plants ->
        plants.forEach { send(it) }
    }
    getPlantByLoc(loc).collect { plants ->
        plants.forEach { send(it) }
    }
}
发布评论

评论列表(0)

  1. 暂无评论