Snapshot.Companion


Summary

Constants

const Int

All new state objects initial state records should be PreexistingSnapshotId which then allows snapshots outside the creating snapshot to access the object with its initial state.

Cmn

Public functions

inline T
<T : Any?> global(block: () -> T)

Escape the current snapshot, if there is one.

Cmn
Unit

Notify the snapshot that all objects created in this snapshot to this point should be considered initialized.

Cmn
T
<T : Any?> observe(
    readObserver: ((Any) -> Unit)?,
    writeObserver: ((Any) -> Unit)?,
    block: () -> T
)

Observe reads and or write of state objects in the current thread.

Cmn
Int
Cmn
ObserverHandle

Register an apply listener that is called back when snapshots are applied to the global state.

Cmn
ObserverHandle

Register an observer of the first write to the global state of a global state object since the last call to sendApplyNotifications.

Cmn
Unit

Send any pending apply notifications for state objects changed outside a snapshot.

Cmn
MutableSnapshot
takeMutableSnapshot(
    readObserver: ((Any) -> Unit)?,
    writeObserver: ((Any) -> Unit)?
)

Take a snapshot of the current value of all state objects that also allows the state to be changed and later atomically applied when MutableSnapshot.apply is called.

Cmn
Snapshot
takeSnapshot(readObserver: ((Any) -> Unit)?)

Take a snapshot of the current value of all state objects.

Cmn
inline R
<R : Any?> withMutableSnapshot(block: () -> R)

Take a MutableSnapshot and run block within it.

Cmn
inline T

Passed block will be run with all the currently set snapshot read observers disabled.

Cmn

Public properties

Snapshot

Return the thread's active snapshot.

Cmn
Boolean

Returns whether any threads are currently in the process of notifying observers about changes to the global snapshot.

Cmn
Boolean

Return true if the thread is currently in the context of a snapshot.

Cmn

Extension functions

ObserverHandle

This is a tooling API and is not intended to be used in a production application as it will introduce global overhead to creating, applying and disposing all snapshots and, potentially, to reading and writing all state objects.

Cmn

Constants

PreexistingSnapshotId

const val PreexistingSnapshotId = 1: Int

All new state objects initial state records should be PreexistingSnapshotId which then allows snapshots outside the creating snapshot to access the object with its initial state.

Public functions

global

inline fun <T : Any?> global(block: () -> T): T

Escape the current snapshot, if there is one. All state objects will have the value associated with the global while the block lambda is executing.

Returns
T

the result of block

notifyObjectsInitialized

fun notifyObjectsInitialized(): Unit

Notify the snapshot that all objects created in this snapshot to this point should be considered initialized. If any state object is are modified passed this point it will appear as modified in the snapshot and any applicable snapshot write observer will be called for the object and the object will be part of the a set of mutated objects sent to any applicable snapshot apply observer.

Unless notifyObjectsInitialized is called, state objects created in a snapshot are not considered modified by the snapshot even if they are modified after construction.

Compose uses this between phases of composition to allow observing changes to state objects create in a previous phase.

observe

fun <T : Any?> observe(
    readObserver: ((Any) -> Unit)? = null,
    writeObserver: ((Any) -> Unit)? = null,
    block: () -> T
): T

Observe reads and or write of state objects in the current thread.

This only affects the current snapshot (if any) and any new snapshots create from Snapshot.takeSnapshot and takeMutableSnapshot. It will not affect any snapshots previous created even if Snapshot.enter is called in block.

Parameters
readObserver: ((Any) -> Unit)? = null

called when any state object is read.

writeObserver: ((Any) -> Unit)? = null

called when a state object is created or just before it is written to the first time in the snapshot or a nested mutable snapshot. This might be called several times for the same object if nested mutable snapshots are created.

block: () -> T

the code the readObserver and writeObserver will be observing. Once block returns, the readObserver and writeObserver will no longer be called.

openSnapshotCount

@InternalComposeApi
fun openSnapshotCount(): Int

registerApplyObserver

fun registerApplyObserver(observer: (Set<Any>, Snapshot) -> Unit): ObserverHandle

Register an apply listener that is called back when snapshots are applied to the global state.

Returns
ObserverHandle

ObserverHandle to unregister observer.

registerGlobalWriteObserver

fun registerGlobalWriteObserver(observer: (Any) -> Unit): ObserverHandle

Register an observer of the first write to the global state of a global state object since the last call to sendApplyNotifications.

Composition uses this to schedule a new composition whenever a state object that was read in composition is modified.

State objects can be sent to the apply observer that have not been sent to global write observers. This happens for state objects inside MutableSnapshot that is later applied by calling MutableSnapshot.apply.

This should only be used to determine if a call to sendApplyNotifications should be scheduled to be called.

Returns
ObserverHandle

ObserverHandle to unregister observer.

sendApplyNotifications

fun sendApplyNotifications(): Unit

Send any pending apply notifications for state objects changed outside a snapshot.

Apply notifications for state objects modified outside snapshot are deferred until method is called. This method is implicitly called whenever a non-nested MutableSnapshot is applied making its changes visible to all new, non-nested snapshots.

Composition schedules this to be called after changes to state objects are detected an observer registered with registerGlobalWriteObserver.

takeMutableSnapshot

fun takeMutableSnapshot(
    readObserver: ((Any) -> Unit)? = null,
    writeObserver: ((Any) -> Unit)? = null
): MutableSnapshot

Take a snapshot of the current value of all state objects that also allows the state to be changed and later atomically applied when MutableSnapshot.apply is called. The values are preserved until Snapshot.dispose is called on the result. The global state will either see all the changes made as one atomic change, when MutableSnapshot .apply is called, or none of the changes if the mutable state object is disposed before being applied.

The values in a snapshot can be modified by calling Snapshot.enter and then, in its lambda, modify any state object. The new values of the state objects will only become visible to the global state when MutableSnapshot.apply is called.

An active snapshot (after it is created but before Snapshot.dispose is called) requires resources to track the values in the snapshot. Once a snapshot is no longer needed it should disposed by calling Snapshot.dispose.

Leaving a snapshot active could cause hard to diagnose memory leaks as values are maintained by state objects for these unneeded snapshots. Take care to always call Snapshot.dispose on all snapshots when they are no longer needed.

A nested snapshot can be taken by calling Snapshot.takeNestedSnapshot, for a read-only snapshot, or MutableSnapshot.takeNestedMutableSnapshot for a snapshot that can be changed. Nested mutable snapshots are applied to the this, the parent snapshot, when their MutableSnapshot.apply is called. Their applied changes will be visible to in this snapshot but will not be visible other snapshots (including other nested snapshots) or the global state until this snapshot is applied by calling MutableSnapshot.apply.

Once MutableSnapshot.apply is called on this, the parent snapshot, all calls to MutableSnapshot.apply on an active nested snapshot will fail.

Changes to a mutable snapshot are isolated, using snapshot isolation, from all other snapshots. Their changes are only visible as global state or to new snapshots once MutableSnapshot.apply is called.

Applying a snapshot can fail if currently visible changes to the state object conflicts with a change made in the snapshot.

When in a mutable snapshot, takeMutableSnapshot creates a nested snapshot of the current mutable snapshot. If the current snapshot is read-only, an exception is thrown. The current snapshot is the result of calling currentSnapshot which is updated by calling Snapshot.enter which makes the Snapshot the current snapshot while in its lambda.

Composition uses mutable snapshots to allow changes made in a Composable functions to be temporarily isolated from the global state and is later applied to the global state when the composition is applied. If MutableSnapshot.apply fails applying this snapshot, the snapshot and the changes calculated during composition are disposed and a new composition is scheduled to be calculated again.

Parameters
readObserver: ((Any) -> Unit)? = null

called when any state object is read in the lambda passed to Snapshot.enter or in the Snapshot.enter of any nested snapshots.

Composition, layout and draw use readObserver to implicitly subscribe to changes to state objects to know when to update.

writeObserver: ((Any) -> Unit)? = null

called when a state object is created or just before it is written to the first time in the snapshot or a nested mutable snapshot. This might be called several times for the same object if nested mutable snapshots are created.

Composition uses writeObserver to track when a state object is modified during composition in order to invalidate the reads that have not yet occurred. This allows a single pass of composition for state objects that are written to before they are read (such as modifying the value of a dynamic ambient provider).

takeSnapshot

fun takeSnapshot(readObserver: ((Any) -> Unit)? = null): Snapshot

Take a snapshot of the current value of all state objects. The values are preserved until Snapshot.dispose is called on the result.

The readObserver parameter can be used to track when all state objects are read when in Snapshot.enter. A snapshot apply observer can be registered using Snapshot.registerApplyObserver to observe modification of state objects.

An active snapshot (after it is created but before Snapshot.dispose is called) requires resources to track the values in the snapshot. Once a snapshot is no longer needed it should disposed by calling Snapshot.dispose.

Leaving a snapshot active could cause hard to diagnose memory leaks values as are maintained by state objects for these unneeded snapshots. Take care to always call Snapshot.dispose on all snapshots when they are no longer needed.

Composition uses both of these to implicitly subscribe to changes to state object and automatically update the composition when state objects read during composition change.

A nested snapshot can be taken of a snapshot which is an independent read-only copy of the snapshot and can be disposed independently. This is used by takeSnapshot when in a read-only snapshot for API consistency allowing the result of takeSnapshot to be disposed leaving the parent snapshot active.

Parameters
readObserver: ((Any) -> Unit)? = null

called when any state object is read in the lambda passed to Snapshot.enter or in the Snapshot.enter of any nested snapshot.

withMutableSnapshot

inline fun <R : Any?> withMutableSnapshot(block: () -> R): R

Take a MutableSnapshot and run block within it. When block returns successfully, attempt to MutableSnapshot.apply the snapshot. Returns the result of block or throws SnapshotApplyConflictException if snapshot changes attempted by block could not be applied.

Prior to returning, any changes made to snapshot state (e.g. state holders returned by androidx.compose.runtime.mutableStateOf are not visible to other threads. When withMutableSnapshot returns successfully those changes will be made visible to other threads and any snapshot observers (e.g. androidx.compose.runtime.snapshotFlow) will be notified of changes.

block must not suspend if withMutableSnapshot is called from a suspend function.

withoutReadObservation

inline fun <T : Any?> withoutReadObservation(block: @DisallowComposableCalls () -> T): T

Passed block will be run with all the currently set snapshot read observers disabled.

Public properties

current

val currentSnapshot

Return the thread's active snapshot. If no thread snapshot is active then the current global snapshot is used.

isApplyObserverNotificationPending

val isApplyObserverNotificationPendingBoolean

Returns whether any threads are currently in the process of notifying observers about changes to the global snapshot.

isInSnapshot

val isInSnapshotBoolean

Return true if the thread is currently in the context of a snapshot.

Extension functions

observeSnapshots

@ExperimentalComposeRuntimeApi
fun Snapshot.Companion.observeSnapshots(
    snapshotObserver: SnapshotObserver
): ObserverHandle

This is a tooling API and is not intended to be used in a production application as it will introduce global overhead to creating, applying and disposing all snapshots and, potentially, to reading and writing all state objects.

Observe when snapshots are created, applied, and/or disposed. The observer can also install read and write observers on the snapshot being created.

This method is thread-safe and calling ObserverHandle.dispose on the ObserverHandle returned is also thread-safe.

Parameters
snapshotObserver: SnapshotObserver

the snapshot observer to install.

Returns
ObserverHandle

ObserverHandle an instance to unregister the snapshotObserver.