android - How can I use data from another flow data in flow? (Kotlin Flow) - TagMerge
3How can I use data from another flow data in flow? (Kotlin Flow)How can I use data from another flow data in flow? (Kotlin Flow)

How can I use data from another flow data in flow? (Kotlin Flow)

Asked 9 months ago
0
3 answers

With combine and zip operators you can not depend on the other's result. So in general your chaining approach with map is OK.

There is several options you have:

  • Assuming your repository method is not called from anywhere else, the reason for it being called twice is that the region Flow is emitting twice. So try to find out why this is the case.

    Anyhow if your region Flow method returns the same region twice you can fix it by simply adding .distinctUntilChanged() after getRegion() like:

    getRegion().distinctUntilChanged().map { region: Region ->
       repository.requestDataFromServer(region)
    }
    

    It will make sure your region Flow doesn't emit redundantly with the same data. Alternatively add distinctUntilChanged() directly to the repository method, if this is always the expected behavior.

  • Ask yourself if this method really needs to return a stream (Flow). I guess you need a stream since the region can change at runtime and you want something in your app to update automatically? But if not you could simply convert the stream to a single result:

    val region = getRegion().first()
    repository.requestDataFromServer(region)
    

Source: link

0

Following the examples from Kotlin flows, a StateFlow can be exposed from the LatestNewsViewModel so that the View can listen for UI state updates and inherently make the screen state survive configuration changes.
class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {

    // Backing property to avoid state updates from other classes
    private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList()))
    // The UI collects from this StateFlow to get its state updates
    val uiState: StateFlow<LatestNewsUiState> = _uiState

    init {
        viewModelScope.launch {
            newsRepository.favoriteLatestNews
                // Update View with the latest favorite news
                // Writes to the value property of MutableStateFlow,
                // adding a new element to the flow and updating all
                // of its collectors
                .collect { favoriteNews ->
                    _uiState.value = LatestNewsUiState.Success(favoriteNews)
                }
        }
    }
}

// Represents different states for the LatestNews screen
sealed class LatestNewsUiState {
    data class Success(news: List<ArticleHeadline>): LatestNewsUiState()
    data class Error(exception: Throwable): LatestNewsUiState()
}
The View listens for StateFlow as with any other flow:
class LatestNewsActivity : AppCompatActivity() {
    private val latestNewsViewModel = // getViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Start a coroutine in the lifecycle scope
        lifecycleScope.launch {
            // repeatOnLifecycle launches the block in a new coroutine every time the
            // lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Trigger the flow and start listening for values.
                // Note that this happens when lifecycle is STARTED and stops
                // collecting when the lifecycle is STOPPED
                latestNewsViewModel.uiState.collect { uiState ->
                    // New value received
                    when (uiState) {
                        is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
                        is LatestNewsUiState.Error -> showError(uiState.exception)
                    }
                }
            }
        }
    }
}
A CoroutineScope that is used to share the flow. This scope should live longer than any consumer to keep the shared flow alive as long as needed. The number of items to replay to each new collector. The start behavior policy.
class NewsRemoteDataSource(...,
    private val externalScope: CoroutineScope,
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
}
You can create a SharedFlow without using shareIn. As an example, you could use a SharedFlow to send ticks to the rest of the app so that all the content refreshes periodically at the same time. Apart from fetching the latest news, you might also want to refresh the user information section with its favorite topics collection. In the following code snippet, a TickHandler exposes a SharedFlow so that other classes know when to refresh its content. As with StateFlow, use a backing property of type MutableSharedFlow in a class to send items to the flow:
// Class that centralizes when the content of the app needs to be refreshed
class TickHandler(
    private val externalScope: CoroutineScope,
    private val tickIntervalMs: Long = 5000
) {
    // Backing property to avoid flow emissions from other classes
    private val _tickFlow = MutableSharedFlow<Unit>(replay = 0)
    val tickFlow: SharedFlow<Event<String>> = _tickFlow

    init {
        externalScope.launch {
            while(true) {
                _tickFlow.emit(Unit)
                delay(tickIntervalMs)
            }
        }
    }
}

class NewsRepository(
    ...,
    private val tickHandler: TickHandler,
    private val externalScope: CoroutineScope
) {
    init {
        externalScope.launch {
            // Listen for tick updates
            tickHandler.tickFlow.collect {
                refreshLatestNews()
            }
        }
    }

    suspend fun refreshLatestNews() { ... }
    ...
}

Source: link

0

Add the following in the app's build.gradle,
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
and in the project's build.gradle add,
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61"
In MainActivity's layout file let's create a UI that will have a button.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="Launch Kotlin Flow"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
Now, let's begin the implementation of Flow APIs in MainActivity. In onCreate() function of Activity lets add two function like,
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setupFlow()
    setupClicks()
}
We will declare a lateinit variable of Flow of Int type,
lateinit var flow: Flow<Int>

Source: link

Recent Questions on android

    Programming Languages