HomeAndroidConvert Circulation to SharedFlow and StateFlow

Convert Circulation to SharedFlow and StateFlow


That is a part of the asynchronous circulation collection:

Circulation is a chilly stream. It emits worth solely when somebody collects or subscribes to it. So it does NOT maintain any information or state.

SharedFlow is a sizzling stream. It may well emit worth even when nobody collects or subscribes to it. It does NOT maintain any information too.

StateFlow can also be a sizzling steam. It does NOT emit worth, nevertheless it holds the worth/information.

Circulation Kind Chilly or Scorching Stream Information Holder
Circulation Chilly No
SharedFlow Scorching (by default) No
StateFlow Scorching (by default) Sure

The explanation why SharedFlow and StateFlow are sizzling streams by default, is that they may also be chilly streams relying on the way you create them. See shareIn and stateIn sections under.

What’s information holder?

Information holder (may also be known as as state holder) means it holds information. It retains and shops the final information of the stream, The information can also be observable which permits subscribers to subscribe to it.

There are 3 varieties of information holders in Android improvement

  • LiveData

  • StateFlow

  • State (Jetpack Compose)

3 of them are fairly related, however they’ve variations. See the desk under.

Information Holder Kind Android or Kotlin Library? Lifecycle Conscious? Required Preliminary Worth?
LiveData Android Sure No
StateFlow Kotlin No Sure
State (Compose) Android No Sure

StateFlow is Platform Unbiased

LiveData is Android-specific and ultimately will probably be changed by StateFlow. Compose state is just like StateFlow in my view. Nonetheless, compose State could be very particular to Android Jetpack Compose. So it’s platform particular, whereas StateFlow is extra generic and platform impartial.

StateFlow could possibly be life-cycle conscious

LiveData itself is life-cycle conscious and StateFlow is NOT. StateFlow could possibly be life-cycle conscious, relying on the way you acquire it. Compose State itself is NOT life-cycle conscious. Since it’s utilized by the composable capabilities, when a composable perform leaves composition, it robotically unsubscribes from compose State.

StateFlow requires Preliminary Worth

Creating LiveData does NOT require an preliminary worth


val liveData = MutableLiveData<Int>()

however, StateFlow and compose State require an preliminary worth.


val stateFlow = MutableStateFlow<Int?>(null)

val composeState: MutableState<Int?> = mutableStateOf(null)

Convert Circulation to SharedFlow

The next instance relies on this circulation in your View Mannequin class

val circulation: Circulation<Int> = circulation {
    repeat(10000) { worth ->
        delay(1000)
        emit(worth)
    }
}

and this sharedFlow variable outlined.

non-public var sharedFlow = MutableSharedFlow<Int>()

1. Circulation.acquire() and SharedFlow.emit()

This converts the Circulation to SharedFlow utilizing Circulation<T>.acquire and manually name the SharedFlow<T>.emit().

viewModelScope.launch { 
    circulation.acquire { worth 
        -> sharedFlow.emit(worth) 
    } 
}

This can be a sizzling stream. So it emits the worth regardless anybody collects it.

2. Circulation.shareIn()

It’s also possible to use Circulation<T>.shareIn() to attain the identical consequence.

sharedFlow = circulation.shareIn(
    scope = viewModelScope,
    began = SharingStarted.Eagerly
)

Nonetheless, when you change SharingStarted.Eagerly to SharingStarted.WhileSubscribed(), the SharedFlow turns into a chilly stream.

Convert Circulation to StateFlow

So we now have this stateFlow variable outlined within the view mannequin.

non-public val stateFlow = MutableStateFlow<Int?>(null)

1. Circulation.acquire() and StateFlow.worth

This converts the Circulation to StateFlow.

viewModelScope.launch { 
    circulation.acquire { worth -> 
        stateFlow.worth = worth 
    } 
}

Just like SharedFlow, this can be a sizzling stream. The distinction is StateFlow is a knowledge holder and SharedFlow shouldn’t be.

2. Circulation.stateIn()

It’s also possible to use Circulation<T>.stateIn() to attain the identical consequence.

stateFlow = circulation.stateIn(
    scope = viewModelScope,
    began = SharingStarted.Eagerly,
    initialValue = null)

Just like Circulation<T>.shareIn() above, when you change SharingStarted.Eagerly to SharingStarted.WhileSubscribed(), the StateFlow turns into a chilly stream.

Vital be aware: In case you name Circulation<T>.shareIn() and Circulation<T>.stateIn() a number of instances, it creates a number of flows which emit the worth within the background. This ultimately causes pointless useful resource leaks that you just wish to stop.

Accumulate from SharedFlow and StateFlow

Gathering from SharedFlow and StateFlow is identical as gathering from the Circulation. Consult with the next article on other ways of gathering circulation.

1. Accumulate utilizing RepeatOnLifecycle()

The really helpful strategy to acquire Circulation by Google is utilizing LifeCycle.RepeatOnLifecycle(). So, we’ll use it for instance.

This instance under converts the stateFlow to compose state, which is the info holder for composable perform.

@Composable 
enjoyable SharedStateFlowScreen() { 
    val viewModel: StateSharedFlowViewModel = viewModel() 
    val lifeCycle = LocalLifecycleOwner.present.lifecycle

     
    var composeStateValue by bear in mind { mutableStateOf<Int?>(null) }

         
    LaunchedEffect(true) { 
        lifeCycle.repeatOnLifecycle(state = Lifecycle.State.STARTED) {
            viewModel.stateFlow.acquire { composeStateValue = it } 
        } 
    } 
}

2. Accumulate utilizing collectAsStateWithLifecycle()

In case you use Android lifecycle Model 2.6.0-alpha01 or later, you’ll be able to scale back the code considerably utilizing Circulation<T>.collectAsStateWithLifecycle() API

First, it’s essential have this dependency in your construct.gradle file.

dependencies { 
    implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha02' 
}

Then, you’ll be able to scale back the code to the next. Please be aware that it’s essential specify the @OptIn(ExperimentalLifecycleComposeApi::class)

@OptIn(ExperimentalLifecycleComposeApi::class) 
@Composable 
enjoyable SharedStateFlowScreen() { 
    val viewModel: StateSharedFlowViewModel = viewModel()

     
    val composeStateValue by   
        viewModel.stateFlow.collectAsStateWithLifecycle() 
}

Greatest Practices?

Truthfully, what I discover tough in Android improvement is there are simply means too many choices to perform the identical factor. So which one we should always use? Now, we now have LiveData, Circulation, Channel, SharedFlow, StateFlow and compose State. In what situation, we should always use which one?

So I doc the perfect practices based mostly on varied sources and my interpretations. I have no idea whether or not they make sense. Issues like this are doubtless very subjective too.

  1. Do NOT use LiveData particularly when you’re engaged on a brand new venture. LiveData is legacy and ultimately will probably be changed by StateFlow.

  2. Do NOT expose Circulation straight in View Mannequin, convert it to StateFlow as an alternative. This may keep away from pointless workload on the primary UI thread. Circulation is a chilly stream, it emits information (or restarts the info emission) each time you acquire it.

  3. Expose StateFlow in your view mannequin as an alternative of compose State. Since StateFlow is platform impartial, it makes your view mannequin platform impartial which permits you simple migration to KMM (which lets you goal each IOS and Android) for instance. StateFlow can also be extra highly effective (e.g. it permits you to mix a number of flows into one and many others.)

  4. Accumulate StateFlow in your UI parts (both in exercise or composable perform) and convert the info to compose State. Compose State ought to be created and used solely throughout the composable capabilities.

Whether or not ViewModel ought to maintain StateFlow or compose State is questionable. I’ve been utilizing compose State nevertheless it looks like StateFlow is likely to be a greater choice right here based mostly on extra advanced use instances comparable to combining circulation?

Then again, if I convert Circulation to compose State in ViewModel straight, I needn’t convert it once more within the composable perform. Why do I would like 2 state/information holders and acquire twice?

What about one-time occasion?

I don’t positive of the use case of SharedFlow and Channel. It seems for use as a one-time occasion. When you have one subscriber, you employ Channel. When you have a number of subscribers, you employ SharedFlow.

Nonetheless, this text right here by the Google crew form of indicate utilizing SharedFlow or Channel as a one-time occasion shouldn’t be really helpful. It’s primarily as a result of they’re sizzling stream, which runs right into a threat of lacking the occasions when the app is within the background or throughout configuration.

It appears to me we should always in all probability simply use StateFlow for every part. Let’s neglect the remainder! Possibly that is simpler this manner…

Supply Code

GitHub Repository: Demo_AsyncFlow (see the SharedStateFlowActivity)

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments